diff options
author | Phil Burk <[email protected]> | 2022-01-30 12:14:35 -0700 |
---|---|---|
committer | GitHub <[email protected]> | 2022-01-30 11:14:35 -0800 |
commit | e792b44d99676a363bc5e6e6d5c42881e756f956 (patch) | |
tree | cad74da4bf7e2d12052f943bf51256c340502c41 /src/main/java | |
parent | c43a8e222c9a51046c2b11c9549ffa76204d1eb5 (diff) |
WaveRecorder: fix hang in StreamingThread (#106)
The read() could hang forever waiting for data when the
WaveRecorder was stopped.
Now it terminates the thread.
Also fix bug in AudioFifo when partially full. It would not read any data!
Fixes #105
Diffstat (limited to 'src/main/java')
-rw-r--r-- | src/main/java/com/jsyn/io/AudioFifo.java | 48 | ||||
-rw-r--r-- | src/main/java/com/jsyn/util/StreamingThread.java | 15 | ||||
-rw-r--r-- | src/main/java/com/jsyn/util/WaveRecorder.java | 8 |
3 files changed, 43 insertions, 28 deletions
diff --git a/src/main/java/com/jsyn/io/AudioFifo.java b/src/main/java/com/jsyn/io/AudioFifo.java index 0c563e4..b7321c6 100644 --- a/src/main/java/com/jsyn/io/AudioFifo.java +++ b/src/main/java/com/jsyn/io/AudioFifo.java @@ -37,6 +37,7 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { private int sizeMask; private boolean writeWaitEnabled = true; private boolean readWaitEnabled = true; + private volatile boolean mOpen = true; final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); @@ -69,7 +70,12 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { @Override public void close() { - // TODO Maybe we should tell any thread that is waiting that the FIFO is closed. + // Tell any thread that is waiting that the FIFO is closed. + mOpen = false; + lock.lock(); + notEmpty.signal(); + notFull.signal(); + lock.unlock(); } @Override @@ -78,20 +84,22 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { if (readWaitEnabled) { lock.lock(); try { - while (available() < 1) { + while (mOpen && available() < 1) { try { notEmpty.await(); } catch (InterruptedException e) { return Double.NaN; } } - value = readOneInternal(); + if (mOpen) { + value = readOneInternal(); + } } finally { lock.unlock(); } } else { - if (readIndex != writeIndex) { + if (mOpen && readIndex != writeIndex) { value = readOneInternal(); } } @@ -116,7 +124,7 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { if (writeWaitEnabled) { lock.lock(); try { - while (available() == buffer.length) + while (mOpen && available() == buffer.length) { try { notFull.await(); @@ -124,7 +132,9 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { return; // Silently fail } } - writeOneInternal(value); + if (mOpen) { + writeOneInternal(value); + } } finally { lock.unlock(); } @@ -154,20 +164,20 @@ public class AudioFifo implements AudioInputStream, AudioOutputStream { @Override public int read(double[] buffer, int start, int count) { - if (readWaitEnabled) { - for (int i = 0; i < count; i++) { - buffer[i + start] = read(); - } - } else { - if (available() < count) { - count = available(); - } else { - for (int i = 0; i < count; i++) { - buffer[i + start] = read(); - } - } + if (!mOpen) { + return 0; + } + if (!readWaitEnabled) { + count = Math.min(available(), count); + } + int numRead = 0; + for (int i = 0; mOpen && i < count; i++) { + double value = read(); + if (value == Double.NaN) break; + buffer[i + start] = value; + numRead++; } - return count; + return numRead; } @Override diff --git a/src/main/java/com/jsyn/util/StreamingThread.java b/src/main/java/com/jsyn/util/StreamingThread.java index 682f476..7377698 100644 --- a/src/main/java/com/jsyn/util/StreamingThread.java +++ b/src/main/java/com/jsyn/util/StreamingThread.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,7 +23,7 @@ import com.jsyn.io.AudioOutputStream; /** * Read from an AudioInputStream and write to an AudioOutputStream as a background thread. - * + * * @author Phil Burk (C) 2011 Mobileer Inc */ public class StreamingThread extends Thread { @@ -47,17 +47,18 @@ public class StreamingThread extends Thread { try { transportModel.firePositionChanged(framePosition); transportModel.fireStateChanged(TransportModel.STATE_RUNNING); - int framesToRead = geteFramesToRead(buffer); + int framesToRead = getFramesToRead(buffer); while (go && (framesToRead > 0)) { int samplesToRead = framesToRead * samplesPerFrame; while (samplesToRead > 0) { int samplesRead = inputStream.read(buffer, 0, samplesToRead); outputStream.write(buffer, 0, samplesRead); samplesToRead -= samplesRead; + if (samplesRead < samplesToRead) break; // stream closed } framePosition += framesToRead; transportModel.firePositionChanged(framePosition); - framesToRead = geteFramesToRead(buffer); + framesToRead = getFramesToRead(buffer); } transportModel.fireStateChanged(TransportModel.STATE_STOPPED); } catch (IOException e) { @@ -65,7 +66,7 @@ public class StreamingThread extends Thread { } } - private int geteFramesToRead(double[] buffer) { + private int getFramesToRead(double[] buffer) { if (maxFrames > 0) { long numToRead = maxFrames - framePosition; if (numToRead < 0) { @@ -85,7 +86,7 @@ public class StreamingThread extends Thread { /** * Only call this before the thread has started. - * + * * @param framesPerBuffer */ public void setFramesPerBuffer(int framesPerBuffer) { diff --git a/src/main/java/com/jsyn/util/WaveRecorder.java b/src/main/java/com/jsyn/util/WaveRecorder.java index 8008d1d..d189f06 100644 --- a/src/main/java/com/jsyn/util/WaveRecorder.java +++ b/src/main/java/com/jsyn/util/WaveRecorder.java @@ -24,7 +24,8 @@ import com.jsyn.Synthesizer; import com.jsyn.ports.UnitInputPort; /** - * Connect a unit generator to the input. Then start() recording. The signal will be written to a + * Connect a unit generator to the input. Then start() recording. + * The signal will be written to a * WAV format file that can be read by other programs. * * @author Phil Burk (C) 2011 Mobileer Inc @@ -60,7 +61,8 @@ public class WaveRecorder { * @param bitsPerSample 16 or 24 * @throws FileNotFoundException */ - public WaveRecorder(Synthesizer synth, File outputFile, int samplesPerFrame, int bitsPerSample) + public WaveRecorder(Synthesizer synth, File outputFile, + int samplesPerFrame, int bitsPerSample) throws FileNotFoundException { this.synth = synth; reader = new AudioStreamReader(synth, samplesPerFrame); @@ -86,10 +88,12 @@ public class WaveRecorder { public void stop() { if (thread != null) { + reader.close(); thread.requestStop(); try { thread.join(500); } catch (InterruptedException ignored) { + System.out.println("join() " + ignored); } thread = null; } |