From 38c6af7090b2966f501ef5bb62db94c02a04d6b5 Mon Sep 17 00:00:00 2001 From: Matt Jenkins Date: Wed, 29 Jan 2020 20:00:45 +0000 Subject: [PATCH] Implement wheel over waveform to change gain --- .../audiobookrecorder/AudiobookRecorder.java | 51 ++++++++--- .../majenko/audiobookrecorder/Sentence.java | 90 ++++++++++++++----- 2 files changed, 106 insertions(+), 35 deletions(-) diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 86348eb..80b9762 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -439,6 +439,16 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { } }); + sampleWaveform.addMouseWheelListener(new MouseWheelListener() { + public void mouseWheelMoved(MouseWheelEvent e) { + if (selectedSentence == null) return; + if (selectedSentence.isLocked()) return; + int val = ((int)gainPercent.getValue()) - e.getWheelRotation(); + if (val < 1) val = 1; + gainPercent.setValue(val); + } + }); + sampleWaveform.addMarkerDragListener(new MarkerDragListener() { public void leftMarkerMoved(MarkerDragEvent e) { Debug.trace(); @@ -474,7 +484,6 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { Debug.trace(); if (selectedSentence != null) { selectedSentence.autoTrimSampleFFT(); - sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled)); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -488,7 +497,6 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { Debug.trace(); if (selectedSentence != null) { selectedSentence.autoTrimSamplePeak(); - sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled)); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -502,7 +510,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { Debug.trace(); if (selectedSentence != null) { selectedSentence.normalize(); - sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled)); + gainPercent.setValue((int)(selectedSentence.getGain() * 100d)); + updateWaveform(true); } } }); @@ -550,7 +559,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { JSpinner ob = (JSpinner)e.getSource(); if (selectedSentence != null) { selectedSentence.setGain((Integer)ob.getValue() / 100d); - sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled)); + updateWaveform(true); } } }); @@ -672,7 +681,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { KVPair p = effectChain.getItemAt(i); if (p == null) return; selectedSentence.setEffectChain(p.getKey()); - updateWaveform(); + updateWaveform(true); } } }); @@ -2947,6 +2956,10 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { } + public Sentence getRoomNoiseSentence() { + return roomNoise; + } + public byte[] getRoomNoise(int ms) { Debug.trace(); @@ -3461,9 +3474,13 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { } public void updateWaveform() { + updateWaveform(false); + } + + public void updateWaveform(boolean force) { Debug.trace(); if (selectedSentence != null) { - if ((sampleWaveform.getId() != null) && (sampleWaveform.getId().equals(selectedSentence.getId()))) return; + if ((!force) && (sampleWaveform.getId() != null) && (sampleWaveform.getId().equals(selectedSentence.getId()))) return; sampleWaveform.setId(selectedSentence.getId()); if (rawAudio.isSelected()) { sampleWaveform.setData(selectedSentence.getRawAudioData()); @@ -3796,17 +3813,17 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { KVPair p = effectChain.getItemAt(i); if (p.getKey().equals(key)) { effectChain.setSelectedIndex(i); - updateWaveform(); + updateWaveform(true); return; } } if (effects.get(book.getDefaultEffect()) != null) { setEffectChain(book.getDefaultEffect()); - updateWaveform(); + updateWaveform(true); } else { effectChain.setSelectedIndex(0); - updateWaveform(); + updateWaveform(true); } } @@ -3874,12 +3891,14 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { int a = 0; for (int i = 0; i < samples[Sentence.LEFT].length; i++) { if ((i < start) || (i > end)) { - croppedSamples[a++] = samples[i]; + croppedSamples[Sentence.LEFT][a] = samples[Sentence.LEFT][i]; + croppedSamples[Sentence.RIGHT][a] = samples[Sentence.RIGHT][i]; + a++; } } selectedSentence.writeAudioData(croppedSamples); selectedSentence.autoTrimSample(); - updateWaveform(); + updateWaveform(true); } catch (Exception ex) { ex.printStackTrace(); } @@ -3906,9 +3925,13 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { for (int i = 0; i < samples[Sentence.LEFT].length; i++) { if (i < at) { - startSamples[a++] = samples[i]; + startSamples[Sentence.LEFT][a] = samples[Sentence.LEFT][i]; + startSamples[Sentence.RIGHT][a] = samples[Sentence.RIGHT][i]; + a++; } else { - endSamples[b++] = samples[i]; + endSamples[Sentence.LEFT][b] = samples[Sentence.LEFT][i]; + endSamples[Sentence.RIGHT][b] = samples[Sentence.RIGHT][i]; + b++; } } @@ -3919,7 +3942,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener { selectedSentence.writeAudioData(endSamples); selectedSentence.autoTrimSample(); newSentence.autoTrimSample(); - updateWaveform(); + updateWaveform(true); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index ed1de38..3f5f611 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -86,6 +86,8 @@ public class Sentence extends BookTreeNode implements Cacheable { double[][] processedAudio = null; + double[] fftProfile = null; + RecordingThread recordingThread; boolean effectEthereal = false; @@ -282,6 +284,17 @@ public class Sentence extends BookTreeNode implements Cacheable { Debug.trace(); autoTrimSampleFFT(false); } + + public double bucketDifference(double[] a, double[] b) { + double diff = 0d; + int l = Math.min(a.length, b.length); + for (int i = 0; i < l; i++) { + if ((a[i] - b[i]) > diff) { + diff = (a[i] - b[i]); + } + } + return diff; + } public void autoTrimSampleFFT(boolean useRaw) { Debug.trace(); @@ -297,11 +310,13 @@ public class Sentence extends BookTreeNode implements Cacheable { return; } + double[] roomNoiseProfile = AudiobookRecorder.window.getRoomNoiseSentence().getFFTProfile(); + int fftSize = Options.getInteger("audio.recording.trim.blocksize"); int blocks = samples[LEFT].length / fftSize + 1; - int[] intens = new int[blocks]; + double[] intens = new double[blocks]; int block = 0; for (int i = 0; i < samples[LEFT].length; i+= fftSize) { @@ -319,31 +334,20 @@ public class Sentence extends BookTreeNode implements Cacheable { } double[] buckets = FFT.fft(real, imag, true); - double av = 0; - for (int j = 1; j < fftSize/2; j++) { - av += Math.abs(buckets[j]); - } - av /= (fftSize / 2); - intens[block] = 0; - for (int j = 2; j < fftSize; j += 2) { - double d = Math.abs(av - buckets[j]); - if (d > 0.05) { - intens[block]++; - } - } + intens[block] = bucketDifference(buckets, roomNoiseProfile); block++; } - int limit = Options.getInteger("audio.recording.trim.fft"); + double limit = (double)(Options.getInteger("audio.recording.trim.fft")); // Find first block with > 1 intensity and subtract one. int start = 0; for (int i = 0; i < blocks; i++) { - if (intens[i] > limit) break; + if (intens[i] >= (limit / 100.0)) break; start = i; } @@ -358,7 +362,7 @@ public class Sentence extends BookTreeNode implements Cacheable { int end = blocks - 1; // And last block with > 1 intensity and add one. for (int i = blocks-1; i >= 0; i--) { - if (intens[i] > limit) break; + if (intens[i] >= (limit / 100)) break; end = i; } @@ -380,6 +384,50 @@ public class Sentence extends BookTreeNode implements Cacheable { reloadTree(); } + public double[] getFFTProfile() { + Debug.trace(); + if (fftProfile != null) return fftProfile; + + double[][] samples = getProcessedAudioData(); + if (samples == null) { + return null; + } + + int fftSize = Options.getInteger("audio.recording.trim.blocksize"); + + fftProfile = new double[fftSize / 2]; + for (int j = 1; j < fftSize/2; j++) { + fftProfile[j] = 0d; + } + + for (int i = 0; i < samples[LEFT].length; i+= fftSize) { + double[] real = new double[fftSize]; + double[] imag = new double[fftSize]; + + for (int j = 0; j < fftSize; j++) { + if (i + j < samples[LEFT].length) { + real[j] = (samples[LEFT][i+j] + samples[RIGHT][i+j]) / 2d; + imag[j] = 0; + } else { + real[j] = 0; + imag[j] = 0; + } + } + + double[] buckets = FFT.fft(real, imag, true); + + for (int j = 1; j < fftSize/2; j++) { + fftProfile[j] += Math.abs(buckets[j]); + } + } + + for (int j = 1; j < fftSize/2; j++) { + fftProfile[j] /= (double)(fftSize / 2d); + } + return fftProfile; + } + + public void autoTrimSamplePeak() { Debug.trace(); autoTrimSamplePeak(false); @@ -873,12 +921,12 @@ public class Sentence extends BookTreeNode implements Cacheable { int gint = (int)(g * 100d); int gainint = (int)(gain * 100d); + gain = g; if (gint != gainint) { CacheManager.removeFromCache(this); peak = -1; reloadTree(); } - gain = g; } public double getGain() { @@ -894,8 +942,8 @@ public class Sentence extends BookTreeNode implements Cacheable { if (d > 1d) d = 1d; if (d < low) d = low; if (d > high) d = high; - peak = -1; setGain(d); + peak = -1; getPeak(); reloadTree(); return d; @@ -945,7 +993,7 @@ public class Sentence extends BookTreeNode implements Cacheable { } catch (Exception e) { } CacheManager.removeFromCache(Sentence.this); - AudiobookRecorder.window.updateWaveform(); + AudiobookRecorder.window.updateWaveform(true); } } @@ -1039,7 +1087,7 @@ public class Sentence extends BookTreeNode implements Cacheable { } CacheManager.removeFromCache(Sentence.this); - AudiobookRecorder.window.updateWaveform(); + AudiobookRecorder.window.updateWaveform(true); } } @@ -1084,7 +1132,7 @@ public class Sentence extends BookTreeNode implements Cacheable { } CacheManager.removeFromCache(this); - AudiobookRecorder.window.updateWaveform(); + AudiobookRecorder.window.updateWaveform(true); } public double[][] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException {