Fix assert when reading or writing with non-power of 2 channels.

This commit is contained in:
Phil Burk 2016-05-18 21:41:01 -07:00
commit 110043f842
4 changed files with 121 additions and 119 deletions

View file

@ -33,13 +33,13 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
@ -51,7 +51,7 @@
/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#define NUM_CHANNELS (2)
#define NUM_CHANNELS (6)
#define NUM_SECONDS (15)
/* #define DITHER_FLAG (paDitherOff) */
#define DITHER_FLAG (0) /**/
@ -109,8 +109,8 @@ int main(void)
char *sampleBlock;
int i;
int numBytes;
printf("patest_read_write_wire.c\n"); fflush(stdout);
numBytes = FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE ;
@ -170,24 +170,6 @@ int main(void)
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
CLEAR( sampleBlock );
/*
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Wire on. Interrupt to stop.\n"); fflush(stdout);
while( 1 )
{
err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_CloseStream( stream );
*/
free( sampleBlock );
Pa_Terminate();

View file

@ -1921,14 +1921,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/*
* If input and output devs are different or we are doing SR conversion,
* we also need a
* ring buffer to store inpt data while waiting for output
* data.
* we also need a ring buffer to store input data while waiting for
* output data.
*/
if( (stream->outputUnit && (stream->inputUnit != stream->outputUnit))
|| stream->inputSRConverter )
{
/* May want the ringSize ot initial position in
/* May want the ringSize or initial position in
ring buffer to depend somewhat on sample rate change */
void *data;
@ -1951,7 +1950,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* now we can initialize the ring buffer */
PaUtil_InitializeRingBuffer( &stream->inputRingBuffer, szfl*inputParameters->channelCount, ringSize, data ) ;
result = PaUtil_InitializeRingBuffer( &stream->inputRingBuffer, szfl*inputParameters->channelCount, ringSize, data );
if( result != 0 )
{
/* The only reason this should fail is if ringSize is not a power of 2, which we do not anticipate happening. */
result = paUnanticipatedHostError;
free(data);
goto error;
}
/* advance the read point a little, so we are reading from the
middle of the buffer */
if( stream->outputUnit )
@ -1973,12 +1980,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->outputFramesPerBuffer,
sampleRate );
result = initializeBlioRingBuffers( &stream->blio,
inputParameters?inputParameters->sampleFormat:0 ,
outputParameters?outputParameters->sampleFormat:0 ,
MAX(stream->inputFramesPerBuffer,stream->outputFramesPerBuffer),
inputParameters ? inputParameters->sampleFormat : 0,
outputParameters ? outputParameters->sampleFormat : 0,
ringSize,
inputParameters?inputChannelCount:0 ,
outputParameters?outputChannelCount:0 ) ;
inputParameters ? inputChannelCount : 0,
outputParameters ? outputChannelCount : 0 ) ;
if( result != paNoError )
goto error;

View file

@ -108,14 +108,16 @@ static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
*
*/
/* This should be called with the relevant info when initializing a stream for
callback. */
/**
* This should be called with the relevant info when initializing a stream for callback.
*
* @param ringBufferSizeInFrames must be a power of 2
*/
PaError initializeBlioRingBuffers(
PaMacBlio *blio,
PaSampleFormat inputSampleFormat,
PaSampleFormat outputSampleFormat,
size_t framesPerBuffer,
long ringBufferSize,
long ringBufferSizeInFrames,
int inChan,
int outChan )
{
@ -126,20 +128,19 @@ PaError initializeBlioRingBuffers(
/* zeroify things */
bzero( blio, sizeof( PaMacBlio ) );
/* this is redundant, but the buffers are used to check
if the bufffers have been initialized, so we do it explicitly. */
if the buffers have been initialized, so we do it explicitly. */
blio->inputRingBuffer.buffer = NULL;
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
blio->ringBufferFrames = ringBufferSize;
blio->ringBufferFrames = ringBufferSizeInFrames;
blio->inputSampleFormat = inputSampleFormat;
blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat); // FIXME: WHY?
blio->outputSampleFormat = outputSampleFormat;
blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
blio->statusFlags = 0;
@ -163,7 +164,7 @@ PaError initializeBlioRingBuffers(
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
data = calloc( ringBufferSizeInFrames, blio->inputSampleSizePow2 * inChan );
if( !data )
{
result = paInsufficientMemory;
@ -172,12 +173,13 @@ PaError initializeBlioRingBuffers(
err = PaUtil_InitializeRingBuffer(
&blio->inputRingBuffer,
1, ringBufferSize*blio->inputSampleSizePow2*inChan,
blio->inputSampleSizePow2 * inChan,
ringBufferSizeInFrames,
data );
assert( !err );
}
if( outChan ) {
data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
data = calloc( ringBufferSizeInFrames, blio->outputSampleSizePow2 * outChan );
if( !data )
{
result = paInsufficientMemory;
@ -186,7 +188,8 @@ PaError initializeBlioRingBuffers(
err = PaUtil_InitializeRingBuffer(
&blio->outputRingBuffer,
1, ringBufferSize*blio->outputSampleSizePow2*outChan,
blio->outputSampleSizePow2 * outChan,
ringBufferSizeInFrames,
data );
assert( !err );
}
@ -266,12 +269,11 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
#endif
blio->statusFlags = 0;
if( blio->outputRingBuffer.buffer ) {
PaUtil_FlushRingBuffer( &blio->outputRingBuffer );
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
//PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
PaUtil_FlushRingBuffer( &blio->outputRingBuffer );
/* Fill the buffer with zeros. */
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize * blio->outputRingBuffer.elementSizeBytes );
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
@ -280,16 +282,14 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
goto error;
#endif
/*
printf( "------%d\n" , blio->framesPerBuffer );
printf( "------%d\n" , blio->outChan );
printf( "------%d\n" , blio->outputSampleSize );
printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize );
*/
}
if( blio->inputRingBuffer.buffer ) {
PaUtil_FlushRingBuffer( &blio->inputRingBuffer );
bzero( blio->inputRingBuffer.buffer,
blio->inputRingBuffer.bufferSize );
blio->inputRingBuffer.bufferSize * blio->inputRingBuffer.elementSizeBytes );
/* Update isInputEmpty. */
#ifdef PA_MAC__BLIO_MUTEX
result = blioSetIsInputEmpty( blio, true );
@ -344,30 +344,32 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
void *userData )
{
PaMacBlio *blio = (PaMacBlio*)userData;
long avail;
long toRead;
long toWrite;
long read;
long written;
ring_buffer_size_t framesAvailable;
ring_buffer_size_t framesToTransfer;
ring_buffer_size_t framesTransferred;
/* set flags returned by OS: */
OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
/* --- Handle Input Buffer --- */
if( blio->inChan ) {
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
if( framesAvailable < frameCount )
{
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
framesToTransfer = framesAvailable;
}
else
{
framesToTransfer = (ring_buffer_size_t)frameCount;
}
toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
/* copy the data */
/* Copy the data from the audio input to the application ring buffer. */
/*printf( "reading %d\n", toRead );*/
read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead );
assert( toRead == read );
framesTransferred = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, framesToTransfer );
assert( framesToTransfer == framesTransferred );
#ifdef PA_MAC__BLIO_MUTEX
/* Priority inversion. See notes below. */
blioSetIsInputEmpty( blio, false );
@ -377,21 +379,31 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
/* --- Handle Output Buffer --- */
if( blio->outChan ) {
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
framesAvailable = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
if( framesAvailable < frameCount )
{
/* zero out the end of the output buffer that we do not have data for */
framesToTransfer = framesAvailable;
toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
size_t bytesPerFrame = blio->outputSampleSizeActual * blio->outChan;
size_t offsetInBytes = framesToTransfer * bytesPerFrame;
size_t countInBytes = (frameCount - framesToTransfer) * bytesPerFrame;
bzero( ((char *)output) + offsetInBytes, countInBytes );
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
framesToTransfer = framesAvailable;
}
else
{
framesToTransfer = (ring_buffer_size_t)frameCount;
}
if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
bzero( ((char *)output)+toWrite,
frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite );
assert( toWrite == written );
framesTransferred = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, framesToTransfer );
assert( framesToTransfer == framesTransferred );
#ifdef PA_MAC__BLIO_MUTEX
/* We have a priority inversion here. However, we will only have to
wait if this was true and is now false, which means we've got
@ -406,24 +418,25 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
PaError ReadStream( PaStream* stream,
void *buffer,
unsigned long frames )
unsigned long framesRequested )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
char *cbuf = (char *) buffer;
PaError ret = paNoError;
VVDBUG(("ReadStream()\n"));
while( frames > 0 ) {
long avail;
long toRead;
while( framesRequested > 0 ) {
ring_buffer_size_t framesAvailable;
ring_buffer_size_t framesToTransfer;
ring_buffer_size_t framesTransferred;
do {
avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
framesAvailable = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
/*
printf( "Read Buffer is %%%g full: %ld of %ld.\n",
100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
avail, blio->inputRingBuffer.bufferSize );
framesAvailable, blio->inputRingBuffer.bufferSize );
*/
if( avail == 0 ) {
if( framesAvailable == 0 ) {
#ifdef PA_MAC_BLIO_MUTEX
/**block when empty*/
ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
@ -441,14 +454,13 @@ PaError ReadStream( PaStream* stream,
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
#endif
}
} while( avail == 0 );
toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
} while( framesAvailable == 0 );
framesToTransfer = MIN( framesAvailable, framesRequested );
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, framesToTransfer );
cbuf += framesToTransfer * blio->inputSampleSizeActual * blio->inChan;
framesRequested -= framesToTransfer;
if( toRead == avail ) {
if( framesToTransfer == framesAvailable ) {
#ifdef PA_MAC_BLIO_MUTEX
/* we just emptied the buffer, so we need to mark it as empty. */
ret = blioSetIsInputEmpty( blio, true );
@ -457,8 +469,10 @@ PaError ReadStream( PaStream* stream,
/* of course, in the meantime, the callback may have put some sats
in, so
so check for that, too, to avoid a race condition. */
/* FIXME - this does not seem to fix any race condition. */
if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) {
blioSetIsInputEmpty( blio, false );
/* FIXME - why check? ret has not been set? */
if( ret )
return ret;
}
@ -468,6 +482,7 @@ PaError ReadStream( PaStream* stream,
/* Report either paNoError or paInputOverflowed. */
/* may also want to report other errors, but this is non-standard. */
/* FIXME should not clobber ret, use if(blio->statusFlags & paInputOverflow) */
ret = blio->statusFlags & paInputOverflow;
/* report underflow only once: */
@ -482,25 +497,27 @@ PaError ReadStream( PaStream* stream,
PaError WriteStream( PaStream* stream,
const void *buffer,
unsigned long frames )
unsigned long framesRequested )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
char *cbuf = (char *) buffer;
PaError ret = paNoError;
VVDBUG(("WriteStream()\n"));
while( frames > 0 ) {
long avail = 0;
long toWrite;
while( framesRequested > 0 ) {
ring_buffer_size_t framesAvailable;
ring_buffer_size_t framesToTransfer;
ring_buffer_size_t framesTransferred;
do {
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
/*
printf( "Write Buffer is %%%g full: %ld of %ld.\n",
100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize,
avail, blio->outputRingBuffer.bufferSize );
framesAvailable, blio->outputRingBuffer.bufferSize );
*/
if( avail == 0 ) {
if( framesAvailable == 0 ) {
#ifdef PA_MAC_BLIO_MUTEX
/*block while full*/
ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
@ -518,16 +535,15 @@ PaError WriteStream( PaStream* stream,
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
#endif
}
} while( avail == 0 );
} while( framesAvailable == 0 );
toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
framesToTransfer = MIN( framesAvailable, framesRequested );
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, framesToTransfer );
cbuf += framesToTransfer * blio->outputSampleSizeActual * blio->outChan;
framesRequested -= framesToTransfer;
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
if( framesToTransfer == framesAvailable ) {
/* we just filled up the buffer, so we need to mark it as filled. */
ret = blioSetIsOutputFull( blio, true );
if( ret )
@ -536,6 +552,7 @@ PaError WriteStream( PaStream* stream,
so check for that, too, to avoid a race condition. */
if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) {
blioSetIsOutputFull( blio, false );
/* FIXME remove or review this code, does not fix race, ret not set! */
if( ret )
return ret;
}
@ -545,6 +562,7 @@ PaError WriteStream( PaStream* stream,
/* Report either paNoError or paOutputUnderflowed. */
/* may also want to report other errors, but this is non-standard. */
/* FIXME should not clobber ret, use if(blio->statusFlags & paInputOverflow) */
ret = blio->statusFlags & paOutputUnderflow;
/* report underflow only once: */
@ -562,11 +580,12 @@ PaError WriteStream( PaStream* stream,
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
{
if( blio->outputRingBuffer.buffer ) {
long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
while( avail != blio->outputRingBuffer.bufferSize ) {
if( avail == 0 )
/* FIXME loop until PaUtil_GetRingBufferReadAvailable==0 */
ring_buffer_size_t framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
while( framesAvailable != blio->outputRingBuffer.bufferSize ) {
if( framesAvailable == 0 )
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
}
}
}
@ -577,8 +596,7 @@ signed long GetStreamReadAvailable( PaStream* stream )
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
/ ( blio->inputSampleSizeActual * blio->inChan );
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
}
@ -587,7 +605,6 @@ signed long GetStreamWriteAvailable( PaStream* stream )
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
/ ( blio->outputSampleSizeActual * blio->outChan );
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
}

View file

@ -64,7 +64,7 @@
#include "pa_mac_core_utilities.h"
/*
* Number of miliseconds to busy wait whil waiting for data in blocking calls.
* Number of milliseconds to busy wait while waiting for data in blocking calls.
*/
#define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5)
/*
@ -79,7 +79,7 @@
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;
size_t ringBufferFrames;
ring_buffer_size_t ringBufferFrames;
PaSampleFormat inputSampleFormat;
size_t inputSampleSizeActual;
size_t inputSampleSizePow2;
@ -87,8 +87,6 @@ typedef struct {
size_t outputSampleSizeActual;
size_t outputSampleSizePow2;
size_t framesPerBuffer;
int inChan;
int outChan;
@ -117,8 +115,7 @@ PaError initializeBlioRingBuffers(
PaMacBlio *blio,
PaSampleFormat inputSampleFormat,
PaSampleFormat outputSampleFormat,
size_t framesPerBuffer,
long ringBufferSize,
long ringBufferSizeInFrames,
int inChan,
int outChan );
PaError destroyBlioRingBuffers( PaMacBlio *blio );