diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index ab86556..24a6208 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -34,7 +34,7 @@ public class AudiobookRecorder extends JFrame { public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us"; static Properties config = new Properties(); - HashMap effects; + TreeMap effects; String defaultEffectChain = "none"; @@ -748,7 +748,6 @@ public class AudiobookRecorder extends JFrame { prefs.setProperty("chapter.close.pre-gap", Options.get("catenation.pre-chapter")); prefs.setProperty("chapter.close.post-gap", Options.get("catenation.post-chapter")); - loadEffects(); buildBook(prefs); Options.set("path.last-book", book.getName()); @@ -1684,7 +1683,6 @@ public class AudiobookRecorder extends JFrame { prefs.loadFromXML(fis); File r = f.getParentFile(); - loadEffects(); buildBook(prefs); @@ -1716,6 +1714,8 @@ public class AudiobookRecorder extends JFrame { book.setComment(prefs.getProperty("book.comment")); book.setACX(prefs.getProperty("book.acx")); + loadEffects(); + defaultEffectChain = prefs.getProperty("audio.effect.default"); if (defaultEffectChain == null) { defaultEffectChain = "none"; @@ -2119,7 +2119,7 @@ public class AudiobookRecorder extends JFrame { Thread t = new Thread(new Runnable() { public void run() { Sentence ns = (Sentence)next; - ns.loadFile(); // Cache it + ns.getProcessedAudioData(); // Cache it } }); t.start(); @@ -2649,7 +2649,7 @@ public class AudiobookRecorder extends JFrame { } public void loadEffects() { - effects = new HashMap(); + effects = new TreeMap(); loadEffectsFromFolder(new File(Options.get("path.storage"), "System")); if (book != null) { loadEffectsFromFolder(new File(Options.get("path.storage"), book.getName())); @@ -2705,6 +2705,11 @@ public class AudiobookRecorder extends JFrame { if (eff != null) { group.addEffect(eff); } + } else if (e.getTagName().equals("pan")) { + Effect eff = (Effect)loadPan(e); + if (eff != null) { + group.addEffect(eff); + } } else if (e.getTagName().equals("amplifier")) { Effect eff = (Effect)loadAmplifier(e); if (eff != null) { @@ -2769,6 +2774,9 @@ public class AudiobookRecorder extends JFrame { DelayLine line = new DelayLine(); NodeList list = root.getChildNodes(); + if (Utils.s2b(root.getAttribute("wetonly"))) { + line.setWetOnly(true); + } for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); @@ -2777,7 +2785,9 @@ public class AudiobookRecorder extends JFrame { if (e.getTagName().equals("delay")) { int samples = Utils.s2i(e.getAttribute("samples")); double gain = Utils.s2d(e.getAttribute("gain")); - DelayLineStore store = line.addDelayLine(samples, gain); + double pan = Utils.s2d(e.getAttribute("pan")); + DelayLineStore store = line.addDelayLine(samples, gain, pan); + NodeList inner = e.getChildNodes(); for (int j = 0; j < inner.getLength(); j++) { @@ -2795,6 +2805,11 @@ public class AudiobookRecorder extends JFrame { if (eff != null) { store.addEffect(eff); } + } else if (ie.getTagName().equals("pan")) { + Effect eff = (Effect)loadPan(ie); + if (eff != null) { + store.addEffect(eff); + } } else if (ie.getTagName().equals("amplifier")) { Effect eff = (Effect)loadAmplifier(ie); if (eff != null) { @@ -2835,6 +2850,11 @@ public class AudiobookRecorder extends JFrame { return a; } + public Pan loadPan(Element root) { + Pan p = new Pan(Utils.s2d(root.getAttribute("pan"))); + return p; + } + public Clipping loadClipping(Element root) { Clipping c = new Clipping(Utils.s2d(root.getAttribute("clip"))); return c; diff --git a/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java b/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java index f9b1622..1bcaa09 100644 --- a/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java +++ b/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java @@ -31,6 +31,12 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer { icn.add(Overlays.important, OverlayIcon.TOP_RIGHT); } + if (s.getEffectChain() != null) { + if (!s.getEffectChain().equals("none")) { + icn.add(Overlays.filter, OverlayIcon.BOTTOM_RIGHT); + } + } + ret.setIcon(icn); } else if (value instanceof Chapter) { diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLine.java b/src/uk/co/majenko/audiobookrecorder/DelayLine.java index 4d9624f..a680407 100644 --- a/src/uk/co/majenko/audiobookrecorder/DelayLine.java +++ b/src/uk/co/majenko/audiobookrecorder/DelayLine.java @@ -6,6 +6,8 @@ public class DelayLine implements Effect { ArrayList delayLines; + boolean wetOnly = false; + public DelayLine() { delayLines = new ArrayList(); } @@ -19,6 +21,12 @@ public class DelayLine implements Effect { for (int i = 0; i < samples.length; i++) { savedSamples[i] = new Sample(samples[i].left, samples[i].right); } + if (wetOnly) { + for (int i = 0; i < samples.length; i++) { + samples[i].left = 0d; + samples[i].right = 0d; + } + } for (DelayLineStore d : delayLines) { Sample[] subSamples = new Sample[samples.length]; @@ -62,6 +70,12 @@ public class DelayLine implements Effect { return out; } + public DelayLineStore addDelayLine(int samples, double gain, double pan) { + DelayLineStore s = new DelayLineStore(samples, gain, pan); + delayLines.add(s); + return s; + } + public DelayLineStore addDelayLine(int samples, double gain) { DelayLineStore s = new DelayLineStore(samples, gain); delayLines.add(s); @@ -88,4 +102,8 @@ public class DelayLine implements Effect { s.init(sf); } } + + public void setWetOnly(boolean b) { + wetOnly = b; + } } diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java index bf1addf..394be5d 100644 --- a/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java +++ b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java @@ -6,12 +6,21 @@ import java.util.ArrayList; public class DelayLineStore { double gain; int numSamples; + double pan; ArrayList effects; + public DelayLineStore(int s, double g, double p) { + numSamples = s; + gain = g; + pan = p; + effects = new ArrayList(); + } + public DelayLineStore(int s, double g) { numSamples = s; gain = g; + pan = 0d; effects = new ArrayList(); } @@ -23,6 +32,14 @@ public class DelayLineStore { for (Sample sample : samples) { sample.left *= gain; sample.right *= gain; + + if (pan < 0) { + double p = 1 + pan; + sample.right *= p; + } else { + double p = 1 - pan; + sample.left *= p; + } } } diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index c961148..4fe70d9 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -64,6 +64,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { double storedLength = -1d; Sample[] audioData = null; + Sample[] processedAudio = null; RecordingThread recordingThread; @@ -391,7 +392,11 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public String toString() { - return text; + if (effectChain == null) return text; + if (effectChain.equals("none")) return text; + Effect e = AudiobookRecorder.window.effects.get(effectChain); + if (e == null) return text; + return text + " (" + e.toString() + ")"; } public boolean isRecording() { @@ -560,6 +565,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void clearCache() { audioData = null; + processedAudio = null; } public boolean lockedInCache() { @@ -669,8 +675,11 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void setGain(double g) { if (g <= 0.0001d) g = 1.0d; if (g == gain) return; + + if (gain != g) { + clearCache(); + } gain = g; - clearCache(); } public double getGain() { @@ -952,10 +961,12 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public Sample[] getProcessedAudioData() { loadFile(); + if (processedAudio != null) return processedAudio; + if (audioData == null) return null; - Sample[] samples = new Sample[audioData.length]; + processedAudio = new Sample[audioData.length]; for (int i = 0; i < audioData.length; i++) { - samples[i] = new Sample(audioData[i].left, audioData[i].right); + processedAudio[i] = new Sample(audioData[i].left, audioData[i].right); } // Add processing in here. @@ -965,7 +976,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { if (eff != null) { eff.init(getAudioFormat().getFrameRate()); - eff.process(samples); + eff.process(processedAudio); } if (effectChain != null) { @@ -974,17 +985,17 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { eff = AudiobookRecorder.window.effects.get(effectChain); if (eff != null) { eff.init(getAudioFormat().getFrameRate()); - eff.process(samples); + eff.process(processedAudio); } } } // Add final master gain stage - for (int i = 0; i < samples.length; i++) { - samples[i].left = samples[i].left * gain; - samples[i].right = samples[i].right * gain; + for (int i = 0; i < processedAudio.length; i++) { + processedAudio[i].left = processedAudio[i].left * gain; + processedAudio[i].right = processedAudio[i].right * gain; } - return samples; + return processedAudio; } public Sample[] getDoubleAudioData() { @@ -1028,6 +1039,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public void setEffectChain(String key) { + if ((effectChain != null) && (!effectChain.equals(key))) { + clearCache(); + } effectChain = key; }