diff options
Diffstat (limited to 'portaudio/src/common/pa_process.c')
-rw-r--r-- | portaudio/src/common/pa_process.c | 1831 |
1 files changed, 1831 insertions, 0 deletions
diff --git a/portaudio/src/common/pa_process.c b/portaudio/src/common/pa_process.c new file mode 100644 index 0000000..084cabb --- /dev/null +++ b/portaudio/src/common/pa_process.c @@ -0,0 +1,1831 @@ +/* + * $Id$ + * Portable Audio I/O Library + * streamCallback <-> host buffer processing adapter + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * 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 + * license above. + */ + +/** @file + @ingroup common_src + + @brief Buffer Processor implementation. +*/ + + +#include <assert.h> +#include <string.h> /* memset() */ + +#include "pa_process.h" +#include "pa_util.h" + + +#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024 + +#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) ) + + +/* greatest common divisor - PGCD in French */ +static unsigned long GCD( unsigned long a, unsigned long b ) +{ + return (b==0) ? a : GCD( b, a%b); +} + +/* least common multiple - PPCM in French */ +static unsigned long LCM( unsigned long a, unsigned long b ) +{ + return (a*b) / GCD(a,b); +} + +#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b)) + +static unsigned long CalculateFrameShift( unsigned long M, unsigned long N ) +{ + unsigned long result = 0; + unsigned long i; + unsigned long lcm; + + assert( M > 0 ); + assert( N > 0 ); + + lcm = LCM( M, N ); + for( i = M; i < lcm; i += M ) + result = PA_MAX_( result, i % N ); + + return result; +} + + +PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, + int inputChannelCount, PaSampleFormat userInputSampleFormat, + PaSampleFormat hostInputSampleFormat, + int outputChannelCount, PaSampleFormat userOutputSampleFormat, + PaSampleFormat hostOutputSampleFormat, + double sampleRate, + PaStreamFlags streamFlags, + unsigned long framesPerUserBuffer, + unsigned long framesPerHostBuffer, + PaUtilHostBufferSizeMode hostBufferSizeMode, + PaStreamCallback *streamCallback, void *userData ) +{ + PaError result = paNoError; + PaError bytesPerSample; + unsigned long tempInputBufferSize, tempOutputBufferSize; + PaStreamFlags tempInputStreamFlags; + + if( streamFlags & paNeverDropInput ) + { + /* paNeverDropInput is only valid for full-duplex callback streams, with an unspecified number of frames per buffer. */ + if( !streamCallback || !(inputChannelCount > 0 && outputChannelCount > 0) || + framesPerUserBuffer != paFramesPerBufferUnspecified ) + return paInvalidFlag; + } + + /* initialize buffer ptrs to zero so they can be freed if necessary in error */ + bp->tempInputBuffer = 0; + bp->tempInputBufferPtrs = 0; + bp->tempOutputBuffer = 0; + bp->tempOutputBufferPtrs = 0; + + bp->framesPerUserBuffer = framesPerUserBuffer; + bp->framesPerHostBuffer = framesPerHostBuffer; + + bp->inputChannelCount = inputChannelCount; + bp->outputChannelCount = outputChannelCount; + + bp->hostBufferSizeMode = hostBufferSizeMode; + + bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0; + bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0; + + if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */ + { + bp->useNonAdaptingProcess = 1; + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + || hostBufferSizeMode == paUtilBoundedHostBufferSize ) + { + bp->framesPerTempBuffer = framesPerHostBuffer; + } + else /* unknown host buffer size */ + { + bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_; + } + } + else + { + bp->framesPerTempBuffer = framesPerUserBuffer; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + && framesPerHostBuffer % framesPerUserBuffer == 0 ) + { + bp->useNonAdaptingProcess = 1; + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + } + else + { + bp->useNonAdaptingProcess = 0; + + if( inputChannelCount > 0 && outputChannelCount > 0 ) + { + /* full duplex */ + if( hostBufferSizeMode == paUtilFixedHostBufferSize ) + { + unsigned long frameShift = + CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer ); + + if( framesPerUserBuffer > framesPerHostBuffer ) + { + bp->initialFramesInTempInputBuffer = frameShift; + bp->initialFramesInTempOutputBuffer = 0; + } + else + { + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = frameShift; + } + } + else /* variable host buffer size, add framesPerUserBuffer latency */ + { + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = framesPerUserBuffer; + } + } + else + { + /* half duplex */ + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + } + } + } + + + bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; + bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; + + + if( inputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + /* Under the assumption that no ADC in existence delivers better than 24bits resolution, + we disable dithering when host input format is paInt32 and user format is paInt24, + since the host samples will just be padded with zeros anyway. */ + + tempInputStreamFlags = streamFlags; + if( !(tempInputStreamFlags & paDitherOff) /* dither is on */ + && (hostInputSampleFormat & paInt32) /* host input format is int32 */ + && (userInputSampleFormat & paInt24) /* user requested format is int24 */ ){ + + tempInputStreamFlags = tempInputStreamFlags | paDitherOff; + } + + bp->inputConverter = + PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, tempInputStreamFlags ); + + bp->inputZeroer = PaUtil_SelectZeroer( userInputSampleFormat ); + + bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1; + + bp->hostInputIsInterleaved = (hostInputSampleFormat & paNonInterleaved)?0:1; + + bp->userInputSampleFormatIsEqualToHost = ((userInputSampleFormat & ~paNonInterleaved) == (hostInputSampleFormat & ~paNonInterleaved)); + + tempInputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount; + + bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize ); + if( bp->tempInputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempInputBuffer > 0 ) + memset( bp->tempInputBuffer, 0, tempInputBufferSize ); + + if( userInputSampleFormat & paNonInterleaved ) + { + bp->tempInputBufferPtrs = + (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount ); + if( bp->tempInputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostInputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2); + if( bp->hostInputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount]; + } + + if( outputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bp->outputConverter = + PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags ); + + bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat ); + + bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1; + + bp->hostOutputIsInterleaved = (hostOutputSampleFormat & paNonInterleaved)?0:1; + + bp->userOutputSampleFormatIsEqualToHost = ((userOutputSampleFormat & ~paNonInterleaved) == (hostOutputSampleFormat & ~paNonInterleaved)); + + tempOutputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount; + + bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize ); + if( bp->tempOutputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempOutputBuffer > 0 ) + memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); + + if( userOutputSampleFormat & paNonInterleaved ) + { + bp->tempOutputBufferPtrs = + (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount ); + if( bp->tempOutputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 ); + if( bp->hostOutputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount]; + } + + PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator ); + + bp->samplePeriod = 1. / sampleRate; + + bp->streamCallback = streamCallback; + bp->userData = userData; + + return result; + +error: + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); + + return result; +} + + +void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp ) +{ + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); +} + + +void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp ) +{ + unsigned long tempInputBufferSize, tempOutputBufferSize; + + bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; + bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; + + if( bp->framesInTempInputBuffer > 0 ) + { + tempInputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount; + memset( bp->tempInputBuffer, 0, tempInputBufferSize ); + } + + if( bp->framesInTempOutputBuffer > 0 ) + { + tempOutputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount; + memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); + } +} + + +unsigned long PaUtil_GetBufferProcessorInputLatencyFrames( PaUtilBufferProcessor* bp ) +{ + return bp->initialFramesInTempInputBuffer; +} + + +unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bp ) +{ + return bp->initialFramesInTempOutputBuffer; +} + + +void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostInputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostInputFrameCount[0] = frameCount; +} + + +void PaUtil_SetNoInput( PaUtilBufferProcessor* bp ) +{ + assert( bp->inputChannelCount > 0 ); + + bp->hostInputChannels[0][0].data = 0; +} + + +void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + assert( bp->hostInputIsInterleaved ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[0][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[0][channel+i].stride = channelCount; + } +} + + +void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + assert( !bp->hostInputIsInterleaved ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = 1; +} + + +void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostInputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + assert( bp->hostInputIsInterleaved ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[1][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[1][channel+i].stride = channelCount; + } +} + + +void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + assert( !bp->hostInputIsInterleaved ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = 1; +} + + +void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostOutputFrameCount[0] = frameCount; +} + + +void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp ) +{ + assert( bp->outputChannelCount > 0 ); + + bp->hostOutputChannels[0][0].data = 0; + + /* note that only NonAdaptingProcess is able to deal with no output at this stage. not implemented for AdaptingProcess */ +} + + +void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + assert( data != NULL ); + + bp->hostOutputChannels[0][channel].data = data; + bp->hostOutputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + assert( bp->hostOutputIsInterleaved ); + + for( i=0; i< channelCount; ++i ) + { + PaUtil_SetOutputChannel( bp, channel + i, p, channelCount ); + p += bp->bytesPerHostOutputSample; + } +} + + +void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + assert( !bp->hostOutputIsInterleaved ); + + PaUtil_SetOutputChannel( bp, channel, data, 1 ); +} + + +void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostOutputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + assert( data != NULL ); + + bp->hostOutputChannels[1][channel].data = data; + bp->hostOutputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + assert( bp->hostOutputIsInterleaved ); + + for( i=0; i< channelCount; ++i ) + { + PaUtil_Set2ndOutputChannel( bp, channel + i, p, channelCount ); + p += bp->bytesPerHostOutputSample; + } +} + + +void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + assert( !bp->hostOutputIsInterleaved ); + + PaUtil_Set2ndOutputChannel( bp, channel, data, 1 ); +} + + +void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp, + PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ) +{ + bp->timeInfo = timeInfo; + + /* the first streamCallback will be called to process samples which are + currently in the input buffer before the ones starting at the timeInfo time */ + + bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod; + + /* We just pass through timeInfo->currentTime provided by the caller. This is + not strictly conformant to the word of the spec, since the buffer processor + might call the callback multiple times, and we never refresh currentTime. */ + + /* the first streamCallback will be called to generate samples which will be + outputted after the frames currently in the output buffer have been + outputted. */ + bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod; + + bp->callbackStatusFlags = callbackStatusFlags; + + bp->hostInputFrameCount[1] = 0; + bp->hostOutputFrameCount[1] = 0; +} + + +/* + NonAdaptingProcess() is a simple buffer copying adaptor that can handle + both full and half duplex copies. It processes framesToProcess frames, + broken into blocks bp->framesPerTempBuffer long. + This routine can be used when the streamCallback doesn't care what length + the buffers are, or when framesToProcess is an integer multiple of + bp->framesPerTempBuffer, in which case streamCallback will always be called + with bp->framesPerTempBuffer samples. +*/ +static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr, *destBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + int skipOutputConvert = 0; + int skipInputConvert = 0; + + + if( *streamCallbackResult == paContinue ) + { + do + { + frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo ); + + /* configure user input buffer and convert input data (host -> user) */ + if( bp->inputChannelCount == 0 ) + { + /* no input */ + userInput = 0; + } + else /* there are input channels */ + { + + destBytePtr = (unsigned char *)bp->tempInputBuffer; + + if( bp->userInputIsInterleaved ) + { + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + + /* process host buffer directly, or use temp buffer if formats differ or host buffer non-interleaved, + * or if num channels differs between the host (set in stride) and the user (eg with some Alsa hw:) */ + if( bp->userInputSampleFormatIsEqualToHost && bp->hostInputIsInterleaved + && bp->hostInputChannels[0][0].data && bp->inputChannelCount == hostInputChannels[0].stride ) + { + userInput = hostInputChannels[0].data; + destBytePtr = (unsigned char *)hostInputChannels[0].data; + skipInputConvert = 1; + } + else + { + userInput = bp->tempInputBuffer; + } + } + else /* user input is not interleaved */ + { + destSampleStrideSamples = 1; + destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + if( bp->userInputSampleFormatIsEqualToHost && !bp->hostInputIsInterleaved && bp->hostInputChannels[0][0].data ) + { + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = hostInputChannels[i].data; + } + skipInputConvert = 1; + } + else + { + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * frameCount; + } + } + + userInput = bp->tempInputBufferPtrs; + } + + if( !bp->hostInputChannels[0][0].data ) + { + /* no input was supplied (see PaUtil_SetNoInput), so + zero the input buffer */ + + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount ); + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + } + } + else + { + if( skipInputConvert ) + { + for( i=0; i<bp->inputChannelCount; ++i ) + { + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + else + { + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + } + } + + /* configure user output buffer */ + if( bp->outputChannelCount == 0 ) + { + /* no output */ + userOutput = 0; + } + else /* there are output channels */ + { + if( bp->userOutputIsInterleaved ) + { + /* process host buffer directly, or use temp buffer if formats differ or host buffer non-interleaved, + * or if num channels differs between the host (set in stride) and the user (eg with some Alsa hw:) */ + if( bp->userOutputSampleFormatIsEqualToHost && bp->hostOutputIsInterleaved + && bp->outputChannelCount == hostOutputChannels[0].stride ) + { + userOutput = hostOutputChannels[0].data; + skipOutputConvert = 1; + } + else + { + userOutput = bp->tempOutputBuffer; + } + } + else /* user output is not interleaved */ + { + if( bp->userOutputSampleFormatIsEqualToHost && !bp->hostOutputIsInterleaved ) + { + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = hostOutputChannels[i].data; + } + skipOutputConvert = 1; + } + else + { + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->bytesPerUserOutputSample * frameCount; + } + } + + userOutput = bp->tempOutputBufferPtrs; + } + } + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData ); + + if( *streamCallbackResult == paAbort ) + { + /* callback returned paAbort, don't advance framesProcessed + and framesToGo, they will be handled below */ + } + else + { + bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod; + + /* convert output data (user -> host) */ + + if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) + { + if( skipOutputConvert ) + { + for( i=0; i<bp->outputChannelCount; ++i ) + { + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + else + { + + srcBytePtr = (unsigned char *)bp->tempOutputBuffer; + + if( bp->userOutputIsInterleaved ) + { + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcSampleStrideSamples = 1; + srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample; + } + + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + } + } + while( framesToGo > 0 && *streamCallbackResult == paContinue ); + } + + if( framesToGo > 0 ) + { + /* zero any remaining frames output. There will only be remaining frames + if the callback has returned paComplete or paAbort */ + + frameCount = framesToGo; + + if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) + { + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + } + + return framesProcessed; +} + + +/* + AdaptingInputOnlyProcess() is a half duplex input buffer processor. It + converts data from the input buffers into the temporary input buffer, + when the temporary input buffer is full, it calls the streamCallback. +*/ +static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *destBytePtr; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + + userOutput = 0; + + do + { + frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer ) + ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer ) + : framesToGo; + + /* convert frameCount samples into temp buffer */ + + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destSampleStrideSamples = 1; + destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; + } + + userInput = bp->tempInputBufferPtrs; + } + + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + bp->framesInTempInputBuffer += frameCount; + + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer ) + { + /** + @todo (non-critical optimisation) + The conditional below implements the continue/complete/abort mechanism + simply by continuing on iterating through the input buffer, but not + passing the data to the callback. With care, the outer loop could be + terminated earlier, thus some unneeded conversion cycles would be + saved. + */ + if( *streamCallbackResult == paContinue ) + { + bp->timeInfo->outputBufferDacTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; + } + + bp->framesInTempInputBuffer = 0; + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + }while( framesToGo > 0 ); + + return framesProcessed; +} + + +/* + AdaptingOutputOnlyProcess() is a half duplex output buffer processor. + It converts data from the temporary output buffer, to the output buffers, + when the temporary output buffer is empty, it calls the streamCallback. +*/ +static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + + do + { + if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue ) + { + userInput = 0; + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + bp->timeInfo->inputBufferAdcTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + if( *streamCallbackResult == paAbort ) + { + /* if the callback returned paAbort, we disregard its output */ + } + else + { + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + } + + if( bp->framesInTempOutputBuffer > 0 ) + { + /* convert frameCount frames from user buffer to host buffer */ + + frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo ); + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = 1; + srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + bp->framesInTempOutputBuffer -= frameCount; + } + else + { + /* no more user data is available because the callback has returned + paComplete or paAbort. Fill the remainder of the host buffer + with zeros. + */ + + frameCount = framesToGo; + + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + + }while( framesToGo > 0 ); + + return framesProcessed; +} + +/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from + tempOutputBuffer to hostOutputChannels. This includes data conversion + and interleaving. +*/ +static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp) +{ + unsigned long maxFramesToCopy; + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int frameCount; + unsigned char *srcBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + /* copy frames from user to host output buffers */ + while( bp->framesInTempOutputBuffer > 0 && + ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesInTempOutputBuffer; + + /* select the output buffer set (1st or 2nd) */ + if( bp->hostOutputFrameCount[0] > 0 ) + { + hostOutputChannels = bp->hostOutputChannels[0]; + frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy ); + } + else + { + hostOutputChannels = bp->hostOutputChannels[1]; + frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy ); + } + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = 1; + srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; i<bp->outputChannelCount; ++i ) + { + assert( hostOutputChannels[i].data != NULL ); + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + if( bp->hostOutputFrameCount[0] > 0 ) + bp->hostOutputFrameCount[0] -= frameCount; + else + bp->hostOutputFrameCount[1] -= frameCount; + + bp->framesInTempOutputBuffer -= frameCount; + } +} + +/* + AdaptingProcess is a full duplex adapting buffer processor. It converts + data from the temporary output buffer into the host output buffers, then + from the host input buffers into the temporary input buffers. Calling the + streamCallback when necessary. + When processPartialUserBuffers is 0, all available input data will be + consumed and all available output space will be filled. When + processPartialUserBuffers is non-zero, as many full user buffers + as possible will be processed, but partial buffers will not be consumed. +*/ +static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, int processPartialUserBuffers ) +{ + void *userInput, *userOutput; + unsigned long framesProcessed = 0; + unsigned long framesAvailable; + unsigned long endProcessingMinFrameCount; + unsigned long maxFramesToCopy; + PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels; + unsigned int frameCount; + unsigned char *destBytePtr; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i, j; + + + framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */ + + if( processPartialUserBuffers ) + endProcessingMinFrameCount = 0; + else + endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1); + + /* Fill host output with remaining frames in user output (tempOutputBuffer) */ + CopyTempOutputBuffersToHostOutputBuffers( bp ); + + while( framesAvailable > endProcessingMinFrameCount ) + { + + if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue ) + { + /* the callback will not be called any more, so zero what remains + of the host output buffers */ + + for( i=0; i<2; ++i ) + { + frameCount = bp->hostOutputFrameCount[i]; + if( frameCount > 0 ) + { + hostOutputChannels = bp->hostOutputChannels[i]; + + for( j=0; j<bp->outputChannelCount; ++j ) + { + bp->outputZeroer( hostOutputChannels[j].data, + hostOutputChannels[j].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) + + frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample; + } + bp->hostOutputFrameCount[i] = 0; + } + } + } + + + /* copy frames from host to user input buffers */ + while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer && + ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer; + + /* select the input buffer set (1st or 2nd) */ + if( bp->hostInputFrameCount[0] > 0 ) + { + hostInputChannels = bp->hostInputChannels[0]; + frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy ); + } + else + { + hostInputChannels = bp->hostInputChannels[1]; + frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy ); + } + + /* configure conversion destination pointers */ + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destSampleStrideSamples = 1; + destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + if( bp->hostInputFrameCount[0] > 0 ) + bp->hostInputFrameCount[0] -= frameCount; + else + bp->hostInputFrameCount[1] -= frameCount; + + bp->framesInTempInputBuffer += frameCount; + + /* update framesAvailable and framesProcessed based on input consumed + unless something is very wrong this will also correspond to the + amount of output generated */ + framesAvailable -= frameCount; + framesProcessed += frameCount; + } + + /* call streamCallback */ + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer && + bp->framesInTempOutputBuffer == 0 ) + { + if( *streamCallbackResult == paContinue ) + { + /* setup userInput */ + if( bp->userInputIsInterleaved ) + { + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + for( i = 0; i < bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + userInput = bp->tempInputBufferPtrs; + } + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + /* call streamCallback */ + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + bp->framesInTempInputBuffer = 0; + + if( *streamCallbackResult == paAbort ) + bp->framesInTempOutputBuffer = 0; + else + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + else + { + /* paComplete or paAbort has already been called. */ + + bp->framesInTempInputBuffer = 0; + } + } + + /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) + Means to process the user output provided by the callback. Has to be called after + each callback. */ + CopyTempOutputBuffersToHostOutputBuffers( bp ); + + } + + return framesProcessed; +} + + +unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult ) +{ + unsigned long framesToProcess, framesToGo; + unsigned long framesProcessed = 0; + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 + && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */ + && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ ) + { + assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) == + (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) ); + } + + assert( *streamCallbackResult == paContinue + || *streamCallbackResult == paComplete + || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */ + + if( bp->useNonAdaptingProcess ) + { + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex non-adapting process, splice buffers if they are + different lengths */ + + framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */ + + do{ + unsigned long noInputInputFrameCount; + unsigned long *hostInputFrameCount; + PaUtilChannelDescriptor *hostInputChannels; + unsigned long noOutputOutputFrameCount; + unsigned long *hostOutputFrameCount; + PaUtilChannelDescriptor *hostOutputChannels; + unsigned long framesProcessedThisIteration; + + if( !bp->hostInputChannels[0][0].data ) + { + /* no input was supplied (see PaUtil_SetNoInput) + NonAdaptingProcess knows how to deal with this + */ + noInputInputFrameCount = framesToGo; + hostInputFrameCount = &noInputInputFrameCount; + hostInputChannels = 0; + } + else if( bp->hostInputFrameCount[0] != 0 ) + { + hostInputFrameCount = &bp->hostInputFrameCount[0]; + hostInputChannels = bp->hostInputChannels[0]; + } + else + { + hostInputFrameCount = &bp->hostInputFrameCount[1]; + hostInputChannels = bp->hostInputChannels[1]; + } + + if( !bp->hostOutputChannels[0][0].data ) + { + /* no output was supplied (see PaUtil_SetNoOutput) + NonAdaptingProcess knows how to deal with this + */ + noOutputOutputFrameCount = framesToGo; + hostOutputFrameCount = &noOutputOutputFrameCount; + hostOutputChannels = 0; + } + if( bp->hostOutputFrameCount[0] != 0 ) + { + hostOutputFrameCount = &bp->hostOutputFrameCount[0]; + hostOutputChannels = bp->hostOutputChannels[0]; + } + else + { + hostOutputFrameCount = &bp->hostOutputFrameCount[1]; + hostOutputChannels = bp->hostOutputChannels[1]; + } + + framesToProcess = PA_MIN_( *hostInputFrameCount, + *hostOutputFrameCount ); + + assert( framesToProcess != 0 ); + + framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult, + hostInputChannels, hostOutputChannels, + framesToProcess ); + + *hostInputFrameCount -= framesProcessedThisIteration; + *hostOutputFrameCount -= framesProcessedThisIteration; + + framesProcessed += framesProcessedThisIteration; + framesToGo -= framesProcessedThisIteration; + + }while( framesToGo > 0 ); + } + else + { + /* half duplex non-adapting process, just process 1st and 2nd buffer */ + /* process first buffer */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[0] + : bp->hostOutputFrameCount[0]; + + framesProcessed = NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], bp->hostOutputChannels[0], + framesToProcess ); + + /* process second buffer if provided */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[1] + : bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], bp->hostOutputChannels[1], + framesToProcess ); + } + } + } + else /* block adaption necessary*/ + { + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex */ + + if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed ) + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 0 /* dont process partial user buffers */ ); + } + else + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 1 /* process partial user buffers */ ); + } + } + else if( bp->inputChannelCount != 0 ) + { + /* input only */ + framesToProcess = bp->hostInputFrameCount[0]; + + framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], framesToProcess ); + + framesToProcess = bp->hostInputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], framesToProcess ); + } + } + else + { + /* output only */ + framesToProcess = bp->hostOutputFrameCount[0]; + + framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[0], framesToProcess ); + + framesToProcess = bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[1], framesToProcess ); + } + } + } + + return framesProcessed; +} + + +int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp ) +{ + return (bp->framesInTempOutputBuffer) ? 0 : 1; +} + + +unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp, + void **buffer, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostInputChannels; + unsigned int framesToCopy; + unsigned char *destBytePtr; + void **nonInterleavedDestPtrs; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + hostInputChannels = bp->hostInputChannels[0]; + framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount ); + + if( bp->userInputIsInterleaved ) + { + destBytePtr = (unsigned char*)*buffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + + for( i=0; i<bp->inputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + framesToCopy, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next dest channel */ + + /* advance source ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + /* advance callers dest pointer (buffer) */ + *buffer = ((unsigned char *)*buffer) + + framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample; + } + else + { + /* user input is not interleaved */ + + nonInterleavedDestPtrs = (void**)*buffer; + + destSampleStrideSamples = 1; + + for( i=0; i<bp->inputChannelCount; ++i ) + { + destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i]; + + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + framesToCopy, &bp->ditherGenerator ); + + /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */ + destBytePtr += bp->bytesPerUserInputSample * framesToCopy; + nonInterleavedDestPtrs[i] = destBytePtr; + + /* advance source ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + + bp->hostInputFrameCount[0] -= framesToCopy; + + return framesToCopy; +} + +unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp, + const void ** buffer, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int framesToCopy; + unsigned char *srcBytePtr; + void **nonInterleavedSrcPtrs; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + hostOutputChannels = bp->hostOutputChannels[0]; + framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = (unsigned char*)*buffer; + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + framesToCopy, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + /* advance callers source pointer (buffer) */ + *buffer = ((unsigned char *)*buffer) + + framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample; + + } + else + { + /* user output is not interleaved */ + + nonInterleavedSrcPtrs = (void**)*buffer; + + srcSampleStrideSamples = 1; + + for( i=0; i<bp->outputChannelCount; ++i ) + { + srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i]; + + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + framesToCopy, &bp->ditherGenerator ); + + + /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */ + srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy; + nonInterleavedSrcPtrs[i] = srcBytePtr; + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + bp->hostOutputFrameCount[0] += framesToCopy; + + return framesToCopy; +} + + +unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int framesToZero; + unsigned int i; + + hostOutputChannels = bp->hostOutputChannels[0]; + framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); + + for( i=0; i<bp->outputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + framesToZero ); + + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + bp->hostOutputFrameCount[0] += framesToZero; + + return framesToZero; +} |