From c01fee3b73432bb5bd267a3dd0a7f454b854db1f Mon Sep 17 00:00:00 2001 From: Matt Jenkins Date: Sun, 21 Jul 2019 17:21:50 +0100 Subject: [PATCH] Switch to Sample object instead of double, and refactor effects for whole sampleset processing --- src/uk/co/majenko/audiobookrecorder/AGC.java | 39 ++++--- .../majenko/audiobookrecorder/Amplifier.java | 7 +- .../audiobookrecorder/AudiobookRecorder.java | 14 +-- .../co/majenko/audiobookrecorder/Biquad.java | 45 ++++++-- .../majenko/audiobookrecorder/Clipping.java | 11 +- .../majenko/audiobookrecorder/DelayLine.java | 57 +++++++--- .../audiobookrecorder/DelayLineStore.java | 37 ++----- .../co/majenko/audiobookrecorder/Effect.java | 2 +- .../audiobookrecorder/EffectGroup.java | 6 +- src/uk/co/majenko/audiobookrecorder/LFO.java | 25 +++-- .../co/majenko/audiobookrecorder/Sample.java | 4 +- .../majenko/audiobookrecorder/Sentence.java | 104 +++++++++--------- .../majenko/audiobookrecorder/Waveform.java | 6 +- 13 files changed, 200 insertions(+), 157 deletions(-) diff --git a/src/uk/co/majenko/audiobookrecorder/AGC.java b/src/uk/co/majenko/audiobookrecorder/AGC.java index 4847900..b966329 100644 --- a/src/uk/co/majenko/audiobookrecorder/AGC.java +++ b/src/uk/co/majenko/audiobookrecorder/AGC.java @@ -25,27 +25,36 @@ public class AGC implements Effect { return getName(); } - public double process(double sample) { - double absSample = Math.abs(sample) * gain; + public void process(Sample[] samples) { + gain = 1d; + for (int i = 0; i < samples.length; i++) { + double absSampleLeft = Math.abs(samples[i].left) * gain; + double absSampleRight = Math.abs(samples[i].right) * gain; - if (absSample > ceiling) { - gain -= attack; - if (gain < 0) gain = 0; - } - - if (absSample < ceiling) { - gain += decay; - if (gain > limit) { - gain = limit; + if (absSampleLeft > ceiling) { + gain -= attack; + if (gain < 0) gain = 0; } + + if (absSampleRight > ceiling) { + gain -= attack; + if (gain < 0) gain = 0; + } + + if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) { + gain += decay; + if (gain > limit) { + gain = limit; + } + } + + samples[i].left *= gain; + samples[i].right *= gain; } - - sample *= gain; - - return sample; } public void init(double sr) { + gain = 1d; } public void dump() { diff --git a/src/uk/co/majenko/audiobookrecorder/Amplifier.java b/src/uk/co/majenko/audiobookrecorder/Amplifier.java index 80e2fe3..83278cf 100644 --- a/src/uk/co/majenko/audiobookrecorder/Amplifier.java +++ b/src/uk/co/majenko/audiobookrecorder/Amplifier.java @@ -19,8 +19,11 @@ public class Amplifier implements Effect { return null; } - public double process(double sample) { - return sample * gain; + public void process(Sample[] samples) { + for (int i = 0; i < samples.length; i++) { + samples[i].left *= gain; + samples[i].right *= gain; + } } public double getGain() { diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index c2d0eaf..ab86556 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -29,7 +29,7 @@ public class AudiobookRecorder extends JFrame { // Settings - tweakable - public static final int PLAYBACK_CHUNK_SIZE = 256; // Was 1024 + public static final int PLAYBACK_CHUNK_SIZE = 16384; public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us"; @@ -1943,14 +1943,14 @@ public class AudiobookRecorder extends JFrame { public double getNoiseFloor() { if (roomNoise == null) return 0; - double[] samples = roomNoise.getDoubleAudioData(); + Sample[] samples = roomNoise.getDoubleAudioData(); if (samples == null) { return 0; } double ms = 0; for (int i = 0; i < samples.length; i++) { - if (Math.abs(samples[i]) > ms) { - ms = Math.abs(samples[i]); + if (Math.abs(samples[i].getMono()) > ms) { + ms = Math.abs(samples[i].getMono()); } } @@ -1977,7 +1977,7 @@ public class AudiobookRecorder extends JFrame { public void run() { roomNoise.stopRecording(); centralPanel.setFlash(false); - statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB"); + statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB"); } }, 5000); // 5 seconds of recording } @@ -2001,7 +2001,7 @@ public class AudiobookRecorder extends JFrame { try { AudioFormat sampleformat = s.getAudioFormat(); - AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); + AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false); play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer()); play.open(format); @@ -2095,7 +2095,7 @@ public class AudiobookRecorder extends JFrame { try { AudioFormat sampleformat = s.getAudioFormat(); - AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); + AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false); play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer()); play.open(format); play.start(); diff --git a/src/uk/co/majenko/audiobookrecorder/Biquad.java b/src/uk/co/majenko/audiobookrecorder/Biquad.java index 1b3ef92..415841e 100644 --- a/src/uk/co/majenko/audiobookrecorder/Biquad.java +++ b/src/uk/co/majenko/audiobookrecorder/Biquad.java @@ -32,7 +32,8 @@ public class Biquad implements Effect { int type; double a0, a1, a2, b1, b2; double Fc, Q, peakGain; - double z1, z2; + double lz1, lz2; + double rz1, rz2; double sampleFrequency; public Biquad() { @@ -45,15 +46,19 @@ public class Biquad implements Effect { Fc = 440d; Q = 0.707d; peakGain = 0.0d; - z1 = 0.0d; - z2 = 0.0d; + lz1 = 0.0d; + lz2 = 0.0d; + rz1 = 0.0d; + rz2 = 0.0d; sampleFrequency = 44100d; } public Biquad(int type, double Fc, double Q, double peakGainDB) { setBiquad(type, Fc, Q, peakGainDB); - z1 = 0.0; - z2 = 0.0; + lz1 = 0.0; + lz2 = 0.0; + rz1 = 0.0; + rz2 = 0.0; sampleFrequency = 44100d; } @@ -84,17 +89,33 @@ public class Biquad implements Effect { setPeakGain(peakGainDB); } - public double process(double in) { - double out = in * a0 + z1; - z1 = in * a1 + z2 - b1 * out; - z2 = in * a2 - b2 * out; - return out; + public void process(Sample[] samples) { + lz1 = 0d; + lz2 = 0d; + rz1 = 0d; + rz2 = 0d; + for (Sample in : samples) { + double lout = in.left * a0 + lz1; + + lz1 = in.left * a1 + lz2 - b1 * lout; + lz2 = in.left * a2 - b2 * lout; + + double rout = in.right * a0 + rz1; + + rz1 = in.right * a1 + rz2 - b1 * rout; + rz2 = in.right * a2 - b2 * rout; + + in.left = lout; + in.right = rout; + } } public void init(double sf) { sampleFrequency = sf; - z1 = 0d; - z2 = 0d; + lz1 = 0d; + lz2 = 0d; + rz1 = 0d; + rz2 = 0d; calcBiquad(); } diff --git a/src/uk/co/majenko/audiobookrecorder/Clipping.java b/src/uk/co/majenko/audiobookrecorder/Clipping.java index 59db0a8..e7f582b 100644 --- a/src/uk/co/majenko/audiobookrecorder/Clipping.java +++ b/src/uk/co/majenko/audiobookrecorder/Clipping.java @@ -19,10 +19,13 @@ public class Clipping implements Effect { return null; } - public double process(double sample) { - if (sample > clip) return clip; - if (sample < -clip) return -clip; - return sample; + public void process(Sample[] samples) { + for (Sample sample : samples) { + if (sample.left > clip) sample.left = clip; + if (sample.left < -clip) sample.left = -clip; + if (sample.right > clip) sample.right = clip; + if (sample.right < -clip) sample.right = -clip; + } } public double getClip() { diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLine.java b/src/uk/co/majenko/audiobookrecorder/DelayLine.java index 5cc7977..4d9624f 100644 --- a/src/uk/co/majenko/audiobookrecorder/DelayLine.java +++ b/src/uk/co/majenko/audiobookrecorder/DelayLine.java @@ -14,23 +14,52 @@ public class DelayLine implements Effect { return "Delay Line (" + delayLines.size() + " lines)"; } - public double process(double sample) { - double s = sample; - for (DelayLineStore d : delayLines) { - double echo = d.pass(sample); - s = mix(s, echo); + public void process(Sample[] samples) { + Sample[] savedSamples = new Sample[samples.length]; + for (int i = 0; i < samples.length; i++) { + savedSamples[i] = new Sample(samples[i].left, samples[i].right); + } + + for (DelayLineStore d : delayLines) { + Sample[] subSamples = new Sample[samples.length]; + for (int i = 0; i < samples.length; i++) { + subSamples[i] = new Sample(savedSamples[i].left, savedSamples[i].right); + } + + d.process(subSamples); + + for (int i = 0; i < subSamples.length; i++) { + int off = i + d.getSamples(); + if ((off < samples.length) && (off > 0)) { + + Sample ns = mix(samples[off], subSamples[i]); + samples[off].left = ns.left; + samples[off].right = ns.right; + } + } } - return s; } - double mix(double a, double b) { - if ((a < 0) && (b < 0)) { - return (a + b) - (a * b); - } - if ((a > 0) && (b > 0)) { - return (a + b) - (a * b); + Sample mix(Sample a, Sample b) { + Sample out = new Sample(0, 0); + + if ((a.left < 0) && (b.left < 0)) { + out.left = (a.left + b.left) - (a.left * b.left); + } else if ((a.left > 0) && (b.left > 0)) { + out.left = (a.left + b.left) - (a.left * b.left); + } else { + out.left = a.left + b.left; } - return a + b; + + if ((a.right < 0) && (b.right < 0)) { + out.right = (a.right + b.right) - (a.right * b.right); + } else if ((a.right > 0) && (b.right > 0)) { + out.right = (a.right + b.right) - (a.right * b.right); + } else { + out.right = a.right + b.right; + } + + return out; } public DelayLineStore addDelayLine(int samples, double gain) { @@ -59,6 +88,4 @@ public class DelayLine implements Effect { s.init(sf); } } - - } diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java index 60a89b9..bf1addf 100644 --- a/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java +++ b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java @@ -4,42 +4,29 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.ArrayList; public class DelayLineStore { - ArrayBlockingQueue fifo; double gain; int numSamples; ArrayList effects; public DelayLineStore(int s, double g) { - fifo = new ArrayBlockingQueue(s); - for (int i = 0; i < s; i++) { - fifo.add(0d); - } numSamples = s; gain = g; effects = new ArrayList(); } - public double pass(double s) { - try { - for (Effect e : effects) { - s = e.process(s); - } - double v = s * gain; - double t = fifo.poll(); - fifo.add(v); - return t; - } catch (Exception e) { - e.printStackTrace(); + public void process(Sample[] samples) { + for (Effect e : effects) { + e.process(samples); + } + + for (Sample sample : samples) { + sample.left *= gain; + sample.right *= gain; } - return 0d; } public void setSamples(int s) { - fifo = new ArrayBlockingQueue(s); - for (int i = 0; i < s; i++) { - fifo.add(0d); - } numSamples = s; } @@ -55,13 +42,6 @@ public class DelayLineStore { return gain; } - public void purge() { - fifo.clear(); - for (int i = 0; i < numSamples; i++) { - fifo.add(0d); - } - } - public void addEffect(Effect e) { effects.add(e); } @@ -70,7 +50,6 @@ public class DelayLineStore { for (Effect e : effects) { e.init(sf); } - purge(); } public void dump() { diff --git a/src/uk/co/majenko/audiobookrecorder/Effect.java b/src/uk/co/majenko/audiobookrecorder/Effect.java index 6e5021d..a58dcdf 100644 --- a/src/uk/co/majenko/audiobookrecorder/Effect.java +++ b/src/uk/co/majenko/audiobookrecorder/Effect.java @@ -3,7 +3,7 @@ package uk.co.majenko.audiobookrecorder; import java.util.ArrayList; public interface Effect { - public double process(double sample); + public void process(Sample[] samples); public String getName(); public ArrayList getChildEffects(); public void dump(); diff --git a/src/uk/co/majenko/audiobookrecorder/EffectGroup.java b/src/uk/co/majenko/audiobookrecorder/EffectGroup.java index 3591984..43d4768 100644 --- a/src/uk/co/majenko/audiobookrecorder/EffectGroup.java +++ b/src/uk/co/majenko/audiobookrecorder/EffectGroup.java @@ -16,12 +16,10 @@ public class EffectGroup implements Effect { effects = new ArrayList(); } - public double process(double sample) { - double out = sample; + public void process(Sample[] samples) { for (Effect e : effects) { - out = e.process(out); + e.process(samples); } - return out; } public void setName(String n) { diff --git a/src/uk/co/majenko/audiobookrecorder/LFO.java b/src/uk/co/majenko/audiobookrecorder/LFO.java index 2195955..1907e61 100644 --- a/src/uk/co/majenko/audiobookrecorder/LFO.java +++ b/src/uk/co/majenko/audiobookrecorder/LFO.java @@ -22,24 +22,25 @@ public class LFO implements Effect { phase = p; } - public double process(double sample) { - double v = Math.sin(phase); - phase += sampleStep; - if (phase > (Math.PI * 2d)) { - phase -= (Math.PI * 2d); - } + public void process(Sample[] samples) { + for (Sample sample : samples) { + double v = Math.sin(phase); + phase += sampleStep; + if (phase > (Math.PI * 2d)) { + phase -= (Math.PI * 2d); + } // // Make it between 0 and 1. // v = 1d + v; // v /= 2d; - // Multiply it by the gain factor - v *= depth; + // Multiply it by the gain factor + v *= depth; - // Apply it to the sample - sample += (sample * v); - - return sample; + // Apply it to the sample + sample.left += (sample.left * v); + sample.right += (sample.right * v); + } } public String getName() { return "Low Frequency Oscillator (" + frequency + " Hz, " + (depth * 100d) + "%)"; } diff --git a/src/uk/co/majenko/audiobookrecorder/Sample.java b/src/uk/co/majenko/audiobookrecorder/Sample.java index add80fb..a87e53b 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sample.java +++ b/src/uk/co/majenko/audiobookrecorder/Sample.java @@ -1,8 +1,8 @@ package uk.co.majenko.audiobookrecorder; public class Sample { - double left; - double right; + public double left; + public double right; public Sample(double m) { left = m; diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index 8af49db..c961148 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -63,7 +63,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudioFormat storedFormat = null; double storedLength = -1d; - double[] audioData = null; + Sample[] audioData = null; RecordingThread recordingThread; @@ -198,7 +198,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { double[] real = new double[FFTBuckets]; double[] imag = new double[FFTBuckets]; - double[] samples = getProcessedAudioData(); + Sample[] samples = getProcessedAudioData(); int slices = (samples.length / FFTBuckets) + 1; double[][] out = new double[slices][]; @@ -208,7 +208,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { for (int i = 0; i < samples.length; i += FFTBuckets) { for (int j = 0; j < FFTBuckets; j++) { if (i + j < samples.length) { - real[j] = samples[i+j]; + real[j] = samples[i+j].getMono(); imag[j] = 0; } else { real[j] = 0; @@ -227,7 +227,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void autoTrimSampleFFT() { crossStartOffset = -1; crossEndOffset = -1; - double[] samples = getProcessedAudioData(); + Sample[] samples = getProcessedAudioData(); if (samples == null) return; int blocks = samples.length / 4096 + 1; @@ -241,7 +241,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { for (int j = 0; j < 4096; j++) { if (i + j < samples.length) { - real[j] = samples[i+j]; + real[j] = samples[i+j].getMono(); imag[j] = 0; } else { real[j] = 0; @@ -310,7 +310,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void autoTrimSamplePeak() { crossStartOffset = -1; crossEndOffset = -1; - double[] samples = getProcessedAudioData(); + Sample[] samples = getProcessedAudioData(); if (samples == null) return; double noiseFloor = AudiobookRecorder.window.getNoiseFloor(); noiseFloor *= 1.1; @@ -318,7 +318,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { // Find start for (int i = 0; i < samples.length; i++) { startOffset = i; - if (Math.abs(samples[i]) > noiseFloor) { + if (Math.abs(samples[i].getMono()) > noiseFloor) { startOffset --; if (startOffset < 0) startOffset = 0; break; @@ -333,7 +333,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { for (int i = samples.length-1; i >= 0; i--) { endOffset = i; - if (Math.abs(samples[i]) > noiseFloor) { + if (Math.abs(samples[i].getMono()) > noiseFloor) { endOffset ++; if (endOffset >= samples.length-1) endOffset = samples.length-1; break; @@ -567,7 +567,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public int findNearestZeroCrossing(int pos, int range) { - double[] data = getProcessedAudioData(); + Sample[] data = getProcessedAudioData(); if (data == null) return 0; if (data.length == 0) return 0; @@ -577,19 +577,19 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { int backwards = pos; int forwards = pos; - double backwardsPrev = data[backwards]; - double forwardsPrev = data[forwards]; + double backwardsPrev = data[backwards].getMono(); + double forwardsPrev = data[forwards].getMono(); while (backwards > 0 || forwards < data.length-2) { if (forwards < data.length-2) forwards++; if (backwards > 0) backwards--; - if (backwardsPrev >= 0 && data[backwards] < 0) { // Found one! + if (backwardsPrev >= 0 && data[backwards].getMono() < 0) { // Found one! return backwards; } - if (forwardsPrev < 0 && data[forwards] >= 0) { + if (forwardsPrev < 0 && data[forwards].getMono() >= 0) { return forwards; } @@ -598,8 +598,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { return pos; } - backwardsPrev = data[backwards]; - forwardsPrev = data[forwards]; + backwardsPrev = data[backwards].getMono(); + forwardsPrev = data[forwards].getMono(); } return pos; } @@ -643,15 +643,15 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public double getPeakValue() { double oldGain = gain; gain = 1.0d; - double[] samples = getProcessedAudioData(); + Sample[] samples = getProcessedAudioData(); gain = oldGain; if (samples == null) { return 0; } double ms = 0; for (int i = 0; i < samples.length; i++) { - if (Math.abs(samples[i]) > ms) { - ms = Math.abs(samples[i]); + if (Math.abs(samples[i].getMono()) > ms) { + ms = Math.abs(samples[i].getMono()); } } return ms; @@ -851,19 +851,18 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudiobookRecorder.window.updateWaveform(); } - public double[] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException { + public Sample[] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException { long len = s.getFrameLength(); int frameSize = format.getFrameSize(); int chans = format.getChannels(); int bytes = frameSize / chans; byte[] frame = new byte[frameSize]; - double[] samples = new double[(int)len]; + Sample[] samples = new Sample[(int)len]; for (long fno = 0; fno < len; fno++) { s.read(frame); - int sample = 0; if (chans == 2) { // Stereo int ll = frame[0] >= 0 ? frame[0] : 256 + frame[0]; int lh = frame[1] >= 0 ? frame[1] : 256 + frame[1]; @@ -873,27 +872,27 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { int right = (rh << 8) | rl; if ((left & 0x8000) == 0x8000) left |= 0xFFFF0000; if ((right & 0x8000) == 0x8000) right |= 0xFFFF0000; - sample = (left + right) / 2; + samples[(int)fno] = new Sample((double)left / 32768d, (double)right / 32768d); } else { int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; int h = frame[1] >= 0 ? frame[1] : 256 + frame[1]; - sample = (h << 8) | l; - if ((sample & 0x8000) == 0x8000) sample |= 0xFFFF0000; + int mono = (h << 8) | l; + if ((mono & 0x8000) == 0x8000) mono |= 0xFFFF0000; + samples[(int)fno] = new Sample((double)mono / 32768d); } - samples[(int)fno] = (double)sample / 32768d; } return samples; } - public double[] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException { + public Sample[] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException { long len = s.getFrameLength(); int frameSize = format.getFrameSize(); int chans = format.getChannels(); int bytes = frameSize / chans; byte[] frame = new byte[frameSize]; - double[] samples = new double[(int)len]; + Sample[] samples = new Sample[(int)len]; for (long fno = 0; fno < len; fno++) { @@ -910,15 +909,15 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { int right = (rh << 16) | (rm << 8) | rl; if ((left & 0x800000) == 0x800000) left |= 0xFF000000; if ((right & 0x800000) == 0x800000) right |= 0xFF000000; - sample = (left + right) / 2; + samples[(int)fno] = new Sample((double)left / 8388608d, (double)right / 8388608d); } else { int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; int m = frame[1] >= 0 ? frame[1] : 256 + frame[1]; int h = frame[2] >= 0 ? frame[2] : 256 + frame[2]; - sample = (h << 16) | (m << 8) | l; - if ((sample & 0x800000) == 0x800000) sample |= 0xFF000000; + int mono = (h << 16) | (m << 8) | l; + if ((mono & 0x800000) == 0x800000) mono |= 0xFF000000; + samples[(int)fno] = new Sample((double)mono / 8388608d); } - samples[(int)fno] = (double)sample / 8388608d; } return samples; @@ -932,7 +931,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudioInputStream s = AudioSystem.getAudioInputStream(f); AudioFormat format = getAudioFormat(); - double[] samples = null; + Sample[] samples = null; switch (format.getSampleSizeInBits()) { case 16: @@ -951,12 +950,12 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } } - public double[] getProcessedAudioData() { + public Sample[] getProcessedAudioData() { loadFile(); if (audioData == null) return null; - double[] samples = new double[audioData.length]; + Sample[] samples = new Sample[audioData.length]; for (int i = 0; i < audioData.length; i++) { - samples[i] = audioData[i]; + samples[i] = new Sample(audioData[i].left, audioData[i].right); } // Add processing in here. @@ -966,9 +965,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { if (eff != null) { eff.init(getAudioFormat().getFrameRate()); - for (int i = 0; i < samples.length; i++) { - samples[i] = eff.process(samples[i]); - } + eff.process(samples); } if (effectChain != null) { @@ -977,32 +974,31 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { eff = AudiobookRecorder.window.effects.get(effectChain); if (eff != null) { eff.init(getAudioFormat().getFrameRate()); - for (int i = 0; i < samples.length; i++) { - samples[i] = eff.process(samples[i]); - } + eff.process(samples); } } } // Add final master gain stage for (int i = 0; i < samples.length; i++) { - samples[i] = samples[i] * gain; + samples[i].left = samples[i].left * gain; + samples[i].right = samples[i].right * gain; } return samples; } - public double[] getDoubleAudioData() { + public Sample[] getDoubleAudioData() { return getProcessedAudioData(); } - public double[] getCroppedAudioData() { - double[] inSamples = getDoubleAudioData(); + public Sample[] getCroppedAudioData() { + Sample[] inSamples = getDoubleAudioData(); if (inSamples == null) return null; updateCrossings(); int length = crossEndOffset - crossStartOffset; - double[] samples = new double[length]; + Sample[] samples = new Sample[length]; for (int i = 0; i < length; i++) { samples[i] = inSamples[crossStartOffset + i]; } @@ -1010,17 +1006,23 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public byte[] getPCMData() { - double[] croppedData = getCroppedAudioData(); + Sample[] croppedData = getCroppedAudioData(); if (croppedData == null) return null; int length = croppedData.length; - byte[] pcmData = new byte[length * 2]; + byte[] pcmData = new byte[length * 4]; for (int i = 0; i < length; i++) { - double sd = croppedData[i] * 32768d; + double sd = croppedData[i].left * 32768d; int si = (int)sd; if (si > 32767) si = 32767; if (si < -32768) si = -32768; - pcmData[i * 2] = (byte)(si & 0xFF); - pcmData[(i * 2) + 1] = (byte)((si & 0xFF00) >> 8); + pcmData[i * 4] = (byte)(si & 0xFF); + pcmData[(i * 4) + 1] = (byte)((si & 0xFF00) >> 8); + sd = croppedData[i].right * 32768d; + si = (int)sd; + if (si > 32767) si = 32767; + if (si < -32768) si = -32768; + pcmData[(i * 4) + 2] = (byte)(si & 0xFF); + pcmData[(i * 4) + 3] = (byte)((si & 0xFF00) >> 8); } return pcmData; } diff --git a/src/uk/co/majenko/audiobookrecorder/Waveform.java b/src/uk/co/majenko/audiobookrecorder/Waveform.java index 42d9672..7fe896a 100644 --- a/src/uk/co/majenko/audiobookrecorder/Waveform.java +++ b/src/uk/co/majenko/audiobookrecorder/Waveform.java @@ -9,7 +9,7 @@ import javax.sound.sampled.*; public class Waveform extends JPanel implements MouseListener, MouseMotionListener { - double[] samples = null; + Sample[] samples = null; int leftMarker = 0; int rightMarker = 0; @@ -92,7 +92,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen double lmax = 0; for (int o = 0; o < step; o++) { - double sample = samples[offset + (n * step) + o]; + double sample = samples[offset + (n * step) + o].getMono(); if (sample >= 0) { have += sample; hcnt++; @@ -189,7 +189,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen repaint(); } - public void setData(double[] s) { + public void setData(Sample[] s) { samples = s; playMarker = 0; repaint();