From eb1c2fc7a1647a731c5c9c82dd09e21404422d3f Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Sep 2015 09:58:28 -0400 Subject: [PATCH] Resolved issue with audio and baseband wave recorder writing incorrect file size to recording. --- src/instrument/tap/stream/FloatBufferTap.java | 2 - .../wave/ComplexBufferWaveRecorder.java | 88 ++++++++++------- src/record/wave/RealBufferWaveRecorder.java | 95 +++++++++++-------- src/record/wave/WaveWriter.java | 39 ++++++-- 4 files changed, 142 insertions(+), 82 deletions(-) diff --git a/src/instrument/tap/stream/FloatBufferTap.java b/src/instrument/tap/stream/FloatBufferTap.java index 5b1508f5..6f658216 100644 --- a/src/instrument/tap/stream/FloatBufferTap.java +++ b/src/instrument/tap/stream/FloatBufferTap.java @@ -21,7 +21,6 @@ public class FloatBufferTap extends FloatTap implements Listener @Override public void receive( RealBuffer buffer ) { - mLog.debug("Got a buffer!"); if( mRealBufferListener != null ) { mRealBufferListener.receive( buffer ); @@ -29,7 +28,6 @@ public class FloatBufferTap extends FloatTap implements Listener for( float sample: buffer.getSamples() ) { - mLog.debug("Sending a sample to the tap"); super.receive( sample ); } } diff --git a/src/record/wave/ComplexBufferWaveRecorder.java b/src/record/wave/ComplexBufferWaveRecorder.java index defbf357..bb2f3ebc 100644 --- a/src/record/wave/ComplexBufferWaveRecorder.java +++ b/src/record/wave/ComplexBufferWaveRecorder.java @@ -23,6 +23,7 @@ import java.nio.file.Paths; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.AudioFormat; @@ -58,6 +59,8 @@ public class ComplexBufferWaveRecorder extends Module private ScheduledFuture mProcessorHandle; private LinkedBlockingQueue mBuffers = new LinkedBlockingQueue<>( 500 ); + private AtomicBoolean mRunning = new AtomicBoolean(); + public ComplexBufferWaveRecorder( ThreadPoolManager threadPoolManager, int sampleRate, String filePrefix ) { @@ -79,7 +82,7 @@ public class ComplexBufferWaveRecorder extends Module public void start() { - if( !running() ) + if( mRunning.compareAndSet( false, true ) ) { if( mBufferProcessor == null ) { @@ -110,51 +113,27 @@ public class ComplexBufferWaveRecorder extends Module } } - public boolean running() - { - return mProcessorHandle != null; - } - public void stop() { - if( running() ) + if( mRunning.compareAndSet( true, false ) ) { - mThreadPoolManager.cancel( mProcessorHandle ); - - mProcessorHandle = null; - - if( mWriter != null ) - { - try - { - mWriter.close(); - } - catch( IOException io ) - { - mLog.error( "Error stopping complex wave recorder [" + - getFile() + "]", io ); - } - } - + receive( new PoisonPill() ); } - - mBuffers.clear(); - mWriter = null; - mFile = null; } @Override public void receive( ComplexBuffer buffer ) { - if( running() ) + if( mRunning.get() ) { boolean success = mBuffers.offer( buffer ); if( !success ) { - mLog.error( "ComplexWaveRecorder: buffer overflow or error - throwing " - + "away a buffer of sample data" ); - stop(); + mLog.error( "recorder buffer overflow - purging [" + + mFile.toFile().getAbsolutePath() + "]" ); + + mBuffers.clear(); } } } @@ -188,13 +167,43 @@ public class ComplexBufferWaveRecorder extends Module while( buffer != null ) { - mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) ); - buffer = mBuffers.poll(); + if( buffer instanceof PoisonPill ) + { + buffer = null; + + mBuffers.clear(); + + if( mWriter != null ) + { + try + { + mWriter.close(); + mWriter = null; + } + catch( IOException io ) + { + mLog.error( "Error stopping complex wave recorder [" + + getFile() + "]", io ); + } + } + + mFile = null; + + mThreadPoolManager.cancel( mProcessorHandle ); + + mProcessorHandle = null; + } + else + { + mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) ); + buffer = mBuffers.poll(); + } } } catch ( IOException ioe ) { /* Stop this module if/when we get an IO exception */ + mBuffers.clear(); stop(); mLog.error( "IOException while trying to write to the wave " @@ -202,4 +211,15 @@ public class ComplexBufferWaveRecorder extends Module } } } + + /** + * This is used as a sentinel value to signal the buffer processor to end + */ + public class PoisonPill extends ComplexBuffer + { + public PoisonPill() + { + super( new float[ 1 ] ); + } + } } diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java index f609220d..4d48d934 100644 --- a/src/record/wave/RealBufferWaveRecorder.java +++ b/src/record/wave/RealBufferWaveRecorder.java @@ -20,6 +20,7 @@ package record.wave; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -32,8 +33,10 @@ import module.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import record.wave.ComplexBufferWaveRecorder.PoisonPill; import sample.ConversionUtils; import sample.Listener; +import sample.complex.ComplexBuffer; import sample.real.IRealBufferListener; import sample.real.RealBuffer; import util.TimeStamp; @@ -127,30 +130,9 @@ public class RealBufferWaveRecorder extends Module { if( mRunning.compareAndSet( true, false ) ) { - mThreadPoolManager.cancel( mProcessorHandle ); - - mProcessorHandle = null; - - if( mWriter != null ) - { - try - { - /* Flush remaining buffers */ - writeAudio(); - mWriter.close(); - } - catch( IOException io ) - { - mLog.error( "Error stopping real wave recorder [" + - getFile() + "]", io ); - } - } - + /* Signal the buffer processor to end with a poison pill */ + receive( new PoisonPill() ); } - - mBuffers.clear(); - mWriter = null; - mFile = null; } @Override @@ -162,9 +144,10 @@ public class RealBufferWaveRecorder extends Module if( !success ) { - mLog.error( "recorder buffer overflow or error - throwing " - + "away a buffer of sample data" ); - stop(); + mLog.error( "recorder buffer overflow - purging [" + + mFile.toFile().getAbsolutePath() + "]" ); + + mBuffers.clear(); } mLastBufferReceived = System.currentTimeMillis(); @@ -190,28 +173,53 @@ public class RealBufferWaveRecorder extends Module { } - private void writeAudio() throws IOException - { - RealBuffer buffer = mBuffers.poll(); - - while( buffer != null ) - { - mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) ); - buffer = mBuffers.poll(); - } - } - public class BufferProcessor implements Runnable { public void run() { try { - writeAudio(); + RealBuffer buffer = mBuffers.poll(); + + while( buffer != null ) + { + if( buffer instanceof PoisonPill ) + { + buffer = null; + + mBuffers.clear(); + + if( mWriter != null ) + { + try + { + mWriter.close(); + mWriter = null; + } + catch( IOException io ) + { + mLog.error( "Error stopping complex wave recorder [" + + getFile() + "]", io ); + } + } + + mFile = null; + + mThreadPoolManager.cancel( mProcessorHandle ); + + mProcessorHandle = null; + } + else + { + mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) ); + buffer = mBuffers.poll(); + } + } } catch ( IOException ioe ) { /* Stop this module if/when we get an IO exception */ + mBuffers.clear(); stop(); mLog.error( "IOException while trying to write to the wave " @@ -219,4 +227,15 @@ public class RealBufferWaveRecorder extends Module } } } + + /** + * This is used as a sentinel value to signal the buffer processor to end + */ + public class PoisonPill extends RealBuffer + { + public PoisonPill() + { + super( new float[ 1 ] ); + } + } } diff --git a/src/record/wave/WaveWriter.java b/src/record/wave/WaveWriter.java index d1c64a1c..cce4a0ad 100644 --- a/src/record/wave/WaveWriter.java +++ b/src/record/wave/WaveWriter.java @@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory; public class WaveWriter implements AutoCloseable { - private final static Logger mLog = LoggerFactory.getLogger( WaveWriter.class ); +// private final static Logger mLog = LoggerFactory.getLogger( WaveWriter.class ); private static final Pattern FILENAME_PATTERN = Pattern.compile( "(.*_)(\\d+)(\\.wav)" ); @@ -118,6 +118,7 @@ public class WaveWriter implements AutoCloseable */ public void close() throws IOException { + mFileChannel.force( true ); mFileChannel.close(); } @@ -194,10 +195,13 @@ public class WaveWriter implements AutoCloseable private void updateWaveFileSize() throws IOException { /* Update overall wave size (total size - 8 bytes) */ - mFileChannel.write( getUnsignedIntegerBuffer( mFileChannel.size() - 8 ), 4 ); + ByteBuffer buffer = getUnsignedIntegerBuffer( mFileChannel.size() - 8 ); - /* Update chunk 2 size (total size - 44 bytes) */ - mFileChannel.write( getUnsignedIntegerBuffer( mFileChannel.size() - 44 ), 40 ); + mFileChannel.write( buffer, 4 ); + + ByteBuffer buffer2 = getUnsignedIntegerBuffer( mFileChannel.size() - 44 ); + + mFileChannel.write( buffer2, 40 ); } /** @@ -206,20 +210,39 @@ public class WaveWriter implements AutoCloseable * * The buffer's position is set to 0 to prepare it for writing to a channel. */ - private static ByteBuffer getUnsignedIntegerBuffer( long size ) + protected static ByteBuffer getUnsignedIntegerBuffer( long size ) { ByteBuffer buffer = ByteBuffer.allocate( 4 ); buffer.put( (byte)( size & 0xFFl ) ); - buffer.put( (byte)( ( size & 0xFF00l ) >> 8 ) ); - buffer.put( (byte)( ( size & 0xFF0000l ) >> 16 ) ); - buffer.put( (byte)( ( size & 0xFF000000l ) >> 32 ) ); + buffer.put( (byte)( Long.rotateRight( size & 0xFF00l, 8 ) ) ); + buffer.put( (byte)( Long.rotateRight( size & 0xFF0000l, 16 ) ) ); + + /* This side-steps an issue with right shifting a signed long by 32 + * where it produces an error value. Instead, we right shift in two steps. */ + buffer.put( (byte)Long.rotateRight( + Long.rotateRight( size & 0xFF000000l, 16 ), 8 ) ); buffer.position( 0 ); return buffer; } + public static String toString( ByteBuffer buffer ) + { + StringBuilder sb = new StringBuilder(); + + byte[] bytes = buffer.array(); + + for( byte b: bytes ) + { + sb.append(String.format("%02X ", b)); + sb.append( " " ); + } + + return sb.toString(); + } + /** * Updates the current file name with the rollover counter series suffix */