aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorPhil Burk <[email protected]>2022-01-30 12:14:35 -0700
committerGitHub <[email protected]>2022-01-30 11:14:35 -0800
commite792b44d99676a363bc5e6e6d5c42881e756f956 (patch)
treecad74da4bf7e2d12052f943bf51256c340502c41 /src/main
parentc43a8e222c9a51046c2b11c9549ffa76204d1eb5 (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')
-rw-r--r--src/main/java/com/jsyn/io/AudioFifo.java48
-rw-r--r--src/main/java/com/jsyn/util/StreamingThread.java15
-rw-r--r--src/main/java/com/jsyn/util/WaveRecorder.java8
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;
}