package call.gui; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Hashtable; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import call.AbstractId; import call.AudioDevice; import call.Config; import call.Config.Option; import call.ConfigListener; import call.Id; import call.Microphone; import call.Microphones; import call.PcmFormat; import call.Speaker; import call.Speakers; import call.UnknownDefaultValueException; import call.Util; public class SettingsAudioTab extends AbstractId implements ConfigListener, ActionListener, ChangeListener { private final int[] pcmRates = Util.reverse(Config.PCM_RATES); @SuppressWarnings("unused") private final MainWindow main; private final JScrollPane scrollpane; private final JComboBox<AudioDevice> comboboxMicrophones; private final JComboBox<AudioDevice> comboboxSpeakers; private final JSlider sliderCallBuffer; private final JRadioButton radioSampleSize8; private final JRadioButton radioSampleSize16; private final JSlider sliderPcmRate; private final JCheckBox checkboxStereo; private final JLabel textSelectedFormat; private PcmFormat format; public SettingsAudioTab(MainWindow main) { this.main = main; // area JPanel settingspanel = new JPanel(new GridBagLayout()); // labels JLabel labelMicrophones = new JLabel(Resources.LABEL_SETTINGS_AUDIO_DEFAULT_MICROPHONE); JLabel labelSpeakers = new JLabel(Resources.LABEL_SETTINGS_AUDIO_DEFAULT_SPEAKER); JLabel labelPcmRate = new JLabel(Resources.LABEL_SETTINGS_AUDIO_SAMPLING_RATE); JLabel labelCallBuffer = new JLabel(Resources.LABEL_SETTINGS_AUDIO_CALL_BUFFER); JLabel labelSampleSize = new JLabel(Resources.LABEL_SETTINGS_AUDIO_SAMPLE_SIZE); JLabel labelSelectedFormat = new JLabel(Resources.LABEL_SETTINGS_AUDIO_SELECTED_ENCODING); // elements comboboxMicrophones = createComboboxMicrophones(); comboboxSpeakers = createComboboxSpeakers(); sliderPcmRate = createSliderPcmRate(); sliderCallBuffer = createSliderCallBuffer(); JRadioButton[] radioSampleSize = createRadioButtonsSampleSize(); radioSampleSize8 = radioSampleSize[0]; radioSampleSize16 = radioSampleSize[1]; checkboxStereo = new JCheckBox("Stereo", false); checkboxStereo.setFont(Resources.FONT_TEXT); textSelectedFormat = new JLabel(""); textSelectedFormat.setFont(Resources.FONT_TEXT); final int NONE = GridBagConstraints.NONE; final int HORIZONTAL = GridBagConstraints.HORIZONTAL; @SuppressWarnings("unused") final int VERTICAL = GridBagConstraints.VERTICAL; final int BOTH = GridBagConstraints.BOTH; add(settingspanel, 0, 0, 0, 0, 0.0, 0.0, NONE, labelMicrophones); add(settingspanel, 1, 0, 4, 0, 0.0, 0.0, HORIZONTAL, comboboxMicrophones); add(settingspanel, 0, 1, 0, 0, 0.0, 0.0, NONE, labelSpeakers); add(settingspanel, 1, 1, 4, 0, 0.0, 0.0, HORIZONTAL, comboboxSpeakers); add(settingspanel, 0, 2, 0, 0, 0.0, 0.0, NONE, labelCallBuffer); add(settingspanel, 1, 2, 4, 0, 0.0, 0.0, HORIZONTAL, sliderCallBuffer); add(settingspanel, 0, 3, 0, 0, 0.0, 0.0, NONE, labelPcmRate); add(settingspanel, 1, 3, 4, 0, 0.0, 0.0, HORIZONTAL, sliderPcmRate); add(settingspanel, 0, 4, 0, 0, 0.0, 0.0, NONE, labelSampleSize); add(settingspanel, 1, 4, 0, 0, 0.0, 0.0, HORIZONTAL, radioSampleSize8); add(settingspanel, 2, 4, 0, 0, 0.0, 0.0, HORIZONTAL, radioSampleSize16); add(settingspanel, 3, 4, 0, 0, 0.0, 0.0, HORIZONTAL, checkboxStereo); add(settingspanel, 0, 5, 0, 0, 0.0, 0.1, HORIZONTAL, new JLabel(" ")); add(settingspanel, 0, 6, 0, 0, 0.0, 0.0, NONE, labelSelectedFormat); add(settingspanel, 1, 6, 4, 0, 0.0, 0.0, NONE, textSelectedFormat); add(settingspanel, 0, 7, 0, 0, 0.0, 1.0, BOTH, empty()); add(settingspanel, 1, 7, 0, 0, 0.0, 1.0, BOTH, empty()); add(settingspanel, 2, 7, 0, 0, 0.0, 1.0, BOTH, empty()); add(settingspanel, 3, 7, 0, 0, 0.0, 1.0, BOTH, empty()); add(settingspanel, 4, 7, 0, 0, 0.0, 1.0, BOTH, empty()); add(settingspanel, 5, 7, 0, 0, 0.8, 1.0, BOTH, empty()); // JPanel mainpanel = new JPanel(new BorderLayout()); // mainpanel.add(settingspanel, BorderLayout.CENTER); // mainpanel.add(new JPanel(), BorderLayout.CENTER); settingspanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); scrollpane = new JScrollPane(settingspanel); // config listener Config.addConfigListener(this); Config.notifyConfigListener(this); updateSelectedFormat(); } private GridBagConstraints add(JPanel panel, int x, int y, int w, int h, double weightx, double weighty, int fill, JComponent comp) { GridBagConstraints c = new GridBagConstraints(); c.fill = fill; c.gridx = x; c.gridy = y; c.gridwidth = 1 + w; c.gridheight = 1 + h; c.anchor = GridBagConstraints.LINE_START; c.weightx = weightx != 0 ? weightx : 0.0; c.weighty = weighty != 0 ? weighty : 0.0; c.ipadx = 5; panel.add(comp, c); return c; } private JComponent empty() { return new JLabel(); } private JRadioButton[] createRadioButtonsSampleSize() { JRadioButton radio1 = new JRadioButton("8 bit"); radio1.setActionCommand("8"); radio1.addActionListener(this); radio1.setSelected(true); radio1.setFont(Resources.FONT_TEXT); JRadioButton radio2 = new JRadioButton("16 bit"); radio2.setActionCommand("16"); radio2.addActionListener(this); radio2.setFont(Resources.FONT_TEXT); ButtonGroup group = new ButtonGroup(); group.add(radio1); group.add(radio2); return new JRadioButton[] { radio1, radio2 }; } private JSlider createSliderCallBuffer() { JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 256, 16); slider.addChangeListener(this); slider.setMajorTickSpacing(32); slider.setMinorTickSpacing(8); slider.setPaintTicks(true); slider.setPaintLabels(true); slider.setFont(Resources.FONT_TEXT); return slider; } private JSlider createSliderPcmRate() { int pcmRateMin = 0; int pcmRateMax = pcmRates.length - 1; int pcmRateDefault = Util.indexOf(pcmRates, Config.PCM_DEFAULT_RATE); JSlider slider = new JSlider(JSlider.HORIZONTAL, pcmRateMin, pcmRateMax, pcmRateDefault); slider.addChangeListener(this); slider.setMajorTickSpacing(pcmRateDefault); slider.setMinorTickSpacing(1); slider.setPaintTicks(true); slider.setFont(Resources.FONT_TEXT); // Create the label table Hashtable<Integer, JLabel> labelTable = new Hashtable<>(); int pcmRateGsm = Util.indexOf(pcmRates, 8000); int pcmRateCd = Util.indexOf(pcmRates, 44100); labelTable.put(new Integer(pcmRateGsm), new JLabel("GSM")); labelTable.put(new Integer(pcmRateCd), new JLabel("CD")); labelTable.put(new Integer(pcmRateMax), new JLabel("max")); slider.setLabelTable(labelTable); slider.setPaintLabels(true); return slider; } private JComboBox<AudioDevice> createComboboxMicrophones() { JComboBox<AudioDevice> combobox = new JComboBox<>(); combobox.setModel(new AudioDeviceListModel(Microphones.getInstance())); combobox.addActionListener(this); combobox.setFont(Resources.FONT_TEXT); return combobox; } private JComboBox<AudioDevice> createComboboxSpeakers() { JComboBox<AudioDevice> combobox = new JComboBox<>(); combobox.setModel(new AudioDeviceListModel(Speakers.getInstance())); combobox.addActionListener(this); combobox.setFont(Resources.FONT_TEXT); return combobox; } public JComponent getComponent() { return scrollpane; } @Override public void actionPerformed(ActionEvent event) { if (event.getSource().equals(comboboxMicrophones)) { if (comboboxMicrophones.getSelectedIndex() != -1) { Microphone selected = (Microphone) comboboxMicrophones.getSelectedItem(); Microphones.setCurrentMicrophone(selected); } } if (event.getSource().equals(comboboxSpeakers)) { if (comboboxSpeakers.getSelectedIndex() != -1) { Speaker selected = (Speaker) comboboxSpeakers.getSelectedItem(); Speakers.setCurrentSpeaker(selected); } } if (event.getSource().equals(radioSampleSize8) || event.getSource().equals(radioSampleSize16)) { int samplesize = Config.SELECTED_PCM_SAMPLE_SIZE.getIntegerValue(); if (radioSampleSize8.isSelected() && samplesize != 8) { Config.SELECTED_PCM_SAMPLE_SIZE.setIntegerValue(8); } if (radioSampleSize16.isSelected() && samplesize != 16) { Config.SELECTED_PCM_SAMPLE_SIZE.setIntegerValue(16); } } updateSelectedFormat(); } @Override public void stateChanged(ChangeEvent event) { if (event.getSource().equals(sliderCallBuffer)) { int buffersize = sliderCallBuffer.getValue(); if (buffersize == 0) { buffersize = 1; sliderCallBuffer.setValue(buffersize); } if (!sliderCallBuffer.getValueIsAdjusting()) { buffersize *= 1024; if (Config.BUFFER_SIZE_CALLS.getIntegerValue() != buffersize) Config.BUFFER_SIZE_CALLS.setIntegerValue(buffersize); } } if (event.getSource().equals(sliderPcmRate)) { if (!sliderPcmRate.getValueIsAdjusting()) { int rate = getSliderPcmRate(); if (Config.SELECTED_PCM_RATE.getIntegerValue() != rate) Config.SELECTED_PCM_RATE.setIntegerValue(rate); } } updateSelectedFormat(); } private int getSliderPcmRate() { int rateIndex = sliderPcmRate.getValue(); if (rateIndex >= 0 && rateIndex < pcmRates.length) { int rate = pcmRates[sliderPcmRate.getValue()]; return rate; } else { return Config.PCM_DEFAULT_RATE; } } @Override public void onConfigUpdate(Option option, float value) {} @Override public void onConfigUpdate(Option option, int value) { if (option.equals(Config.BUFFER_SIZE_CALLS)) { value /= 1024; if (sliderCallBuffer.getValue() != value) { sliderCallBuffer.setValue(value); } } if (option.equals(Config.SELECTED_PCM_RATE)) { int rateindex = Util.indexOf(pcmRates, value); if (rateindex >= sliderPcmRate.getMinimum() && rateindex <= sliderPcmRate.getMaximum() && rateindex != sliderPcmRate.getValue()) { sliderPcmRate.setValue(rateindex); } } if (option.equals(Config.SELECTED_PCM_SAMPLE_SIZE)) { if (value == 8) radioSampleSize8.setSelected(true); else radioSampleSize16.setSelected(true); } updateSelectedFormat(); } @Override public void onConfigUpdate(Option option, boolean value) {} @Override public void onConfigUpdate(Option option, String value) {} @Override public void onConfigUpdate(Option option, Id value) { if (option.equals(Config.SELECTED_MICROPHONE)) { comboboxMicrophones.setSelectedItem(value); } if (option.equals(Config.SELECTED_SPEAKER)) { comboboxSpeakers.setSelectedItem(value); } } public void updateSelectedFormat() { int rate = getSliderPcmRate(); int samplesize = Config.SELECTED_PCM_SAMPLE_SIZE.getIntegerValue(); int channels = Config.SELECTED_PCM_CHANNELS.getIntegerValue(); format = new PcmFormat(rate, samplesize, channels); float kHz = (float) (rate) / 1000; int buffersize = sliderCallBuffer.getValue(); int callbuffer = sliderCallBuffer.getValue() * 1024; float bitrate = format.getBitrate(); float byterate = format.getByterate(); int latency = (int) (1000.0f * callbuffer / byterate); updateCallBufferMaximum(format.getByterate()); String text = "PCM <b>" + (channels == 8 ? "mono" : "stereo") + "</b>"; text += ", sampling rate: <b>" + kHz + " kHz</b>"; text += ", bits per sample: <b>" + samplesize + " bit</b>"; text += ", buffer size: <b>" + buffersize + " KB</b>"; text += "<br>\n"; text += "Bitrate: <b>" + (int) (bitrate / 1024) + " kbit/s</b> (= <b>" + (int) (byterate / 1024) + " KB/s</b>), "; text += "Latency: <b>" + latency + " ms</b>"; boolean supported; try { supported = Microphones.getCurrentMicrophone().supportsFormat(format); } catch (UnknownDefaultValueException e) { supported = true; } if (!supported) { text += "<br>\n"; text += "<b><font color=red>This encoding is not supported by the selected microphone!</font></b>"; } textSelectedFormat.setText("<html>" + text + "</html>"); } private void updateCallBufferMaximum(float byterate) { int max = ((int) (byterate / 1024 / 32) + 1) * 32; sliderCallBuffer.setMaximum(max); if (sliderCallBuffer.getValue() > max) { sliderCallBuffer.setValue(max); } sliderCallBuffer.setMajorTickSpacing(max / 8); sliderCallBuffer.setMinorTickSpacing(max / 32); } @Override public String getId() { return "SettingsCodecsTab"; } }