From 1c056c9e7b2a1cebacdf2bd90fc401b1abf8d73c Mon Sep 17 00:00:00 2001 From: Matt Jenkins Date: Sun, 16 Sep 2018 15:34:49 +0100 Subject: [PATCH] Added zero crossing detection #7 --- .../audiobookrecorder/AudiobookRecorder.java | 9 +- .../audiobookrecorder/CacheManager.java | 2 - .../majenko/audiobookrecorder/Sentence.java | 92 +++++++++++++++++-- .../majenko/audiobookrecorder/Waveform.java | 24 +++++ 4 files changed, 116 insertions(+), 11 deletions(-) diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 2dc077c..94819ce 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -257,6 +257,7 @@ public class AudiobookRecorder extends JFrame { selectedSentence.autoTrimSampleFFT(); sampleWaveform.setData(selectedSentence.getAudioData()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); + sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); startOffset.setValue(selectedSentence.getStartOffset()); endOffset.setValue(selectedSentence.getEndOffset()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -272,6 +273,7 @@ public class AudiobookRecorder extends JFrame { selectedSentence.autoTrimSamplePeak(); sampleWaveform.setData(selectedSentence.getAudioData()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); + sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); startOffset.setValue(selectedSentence.getStartOffset()); endOffset.setValue(selectedSentence.getEndOffset()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -292,6 +294,8 @@ public class AudiobookRecorder extends JFrame { if (selectedSentence != null) { selectedSentence.setStartOffset((Integer)ob.getValue()); sampleWaveform.setLeftMarker((Integer)ob.getValue()); + selectedSentence.updateStartCrossing(); + sampleWaveform.setLeftAltMarker(selectedSentence.getStartCrossing()); } } }); @@ -302,6 +306,8 @@ public class AudiobookRecorder extends JFrame { if (selectedSentence != null) { selectedSentence.setEndOffset((Integer)ob.getValue()); sampleWaveform.setRightMarker((Integer)ob.getValue()); + selectedSentence.updateEndCrossing(); + sampleWaveform.setRightAltMarker(selectedSentence.getEndCrossing()); } } }); @@ -1170,6 +1176,8 @@ public class AudiobookRecorder extends JFrame { selectedSentence = s; sampleWaveform.setData(s.getAudioData()); sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset()); + s.updateCrossings(); + sampleWaveform.setAltMarkers(s.getStartCrossing(), s.getEndCrossing()); startOffset.setValue(s.getStartOffset()); endOffset.setValue(s.getEndOffset()); postSentenceGap.setValue(s.getPostGap()); @@ -1532,7 +1540,6 @@ public class AudiobookRecorder extends JFrame { File mp3File = new File(export, name + "-untagged.mp3"); File taggedFile = new File(export, name + ".mp3"); - System.err.println(attributes); encoder.encode(wavFile, mp3File, attributes); Mp3File id3 = new Mp3File(mp3File); diff --git a/src/uk/co/majenko/audiobookrecorder/CacheManager.java b/src/uk/co/majenko/audiobookrecorder/CacheManager.java index 428d20e..f5eff26 100644 --- a/src/uk/co/majenko/audiobookrecorder/CacheManager.java +++ b/src/uk/co/majenko/audiobookrecorder/CacheManager.java @@ -14,12 +14,10 @@ public class CacheManager { cache.add(ob); } else { ob.clearCache(); - System.err.println("Purged " + ob); } } cache.add(c); - System.err.println("Cached " + c); } public static void setCacheSize(int c) { diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index abd7500..24d1fa4 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -20,6 +20,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { int postGap; int startOffset = 0; int endOffset = 0; + int crossStartOffset = -1; + int crossEndOffset = -1; int sampleSize = -1; @@ -132,6 +134,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public void autoTrimSampleFFT() { + crossStartOffset = -1; + crossEndOffset = -1; int[] samples = getAudioData(); if (samples == null) return; @@ -205,10 +209,13 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { if (endOffset <= startOffset) endOffset = startOffset + 4096; if (endOffset < 0) endOffset = 0; if (endOffset >= samples.length) endOffset = samples.length; + updateCrossings(); } public void autoTrimSamplePeak() { + crossStartOffset = -1; + crossEndOffset = -1; int[] samples = getAudioData(); if (samples == null) return; int noiseFloor = AudiobookRecorder.window.getNoiseFloor(); @@ -247,6 +254,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { if (startOffset < 0) startOffset = 0; if (endOffset >= samples.length) endOffset = samples.length-1; + updateCrossings(); } public String getId() { @@ -349,12 +357,40 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { return null; } + public int getStartCrossing() { + return crossStartOffset; + } + public int getStartOffset() { return startOffset; } + public void updateCrossings() { + updateStartCrossing(); + updateEndCrossing(); + } + + public void updateStartCrossing() { + if (crossStartOffset == -1) { + crossStartOffset = findNearestZeroCrossing(startOffset, 4096); + } + } + + public void updateEndCrossing() { + if (crossEndOffset == -1) { + crossEndOffset = findNearestZeroCrossing(endOffset, 4096); + } + } + public void setStartOffset(int o) { - startOffset = o; + if (startOffset != o) { + startOffset = o; + crossStartOffset = -1; + } + } + + public int getEndCrossing() { + return crossEndOffset; } public int getEndOffset() { @@ -362,7 +398,10 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public void setEndOffset(int o) { - endOffset = o; + if (endOffset != o) { + endOffset = o; + crossEndOffset = -1; + } } public int getSampleSize() { @@ -411,7 +450,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { long len = s.getFrameLength(); int frameSize = format.getFrameSize(); - int pos = startOffset * frameSize; + updateCrossings(); + + int pos = crossStartOffset * frameSize; SourceDataLine play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer()); play.open(format); @@ -422,7 +463,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { s.skip(pos); - while (pos < endOffset * frameSize) { + while (pos < crossEndOffset * frameSize) { int nr = s.read(buffer); pos += nr; @@ -438,13 +479,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public byte[] getRawAudioData() { File f = getFile(); try { + updateCrossings(); AudioInputStream s = AudioSystem.getAudioInputStream(f); AudioFormat format = s.getFormat(); int frameSize = format.getFrameSize(); - int length = endOffset - startOffset; + int length = crossEndOffset - crossStartOffset; byte[] data = new byte[length * frameSize]; - s.skip(startOffset * frameSize); + s.skip(crossStartOffset * frameSize); s.read(data); @@ -497,8 +539,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } - System.err.println("Decimated from " + length + " to " + newLen); - ByteArrayInputStream bas = new ByteArrayInputStream(decimated); recognizer.startRecognition(bas); SpeechResult result; @@ -546,4 +586,40 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public boolean lockedInCache() { return id.equals("room-noise"); } + + public int findNearestZeroCrossing(int pos, int range) { + int[] data = getAudioData(); + + if (pos < 0) pos = 0; + if (pos >= data.length) pos = data.length-1; + + int backwards = pos; + int forwards = pos; + + int backwardsPrev = data[backwards]; + int forwardsPrev = data[forwards]; + + 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! + return backwards; + } + + if (forwardsPrev < 0 && data[forwards] >= 0) { + return forwards; + } + + range--; + if (range == 0) { + return pos; + } + + backwardsPrev = data[backwards]; + forwardsPrev = data[forwards]; + } + return pos; + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Waveform.java b/src/uk/co/majenko/audiobookrecorder/Waveform.java index b592156..19048cc 100644 --- a/src/uk/co/majenko/audiobookrecorder/Waveform.java +++ b/src/uk/co/majenko/audiobookrecorder/Waveform.java @@ -12,6 +12,9 @@ public class Waveform extends JPanel { int leftMarker = 0; int rightMarker = 0; + int leftAltMarker = 0; + int rightAltMarker = 0; + public Waveform() { super(); } @@ -87,15 +90,36 @@ public class Waveform extends JPanel { g.setColor(new Color(255, 0, 0)); g.drawLine(leftMarker/step, 0, leftMarker/step, h); g.drawLine(rightMarker/step, 0, rightMarker/step, h); + + g.setColor(new Color(255, 255, 0)); + + g.drawLine(leftAltMarker/step, 0, leftAltMarker/step, h); + g.drawLine(rightAltMarker/step, 0, rightAltMarker/step, h); } } + public void setAltMarkers(int l, int r) { + leftAltMarker = l; + rightAltMarker = r; + repaint(); + } + public void setMarkers(int l, int r) { leftMarker = l; rightMarker = r; repaint(); } + public void setLeftAltMarker(int l) { + leftAltMarker = l; + repaint(); + } + + public void setRightAltMarker(int r) { + rightAltMarker = r; + repaint(); + } + public void setLeftMarker(int l) { leftMarker = l; repaint();