diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/eq.png b/resources/uk/co/majenko/audiobookrecorder/icons/eq.png new file mode 100644 index 0000000..b713f25 Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/eq.png differ diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 9bd59d1..11a8d5b 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -48,6 +48,8 @@ public class AudiobookRecorder extends JFrame { JScrollPane mainScroll; + JDialog equaliserWindow = null; + Book book = null; JTree bookTree; @@ -1147,6 +1149,10 @@ public class AudiobookRecorder extends JFrame { prefs.setProperty("book.genre", book.getGenre()); prefs.setProperty("book.comment", book.getComment()); + for (int i = 0; i < 31; i++) { + prefs.setProperty("audio.eq." + i, String.format("%.3f", book.equaliser.getChannel(i))); + } + for (Enumeration o = book.children(); o.hasMoreElements();) { Chapter c = o.nextElement(); @@ -1212,6 +1218,14 @@ public class AudiobookRecorder extends JFrame { book.setGenre(prefs.getProperty("book.genre")); book.setComment(prefs.getProperty("book.comment")); + for (int i = 0; i < 31; i++) { + if (prefs.getProperty("audio.eq." + i) == null) { + book.equaliser.setChannel(i, Options.getFloat("audio.eq." + i)); + } else { + book.equaliser.setChannel(i, Utils.s2f(prefs.getProperty("audio.eq." + i))); + } + } + bookTreeModel = new DefaultTreeModel(book); bookTree = new JTree(bookTreeModel); bookTree.setEditable(true); @@ -1632,4 +1646,14 @@ public class AudiobookRecorder extends JFrame { public void alertNoRoomNoise() { JOptionPane.showMessageDialog(this, "You must record room noise\nbefore recording or playback", "Error", JOptionPane.ERROR_MESSAGE); } + + public void showEqualiser() { + if (equaliserWindow == null) { + equaliserWindow = new JDialog(); + equaliserWindow.setTitle("Equaliser"); + equaliserWindow.add(book.equaliser); + equaliserWindow.pack(); + } + equaliserWindow.setVisible(true); + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Book.java b/src/uk/co/majenko/audiobookrecorder/Book.java index 4c214f9..79edf9f 100644 --- a/src/uk/co/majenko/audiobookrecorder/Book.java +++ b/src/uk/co/majenko/audiobookrecorder/Book.java @@ -8,6 +8,7 @@ import java.util.*; import java.io.*; import java.nio.file.*; import javax.swing.tree.*; +import davaguine.jeq.core.IIRControls; public class Book extends DefaultMutableTreeNode { @@ -18,9 +19,14 @@ public class Book extends DefaultMutableTreeNode { ImageIcon icon; + public Equaliser equaliser; + + float[] eqChannels = new float[31]; + public Book(String bookname) { super(bookname); name = bookname; + equaliser = new Equaliser(); } public void setAuthor(String a) { diff --git a/src/uk/co/majenko/audiobookrecorder/Equaliser.java b/src/uk/co/majenko/audiobookrecorder/Equaliser.java new file mode 100644 index 0000000..84ddc0b --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/Equaliser.java @@ -0,0 +1,71 @@ +package uk.co.majenko.audiobookrecorder; + +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.io.*; +import java.nio.file.*; +import javax.swing.tree.*; +import javax.sound.sampled.*; +import davaguine.jeq.core.IIRControls; + +public class Equaliser extends JPanel { + + EqualiserChannel channels[]; + + + public Equaliser() { + super(); + + channels = new EqualiserChannel[31]; + + setLayout(new BorderLayout()); + + JPanel inner = new JPanel(); + + inner.setLayout(new FlowLayout()); + + for (int i = 0; i < 31; i++) { + channels[i] = new EqualiserChannel(); + inner.add(channels[i]); + } + + add(inner, BorderLayout.CENTER); + + + JButton smooth = new JButton("Smooth curve"); + smooth.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Float ave[] = new Float[31]; + for (int i = 1; i < 30; i++) { + ave[i] = (channels[i-1].getValue() + channels[i].getValue() + channels[i+1].getValue()) / 3.0f; + } + + for (int i = 1; i < 30; i++) { + channels[i].setValue(ave[i]); + } + } + }); + + add(smooth, BorderLayout.SOUTH); + } + + public float getChannel(int c) { + return channels[c].getValue(); + } + + public void setChannel(int c, float v) { + channels[c].setValue(v); + } + + public void apply(IIRControls c, int chans) { + for (int i = 0; i < 31; i++) { + c.setBandDbValue(i, 0, channels[i].getValue()); + if (chans == 2) { + c.setBandDbValue(i, 1, channels[i].getValue()); + } + } + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java b/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java new file mode 100644 index 0000000..64e4ccf --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java @@ -0,0 +1,78 @@ +package uk.co.majenko.audiobookrecorder; + +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.io.*; +import java.nio.file.*; +import javax.swing.tree.*; +import javax.sound.sampled.*; + +public class EqualiserChannel extends JPanel { + + float value; + JSlider slider; + JTextField textbox; + + public EqualiserChannel() { + super(); + + value = 0; + + slider = new JSlider(-120, 120, 0); + textbox = new JTextField(); + + setLayout(new BorderLayout()); + + slider.setOrientation(SwingConstants.VERTICAL); + add(slider, BorderLayout.CENTER); + textbox = new JTextField("0.0"); + add(textbox, BorderLayout.SOUTH); + + textbox.setPreferredSize(new Dimension(40, 20)); + slider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + value = (float)slider.getValue() / 10.0f; + textbox.setText(String.format("%.1f", value)); + } + }); + + textbox.addFocusListener(new FocusListener() { + public void focusGained(FocusEvent e) { + textbox.selectAll(); + } + + public void focusLost(FocusEvent e) { + value = Utils.s2f(textbox.getText()); + if (value < -12f) value = -12f; + if (value > 12f) value = 12f; + + slider.setValue((int)(value * 10)); + textbox.setText(String.format("%.1f", value)); + } + }); + + textbox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + value = Utils.s2f(textbox.getText()); + if (value < -12f) value = -12f; + if (value > 12f) value = 12f; + + slider.setValue((int)(value * 10)); + textbox.setText(String.format("%.1f", value)); + } + }); + } + + public float getValue() { + return value; + } + + public void setValue(float v) { + value = v; + slider.setValue((int)(value * 10)); + textbox.setText(String.format("%.1f", value)); + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/Icons.java b/src/uk/co/majenko/audiobookrecorder/Icons.java index 4db759f..0255b0e 100644 --- a/src/uk/co/majenko/audiobookrecorder/Icons.java +++ b/src/uk/co/majenko/audiobookrecorder/Icons.java @@ -26,5 +26,6 @@ public class Icons { static public final ImageIcon spinner1 = new ImageIcon(Icons.class.getResource("icons/spinner1.png")); static public final ImageIcon spinner2 = new ImageIcon(Icons.class.getResource("icons/spinner2.png")); static public final ImageIcon spinner3 = new ImageIcon(Icons.class.getResource("icons/spinner3.png")); + static public final ImageIcon eq = new ImageIcon(Icons.class.getResource("icons/eq.png")); } diff --git a/src/uk/co/majenko/audiobookrecorder/JSliderOb.java b/src/uk/co/majenko/audiobookrecorder/JSliderOb.java new file mode 100644 index 0000000..cebdda6 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/JSliderOb.java @@ -0,0 +1,20 @@ +package uk.co.majenko.audiobookrecorder; + +import javax.swing.*; + +public class JSliderOb extends JSlider { + Object object; + + public JSliderOb(int a, int b, int c) { + super(a, b, c); + } + + public void setObject(Object o) { + object = o; + } + + public Object getObject() { + return object; + } +} + diff --git a/src/uk/co/majenko/audiobookrecorder/JTextFieldOb.java b/src/uk/co/majenko/audiobookrecorder/JTextFieldOb.java new file mode 100644 index 0000000..43dfc4b --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/JTextFieldOb.java @@ -0,0 +1,19 @@ +package uk.co.majenko.audiobookrecorder; +import javax.swing.*; + +public class JTextFieldOb extends JTextField { + Object object; + + public JTextFieldOb(String s) { + super(s); + } + + public void setObject(Object o) { + object = o; + } + + public Object getObject() { + return object; + } +} + diff --git a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java index 17a1621..3ffd9f6 100644 --- a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java +++ b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java @@ -15,6 +15,7 @@ public class MainToolBar extends JToolBar { JButton playSentence; JButton playonSentence; JButton stopPlaying; + JButton eq; AudiobookRecorder root; @@ -99,6 +100,20 @@ public class MainToolBar extends JToolBar { }); add(stopPlaying); + addSeparator(); + eq = new JButton(Icons.eq); + eq.setToolTipText("Equaliser"); + eq.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + root.showEqualiser(); + } + }); + + add(eq); + + + + setFloatable(false); } diff --git a/src/uk/co/majenko/audiobookrecorder/Options.java b/src/uk/co/majenko/audiobookrecorder/Options.java index 3ffc067..6f6194f 100644 --- a/src/uk/co/majenko/audiobookrecorder/Options.java +++ b/src/uk/co/majenko/audiobookrecorder/Options.java @@ -30,46 +30,11 @@ public class Options extends JDialog { JCheckBox enableParsing; JSpinner cacheSize; - JSliderOb[] sliders; - JTextFieldOb[] eqvals; - + Equaliser equaliser; static HashMap defaultPrefs; static Preferences prefs = null; - static class JSliderOb extends JSlider { - Object object; - - public JSliderOb(int a, int b, int c) { - super(a, b, c); - } - - public void setObject(Object o) { - object = o; - } - - public Object getObject() { - return object; - } - } - - public class JTextFieldOb extends JTextField { - Object object; - - public JTextFieldOb(String s) { - super(s); - } - - public void setObject(Object o) { - object = o; - } - - public Object getObject() { - return object; - } - } - - static class KVPair implements Comparable { public String key; public String value; @@ -311,98 +276,13 @@ public class Options extends JDialog { tabs.add("Options", optionsPanel); - - - JPanel eqPanel = new JPanel(); - - eqPanel.setLayout(new GridBagLayout()); - - constraint.gridx = 0; - constraint.gridy = 0; - - sliders = new JSliderOb[31]; - eqvals = new JTextFieldOb[31]; + equaliser = new Equaliser(); for (int i = 0; i < 31; i++) { - sliders[i] = new JSliderOb(-120, 120, (int)(Options.getFloat("audio.eq." + i) * 10)); - sliders[i].setOrientation(SwingConstants.VERTICAL); - constraint.gridx = i; - constraint.gridy = 0; - eqPanel.add(sliders[i], constraint); - eqvals[i] = new JTextFieldOb(String.format("%.1f", Options.getFloat("audio.eq." + i))); - constraint.gridy = 1; - eqPanel.add(eqvals[i], constraint); - - sliders[i].setObject(eqvals[i]); - eqvals[i].setObject(sliders[i]); - - eqvals[i].setPreferredSize(new Dimension(40, 20)); - - sliders[i].addChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - JSliderOb o = (JSliderOb)e.getSource(); - String v = String.format("%.1f", (float)o.getValue() / 10.0f); - JTextFieldOb tf = (JTextFieldOb)o.getObject(); - tf.setText(v); - } - }); - - eqvals[i].addFocusListener(new FocusListener() { - public void focusGained(FocusEvent e) { - JTextFieldOb o = (JTextFieldOb)e.getSource(); - o.selectAll(); - } - - public void focusLost(FocusEvent e) { - JTextFieldOb o = (JTextFieldOb)e.getSource(); - String v = o.getText(); - float f = Utils.s2f(v); - if (f < -12f) f = -12f; - if (f > 12f) f = 12f; - JSliderOb s = (JSliderOb)o.getObject(); - s.setValue((int)(f * 10)); - o.setText(String.format("%.1f", f)); - } - }); - - eqvals[i].addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JTextFieldOb o = (JTextFieldOb)e.getSource(); - String v = o.getText(); - float f = Utils.s2f(v); - if (f < -12f) f = -12f; - if (f > 12f) f = 12f; - JSliderOb s = (JSliderOb)o.getObject(); - s.setValue((int)(f * 10)); - o.setText(String.format("%.1f", f)); - o.selectAll(); - } - }); - + equaliser.setChannel(i, Options.getFloat("audio.eq." + i)); } - JButton smooth = new JButton("Smooth curve"); - smooth.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Float ave[] = new Float[31]; - for (int i = 1; i < 30; i++) { - ave[i] = (Utils.s2f(eqvals[i - 1].getText()) + Utils.s2f(eqvals[i].getText()) + Utils.s2f(eqvals[i + 1].getText())) / 3.0f; - } - - for (int i = 1; i < 30; i++) { - eqvals[i].setText(String.format("%.1f", ave[i])); - sliders[i].setValue((int)(ave[i] * 10)); - } - } - }); - - constraint.gridx = 0; - constraint.gridy = 2; - constraint.gridwidth = 4; - eqPanel.add(smooth, constraint); - - tabs.add("EQ", eqPanel); - + tabs.add("EQ", equaliser); add(tabs, BorderLayout.CENTER); @@ -704,7 +584,7 @@ public class Options extends JDialog { set("cache.size", cacheSize.getValue()); for (int i = 0; i < 31; i++) { - set("audio.eq." + i, eqvals[i].getText()); + set("audio.eq." + i, equaliser.getChannel(i)); } savePreferences(); diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index 1845957..b836a17 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -420,12 +420,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { EqualizerInputStream eq = new EqualizerInputStream(s, 31); AudioFormat format = eq.getFormat(); IIRControls controls = eq.getControls(); - for (int i = 0; i < 31; i++) { - controls.setBandDbValue(i, 0, Options.getFloat("audio.eq." + i)); - if (format.getChannels() == 2) { - controls.setBandDbValue(i, 1, Options.getFloat("audio.eq." + i)); - } - } + AudiobookRecorder.window.book.equaliser.apply(controls, format.getChannels()); int frameSize = format.getFrameSize(); @@ -459,12 +454,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudioFormat format = eq.getFormat(); IIRControls controls = eq.getControls(); - for (int i = 0; i < 31; i++) { - controls.setBandDbValue(i, 0, Options.getFloat("audio.eq." + i)); - if (format.getChannels() == 2) { - controls.setBandDbValue(i, 1, Options.getFloat("audio.eq." + i)); - } - } + AudiobookRecorder.window.book.equaliser.apply(controls, format.getChannels()); int frameSize = format.getFrameSize(); @@ -504,12 +494,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudioFormat format = eq.getFormat(); IIRControls controls = eq.getControls(); - for (int i = 0; i < 31; i++) { - controls.setBandDbValue(i, 0, Options.getFloat("audio.eq." + i)); - if (format.getChannels() == 2) { - controls.setBandDbValue(i, 1, Options.getFloat("audio.eq." + i)); - } - } + AudiobookRecorder.window.book.equaliser.apply(controls, format.getChannels()); int frameSize = format.getFrameSize(); int length = crossEndOffset - crossStartOffset;