Spaces:
Runtime error
Runtime error
| /* | |
| * $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. | |
| */ | |
| /* 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); | |
| } | |
| 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; | |
| } | |