mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Resolved issue with audio and baseband wave recorder writing incorrect
file size to recording.
This commit is contained in:
@@ -21,7 +21,6 @@ public class FloatBufferTap extends FloatTap implements Listener<RealBuffer>
|
||||
@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<RealBuffer>
|
||||
|
||||
for( float sample: buffer.getSamples() )
|
||||
{
|
||||
mLog.debug("Sending a sample to the tap");
|
||||
super.receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ComplexBuffer> 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 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user