summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-03-20 12:55:09 +0000
committerBenny Prijono <bennylp@teluu.com>2006-03-20 12:55:09 +0000
commitf13e8318d321d46214e64aba246747a9126a1402 (patch)
tree73a4c97047443ad426020614d89a64f14bb1ac95 /pjmedia
parent4c8c2a65ead2089c30c431ef34c31d4e0c7b9cb3 (diff)
Added MacOS related files to PortAudio
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@339 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_lib.c827
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core.c896
-rw-r--r--pjmedia/src/pjmedia/portaudio/ringbuffer.c199
-rw-r--r--pjmedia/src/pjmedia/portaudio/ringbuffer.h101
4 files changed, 2023 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/portaudio/pa_lib.c b/pjmedia/src/pjmedia/portaudio/pa_lib.c
new file mode 100644
index 00000000..86601592
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_lib.c
@@ -0,0 +1,827 @@
+/*
+ * Portable Audio I/O Library
+ * Host Independant Layer
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/* Modification History:
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifdef _WIN32
+#ifndef __MWERKS__
+#include <memory.h>
+#endif /* __MWERKS__ */
+#else /* !_WIN32 */
+#include <memory.h>
+#endif /* _WIN32 */
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/* The reason we might NOT want to validate the rate before opening the stream
+ * is because many DirectSound drivers lie about the rates they actually support.
+ */
+#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
+
+/*
+O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
+*/
+
+#ifndef FALSE
+ #define FALSE (0)
+ #define TRUE (!FALSE)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
+
+static PaError Pa_KillStream( PortAudioStream *stream, int abort );
+
+/***********************************************************************/
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
+{
+ double err, minErr = allowableError;
+ int i, bestFit = -1;
+
+ for( i=0; i<numRates; i++ )
+ {
+ err = fabs( frameRate - rateTable[i] );
+ if( err < minErr )
+ {
+ minErr = err;
+ bestFit = i;
+ }
+ }
+ return bestFit;
+}
+
+/**************************************************************************
+** Make sure sample rate is legal and also convert to enumeration for driver.
+*/
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr )
+{
+ long bestRateIndex;
+ const PaDeviceInfo *pdi;
+ pdi = Pa_GetDeviceInfo( id );
+ if( pdi == NULL ) return paInvalidDeviceId;
+
+ if( pdi->numSampleRates == -1 )
+ {
+ /* Is it out of range? */
+ if( (requestedFrameRate < pdi->sampleRates[0]) ||
+ (requestedFrameRate > pdi->sampleRates[1]) )
+ {
+ return paInvalidSampleRate;
+ }
+
+ *closestFrameRatePtr = requestedFrameRate;
+ }
+ else
+ {
+ bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
+ if( bestRateIndex < 0 ) return paInvalidSampleRate;
+ *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
+ }
+ return paNoError;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_OpenStream(
+ PortAudioStream** streamPtrPtr,
+ PaDeviceID inputDeviceID,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDeviceID,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ unsigned long streamFlags,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ internalPortAudioStream *past = NULL;
+ PaError result = paNoError;
+ int bitsPerInputSample;
+ int bitsPerOutputSample;
+ /* Print passed parameters. */
+ DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
+ streamPtrPtr, inputDeviceID, numInputChannels,
+ inputSampleFormat, inputDriverInfo ));
+ DBUG((" %d, %d, %d, %p, /* output */\n",
+ outputDeviceID, numOutputChannels,
+ outputSampleFormat, outputDriverInfo ));
+ DBUG((" %g, %d, %d, 0x%x, , %p )\n",
+ sampleRate, framesPerBuffer, numberOfBuffers,
+ streamFlags, userData ));
+
+ /* Check for parameter errors. */
+ if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
+ if( streamPtrPtr == NULL ) return paBadStreamPtr;
+ if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
+ if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId;
+ if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
+
+#if SUPPORT_AUDIO_CAPTURE
+ if( inputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( inputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerInputSample = 8 * size;
+ if( (numInputChannels <= 0) ) return paInvalidChannelCount;
+ }
+#else
+ if( inputDeviceID >= 0 )
+ {
+ return paInvalidChannelCount;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ else
+ {
+ if( numInputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerInputSample = 0;
+ }
+
+ if( outputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( outputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerOutputSample = 8 * size;
+ if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
+ }
+ else
+ {
+ if( numOutputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerOutputSample = 0;
+ }
+
+ if( callback == NULL ) return paNullCallback;
+
+ /* Allocate and clear stream structure. */
+ past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
+ if( past == NULL ) return paInsufficientMemory;
+ memset( past, 0, sizeof(internalPortAudioStream) );
+ AddTraceMessage("Pa_OpenStream: past", (long) past );
+
+ past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
+ past->past_FramesPerUserBuffer = framesPerBuffer;
+ past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
+ past->past_Callback = callback;
+ past->past_UserData = userData;
+ past->past_OutputSampleFormat = outputSampleFormat;
+ past->past_InputSampleFormat = inputSampleFormat;
+ past->past_OutputDeviceID = outputDeviceID;
+ past->past_InputDeviceID = inputDeviceID;
+ past->past_NumInputChannels = numInputChannels;
+ past->past_NumOutputChannels = numOutputChannels;
+ past->past_Flags = streamFlags;
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ {
+ result = paInvalidSampleRate;
+ goto cleanup;
+ }
+
+ /* Allocate buffers that may be used for format conversion from user to native buffers. */
+ if( numInputChannels > 0 )
+ {
+
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ /* Allocate single Input buffer. */
+ past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
+ past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
+ if( past->past_InputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_InputBuffer = NULL;
+ }
+
+ /* Allocate single Output buffer. */
+ if( numOutputChannels > 0 )
+ {
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
+ past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
+ if( past->past_OutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_OutputBuffer = NULL;
+ }
+
+ result = PaHost_OpenStream( past );
+ if( result < 0 ) goto cleanup;
+
+ *streamPtrPtr = (void *) past;
+
+ return result;
+
+cleanup:
+ if( past != NULL ) Pa_CloseStream( past );
+ *streamPtrPtr = NULL;
+ return result;
+}
+
+
+/*************************************************************************/
+DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ return Pa_OpenStream(
+ stream,
+ ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ numInputChannels, sampleFormat, NULL,
+ ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ numOutputChannels, sampleFormat, NULL,
+ sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_CloseStream( PortAudioStream* stream)
+{
+ PaError result;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_CloseStream()\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ Pa_AbortStream( past );
+ result = PaHost_CloseStream( past );
+
+ if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
+ if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
+ PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
+
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StartStream( PortAudioStream *stream )
+{
+ PaError result = paHostError;
+ internalPortAudioStream *past;
+
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ past->past_FrameCount = 0.0;
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StartInput( past );
+ DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StartOutput( past );
+ DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ result = PaHost_StartEngine( past );
+ DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+
+ return paNoError;
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StopStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 0 );
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_AbortStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 1 );
+}
+
+/*************************************************************************/
+static PaError Pa_KillStream( PortAudioStream *stream, int abort )
+{
+ PaError result = paNoError;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_StopStream().\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_StopEngine( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StopInput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StopOutput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+error:
+ past->past_Usage = 0;
+ past->past_IfLastExitValid = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StreamActive( PortAudioStream *stream )
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return PaHost_StreamActive( past );
+}
+
+/*************************************************************************/
+DLL_API const char *Pa_GetErrorText( PaError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case paNoError: msg = "Success"; break;
+ case paHostError: msg = "Host error."; break;
+ case paInvalidChannelCount: msg = "Invalid number of channels."; break;
+ case paInvalidSampleRate: msg = "Invalid sample rate."; break;
+ case paInvalidDeviceId: msg = "Invalid device ID."; break;
+ case paInvalidFlag: msg = "Invalid flag."; break;
+ case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
+ case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
+ case paInsufficientMemory: msg = "Insufficient memory."; break;
+ case paBufferTooBig: msg = "Buffer too big."; break;
+ case paBufferTooSmall: msg = "Buffer too small."; break;
+ case paNullCallback: msg = "No callback routine specified."; break;
+ case paBadStreamPtr: msg = "Invalid stream pointer."; break;
+ case paTimedOut : msg = "Wait Timed Out."; break;
+ case paInternalError: msg = "Internal PortAudio Error."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+/*
+ Get CPU Load as a fraction of total CPU time.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ The amount may vary depending on CPU load.
+ This function may be called from the callback function.
+*/
+DLL_API double Pa_GetCPULoad( PortAudioStream* stream)
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return (double) paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return past->past_Usage;
+}
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+static long Pa_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+ /* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+ /* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Int16, call user code, then convert output
+** to Int16 format for native use.
+** Assumes host native format is paInt16.
+** Returns result from user callback.
+*/
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer )
+{
+ long temp;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ unsigned int i;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+#if SUPPORT_AUDIO_CAPTURE
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ /* Convert from native format to PA format. */
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ switch(past->past_InputSampleFormat)
+ {
+
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ /* Convert 16 bit data to 32 bit integers */
+ int *inBufPtr = (int *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] << 16;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ /* Already in correct format so don't copy. */
+ inputBuffer = nativeInputBuffer;
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+ char *inBufPtr = (char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 7;
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (char)(temp >> 8);
+ }
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+ unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 7;
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (unsigned char)(temp + 0x80);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */
+ if( outputBuffer != NULL )
+ {
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ switch(past->past_OutputSampleFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ if( past->past_Flags & paClipOff ) /* NOTHING */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
+ }
+ }
+ else /* CLIP */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = (long)(outBufPtr[i] * 32767.0f);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ float dithered = (outBufPtr[i] * (32767.0f)) + dither;
+ temp = (long) (dithered);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ int *outBufPtr = (int *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ /* Shift one bit down before dithering so that we have room for overflow from add. */
+ temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ }
+
+ return userResult;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Float32, call user code, then convert output
+** to Float32 format for native use.
+** Assumes host native format is Float32.
+** Returns result from user callback.
+** FIXME - Unimplemented for formats other than paFloat32!!!!
+*/
+long Pa_CallConvertFloat32( internalPortAudioStream *past,
+ float *nativeInputBuffer,
+ float *nativeOutputBuffer )
+{
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ inputBuffer = nativeInputBuffer; // FIXME
+ }
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */ // FIXME
+ return userResult;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_Initialize( void )
+{
+ if( gInitCount++ > 0 ) return paNoError;
+ ResetTraceMessages();
+ return PaHost_Init();
+}
+
+DLL_API PaError Pa_Terminate( void )
+{
+ PaError result = paNoError;
+
+ if( gInitCount == 0 ) return paNoError;
+ else if( --gInitCount == 0 )
+ {
+ result = PaHost_Term();
+ DumpTraceMessages();
+ }
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int size;
+ switch(format )
+ {
+
+ case paUInt8:
+ case paInt8:
+ size = 1;
+ break;
+
+ case paInt16:
+ size = 2;
+ break;
+
+ case paPackedInt24:
+ size = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ case paInt24:
+ size = 4;
+ break;
+
+ default:
+ size = paSampleFormatNotSupported;
+ break;
+ }
+ return (PaError) size;
+}
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core.c b/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
new file mode 100644
index 00000000..020b5aeb
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
@@ -0,0 +1,896 @@
+/*
+ * $Id$
+ * pa_mac_core.c
+ * Implementation of PortAudio for Mac OS X CoreAudio
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+
+#include "portaudio.h"
+#include "pa_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+// ===== constants =====
+
+// ===== structs =====
+#pragma mark structs
+
+// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation
+typedef struct PaMacCore_HAR
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+ AudioDeviceID *macCoreDeviceIds;
+}
+PaMacCoreHostApiRepresentation;
+
+typedef struct PaMacCore_DI
+{
+ PaDeviceInfo inheritedDeviceInfo;
+}
+PaMacCoreDeviceInfo;
+
+// PaMacCoreStream - a stream data structure specifically for this implementation
+typedef struct PaMacCore_S
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ int primeStreamUsingCallback;
+
+ AudioDeviceID inputDevice;
+ AudioDeviceID outputDevice;
+
+ // Processing thread management --------------
+// HANDLE abortEvent;
+// HANDLE processingThread;
+// DWORD processingThreadId;
+
+ char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle
+ int processingThreadPriority;
+ int highThreadPriority;
+ int throttledThreadPriority;
+ unsigned long throttledSleepMsecs;
+
+ int isStopped;
+ volatile int isActive;
+ volatile int stopProcessing; // stop thread once existing buffers have been returned
+ volatile int abortProcessing; // stop thread immediately
+
+// DWORD allBuffersDurationMs; // used to calculate timeouts
+}
+PaMacCoreStream;
+
+// Data needed by the CoreAudio callback functions
+typedef struct PaMacCore_CD
+{
+ PaMacCoreStream *stream;
+ PaStreamCallback *callback;
+ void *userData;
+ PaUtilConverter *inputConverter;
+ PaUtilConverter *outputConverter;
+ void *inputBuffer;
+ void *outputBuffer;
+ int inputChannelCount;
+ int outputChannelCount;
+ PaSampleFormat inputSampleFormat;
+ PaSampleFormat outputSampleFormat;
+ PaUtilTriangularDitherGenerator *ditherGenerator;
+}
+PaMacClientData;
+
+// ===== CoreAudio-PortAudio bridge functions =====
+#pragma mark CoreAudio-PortAudio bridge functions
+
+// Maps CoreAudio OSStatus codes to PortAudio PaError codes
+static PaError conv_err(OSStatus error)
+{
+ PaError result;
+
+ switch (error) {
+ case kAudioHardwareNoError:
+ result = paNoError; break;
+ case kAudioHardwareNotRunningError:
+ result = paInternalError; break;
+ case kAudioHardwareUnspecifiedError:
+ result = paInternalError; break;
+ case kAudioHardwareUnknownPropertyError:
+ result = paInternalError; break;
+ case kAudioHardwareBadPropertySizeError:
+ result = paInternalError; break;
+ case kAudioHardwareIllegalOperationError:
+ result = paInternalError; break;
+ case kAudioHardwareBadDeviceError:
+ result = paInvalidDevice; break;
+ case kAudioHardwareBadStreamError:
+ result = paBadStreamPtr; break;
+ case kAudioHardwareUnsupportedOperationError:
+ result = paInternalError; break;
+ case kAudioDeviceUnsupportedFormatError:
+ result = paSampleFormatNotSupported; break;
+ case kAudioDevicePermissionsError:
+ result = paDeviceUnavailable; break;
+ default:
+ result = paInternalError;
+ }
+
+ return result;
+}
+
+static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate)
+{
+ struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription));
+ streamDescription->mSampleRate = sampleRate;
+ streamDescription->mFormatID = kAudioFormatLinearPCM;
+ streamDescription->mFormatFlags = 0;
+ streamDescription->mFramesPerPacket = 1;
+
+ if (parameters->sampleFormat & paNonInterleaved) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved;
+ streamDescription->mChannelsPerFrame = 1;
+ streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat);
+ streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat);
+ }
+ else {
+ streamDescription->mChannelsPerFrame = parameters->channelCount;
+ }
+
+ streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame;
+ streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket;
+
+ if (parameters->sampleFormat & paFloat32) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+ streamDescription->mBitsPerChannel = 32;
+ }
+ else if (parameters->sampleFormat & paInt32) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ streamDescription->mBitsPerChannel = 32;
+ }
+ else if (parameters->sampleFormat & paInt24) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ streamDescription->mBitsPerChannel = 24;
+ }
+ else if (parameters->sampleFormat & paInt16) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ streamDescription->mBitsPerChannel = 16;
+ }
+ else if (parameters->sampleFormat & paInt8) {
+ streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ streamDescription->mBitsPerChannel = 8;
+ }
+ else if (parameters->sampleFormat & paInt32) {
+ streamDescription->mBitsPerChannel = 8;
+ }
+
+ return streamDescription;
+}
+
+static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime)
+{
+ PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo));
+
+ timeInfo->inputBufferAdcTime = inputTime->mSampleTime;
+ timeInfo->currentTime = now->mSampleTime;
+ timeInfo->outputBufferDacTime = outputTime->mSampleTime;
+
+ return timeInfo;
+}
+
+// ===== support functions =====
+#pragma mark support functions
+
+static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi)
+{
+ if( macCoreHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( macCoreHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( macCoreHostApi );
+}
+
+static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput)
+{
+ UInt32 propSize;
+ PaError err = paNoError;
+ UInt32 i;
+ int numChannels = 0;
+ AudioBufferList *buflist;
+
+ err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
+ buflist = PaUtil_AllocateMemory(propSize);
+ err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
+ if (!err) {
+ for (i = 0; i < buflist->mNumberBuffers; ++i) {
+ numChannels += buflist->mBuffers[i].mNumberChannels;
+ }
+
+ if (isInput)
+ deviceInfo->maxInputChannels = numChannels;
+ else
+ deviceInfo->maxOutputChannels = numChannels;
+
+ int frameLatency;
+ propSize = sizeof(UInt32);
+ err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
+ if (!err) {
+ double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
+ if (isInput) {
+ deviceInfo->defaultLowInputLatency = secondLatency;
+ deviceInfo->defaultHighInputLatency = secondLatency;
+ }
+ else {
+ deviceInfo->defaultLowOutputLatency = secondLatency;
+ deviceInfo->defaultHighOutputLatency = secondLatency;
+ }
+ }
+ }
+ PaUtil_FreeMemory(buflist);
+
+ return err;
+}
+
+static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex )
+{
+ PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ PaError err = paNoError;
+ UInt32 propSize;
+
+ err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
+ // FIXME: this allocation should be part of the allocations group
+ char *name = PaUtil_AllocateMemory(propSize);
+ err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
+ if (!err) {
+ deviceInfo->name = name;
+ }
+
+ Float64 sampleRate;
+ propSize = sizeof(Float64);
+ err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
+ if (!err) {
+ deviceInfo->defaultSampleRate = sampleRate;
+ }
+
+
+ // Get channel info
+ err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1);
+ err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
+
+ return err;
+}
+
+static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation *hostApi;
+ PaMacCoreDeviceInfo *deviceInfoArray;
+
+ // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized.
+ hostApi = &macCoreHostApi->inheritedHostApiRep;
+ hostApi->info.deviceCount = 0;
+ hostApi->info.defaultInputDevice = paNoDevice;
+ hostApi->info.defaultOutputDevice = paNoDevice;
+
+ UInt32 propsize;
+ AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL);
+ int numDevices = propsize / sizeof(AudioDeviceID);
+ hostApi->info.deviceCount = numDevices;
+ if (numDevices > 0) {
+ hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+ if( !hostApi->deviceInfos )
+ {
+ return paInsufficientMemory;
+ }
+
+ // allocate all device info structs in a contiguous block
+ deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory(
+ macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices );
+ if( !deviceInfoArray )
+ {
+ return paInsufficientMemory;
+ }
+
+ macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize);
+ AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds);
+
+ AudioDeviceID defaultInputDevice, defaultOutputDevice;
+ propsize = sizeof(AudioDeviceID);
+ AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice);
+ AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice);
+
+ UInt32 i;
+ for (i = 0; i < numDevices; ++i) {
+ if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) {
+ hostApi->info.defaultInputDevice = i;
+ }
+ if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) {
+ hostApi->info.defaultOutputDevice = i;
+ }
+ InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex);
+ hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo);
+ }
+ }
+
+ return result;
+}
+
+static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput)
+{
+ UInt32 propSize = sizeof(AudioStreamBasicDescription);
+ AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize);
+
+ streamDescription->mSampleRate = sampleRate;
+ streamDescription->mFormatID = 0;
+ streamDescription->mFormatFlags = 0;
+ streamDescription->mBytesPerPacket = 0;
+ streamDescription->mFramesPerPacket = 0;
+ streamDescription->mBytesPerFrame = 0;
+ streamDescription->mChannelsPerFrame = 0;
+ streamDescription->mBitsPerChannel = 0;
+ streamDescription->mReserved = 0;
+
+ OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription);
+ PaUtil_FreeMemory(streamDescription);
+ return result;
+}
+
+static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount)
+{
+ int frameSpacing, channelSpacing;
+ if (destination->inputSampleFormat & paNonInterleaved) {
+ frameSpacing = 1;
+ channelSpacing = destination->inputChannelCount;
+ }
+ else {
+ frameSpacing = destination->inputChannelCount;
+ channelSpacing = 1;
+ }
+
+ AudioBuffer const *inputBuffer = &source->mBuffers[0];
+ void *coreAudioBuffer = inputBuffer->mData;
+ void *portAudioBuffer = destination->inputBuffer;
+ UInt32 i, streamNumber, streamChannel;
+ for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) {
+ if (streamChannel >= inputBuffer->mNumberChannels) {
+ ++streamNumber;
+ inputBuffer = &source->mBuffers[streamNumber];
+ coreAudioBuffer = inputBuffer->mData;
+ streamChannel = 0;
+ }
+ destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);
+ coreAudioBuffer += sizeof(Float32);
+ portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;
+ }
+}
+
+static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount)
+{
+ int frameSpacing, channelSpacing;
+ if (source->outputSampleFormat & paNonInterleaved) {
+ frameSpacing = 1;
+ channelSpacing = source->outputChannelCount;
+ }
+ else {
+ frameSpacing = source->outputChannelCount;
+ channelSpacing = 1;
+ }
+
+ AudioBuffer *outputBuffer = &destination->mBuffers[0];
+ void *coreAudioBuffer = outputBuffer->mData;
+ void *portAudioBuffer = source->outputBuffer;
+ UInt32 i, streamNumber, streamChannel;
+ for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) {
+ if (streamChannel >= outputBuffer->mNumberChannels) {
+ ++streamNumber;
+ outputBuffer = &destination->mBuffers[streamNumber];
+ coreAudioBuffer = outputBuffer->mData;
+ streamChannel = 0;
+ }
+ source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL);
+ coreAudioBuffer += sizeof(Float32);
+ portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing;
+ }
+}
+
+static OSStatus AudioIOProc( AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* inClientData)
+{
+ PaMacClientData *clientData = (PaMacClientData *)inClientData;
+ PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+
+ PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+
+ AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
+ unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+
+ if (clientData->inputBuffer) {
+ CopyInputData(clientData, inInputData, frameCount);
+ }
+ PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+ if (clientData->outputBuffer) {
+ CopyOutputData(outOutputData, clientData, frameCount);
+ }
+
+ PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+
+ if (result == paComplete || result == paAbort) {
+ Pa_StopStream(clientData->stream);
+ }
+}
+
+// This is not for input-only streams, this is for streams where the input device is different from the output device
+// TODO: This needs to store the output data in a buffer, to be written to the device the next time AudioOutputProc is called
+static OSStatus AudioInputProc( AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* inClientData)
+{
+ PaMacClientData *clientData = (PaMacClientData *)inClientData;
+ PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+
+ PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+
+ AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];
+ unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));
+
+ CopyInputData(clientData, inInputData, frameCount);
+ clientData->callback(clientData->inputBuffer, NULL, frameCount, timeInfo, paNoFlag, clientData->userData);
+
+ PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+}
+
+// This is not for output-only streams, this is for streams where the input device is different from the output device
+static OSStatus AudioOutputProc( AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* inClientData)
+{
+ PaMacClientData *clientData = (PaMacClientData *)inClientData;
+ PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+
+ PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+
+ AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
+ unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+
+ clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+
+ CopyOutputData(outOutputData, clientData, frameCount);
+
+ PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+}
+
+static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput)
+{
+ PaError result = paNoError;
+
+ double actualSampleRate;
+ UInt32 propSize = sizeof(double);
+ result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate));
+
+ result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate));
+
+ if (result == paNoError && actualSampleRate != sampleRate) {
+ result = paInvalidSampleRate;
+ }
+
+ return result;
+}
+
+static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)
+{
+ PaError result = paNoError;
+ UInt32 preferredFramesPerBuffer = framesPerBuffer;
+ // while (preferredFramesPerBuffer > UINT32_MAX) {
+ // preferredFramesPerBuffer /= 2;
+ // }
+
+ UInt32 actualFramesPerBuffer;
+ UInt32 propSize = sizeof(UInt32);
+ result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer));
+
+ result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer));
+
+ if (result != paNoError) {
+ // do nothing
+ }
+ else if (actualFramesPerBuffer > framesPerBuffer) {
+ result = paBufferTooSmall;
+ }
+ else if (actualFramesPerBuffer < framesPerBuffer) {
+ result = paBufferTooBig;
+ }
+
+ return result;
+}
+
+static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput)
+{
+ PaError err = paNoError;
+ err = SetSampleRate(device, sampleRate, isInput);
+ if( err == paNoError )
+ err = SetFramesPerBuffer(device, framesPerBuffer, isInput);
+ return err;
+}
+
+// ===== PortAudio functions =====
+#pragma mark PortAudio functions
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
+
+ CleanUp(macCoreHostApi);
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
+ PaDeviceInfo *deviceInfo;
+
+ PaError result = paNoError;
+ if (inputParameters) {
+ deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
+ if (inputParameters->channelCount > deviceInfo->maxInputChannels)
+ result = paInvalidChannelCount;
+ else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) {
+ result = paInvalidSampleRate;
+ }
+ }
+ if (outputParameters && result == paNoError) {
+ deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
+ if (outputParameters->channelCount > deviceInfo->maxOutputChannels)
+ result = paInvalidChannelCount;
+ else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) {
+ result = paInvalidSampleRate;
+ }
+ }
+
+ return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError err = paNoError;
+ PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi;
+ PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream));
+ stream->isActive = 0;
+ stream->isStopped = 1;
+ stream->inputDevice = kAudioDeviceUnknown;
+ stream->outputDevice = kAudioDeviceUnknown;
+
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ ( (streamCallback)
+ ? &macCoreHostApi->callbackStreamInterface
+ : &macCoreHostApi->blockingStreamInterface ),
+ streamCallback, userData );
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ *s = (PaStream*)stream;
+ PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData));
+ clientData->stream = stream;
+ clientData->callback = streamCallback;
+ clientData->userData = userData;
+ clientData->inputBuffer = 0;
+ clientData->outputBuffer = 0;
+ clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator));
+ PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator);
+
+ if (inputParameters != NULL) {
+ stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device];
+ clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags);
+ clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount);
+ clientData->inputChannelCount = inputParameters->channelCount;
+ clientData->inputSampleFormat = inputParameters->sampleFormat;
+ err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1);
+ }
+
+ if (err == paNoError && outputParameters != NULL) {
+ stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device];
+ clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags);
+ clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount);
+ clientData->outputChannelCount = outputParameters->channelCount;
+ clientData->outputSampleFormat = outputParameters->sampleFormat;
+ err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0);
+ }
+
+ if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) {
+ AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice;
+
+ AudioDeviceAddIOProc(device, AudioIOProc, clientData);
+ }
+ else {
+ // using different devices for input and output
+ AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData);
+ AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData);
+ }
+
+ return err;
+}
+
+// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted.
+static PaError CloseStream( PaStream* s )
+{
+ PaError err = paNoError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ if (stream->inputDevice != kAudioDeviceUnknown) {
+ if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+ err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc));
+ }
+ else {
+ err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc));
+ err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc));
+ }
+ }
+ else {
+ err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc));
+ }
+
+ return err;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError err = paNoError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ if (stream->inputDevice != kAudioDeviceUnknown) {
+ if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+ err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc));
+ }
+ else {
+ err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc));
+ err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc));
+ }
+ }
+ else {
+ err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc));
+ }
+
+ stream->isActive = 1;
+ stream->isStopped = 0;
+ return err;
+}
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError err = paNoError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ if (stream->inputDevice != kAudioDeviceUnknown) {
+ if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+ err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc));
+ }
+ else {
+ err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc));
+ err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc));
+ }
+ }
+ else {
+ err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc));
+ }
+
+ stream->isActive = 0;
+ stream->isStopped = 1;
+ return err;
+}
+
+static PaError StopStream( PaStream *s )
+{
+ // TODO: this should be nicer than abort
+ return AbortStream(s);
+}
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ OSStatus err;
+ PaTime result;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp));
+ if (stream->inputDevice != kAudioDeviceUnknown) {
+ err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp);
+ }
+ else {
+ err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp);
+ }
+
+ result = err ? 0 : timeStamp->mSampleTime;
+ PaUtil_FreeMemory(timeStamp);
+
+ return result;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ return paInternalError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ return paInternalError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ return paInternalError;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ return paInternalError;
+}
+
+// HostAPI-specific initialization function
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) );
+ if( !macCoreHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ macCoreHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !macCoreHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &macCoreHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paCoreAudio;
+ (*hostApi)->info.name = "CoreAudio";
+
+ result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex);
+ if (result != paNoError) {
+ goto error;
+ }
+
+ // Set up the proper callbacks to this HostApi's functions
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( macCoreHostApi ) {
+ CleanUp(macCoreHostApi);
+ }
+
+ return result;
+} \ No newline at end of file
diff --git a/pjmedia/src/pjmedia/portaudio/ringbuffer.c b/pjmedia/src/pjmedia/portaudio/ringbuffer.c
new file mode 100644
index 00000000..16031fef
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/ringbuffer.c
@@ -0,0 +1,199 @@
+/*
+ * $Id$
+ * ringbuffer.c
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+/***************************************************************************
+ * Initialize FIFO.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+ if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
+ rbuf->bufferSize = numBytes;
+ rbuf->buffer = (char *)dataPtr;
+ RingBuffer_Flush( rbuf );
+ rbuf->bigMask = (numBytes*2)-1;
+ rbuf->smallMask = (numBytes)-1;
+ return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+ return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+ return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+/***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+ rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetWriteAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if write is not contiguous. */
+ index = rbuf->writeIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetReadAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if read is not contiguous. */
+ index = rbuf->readIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numWritten;
+ void *data1, *data2;
+ numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+
+ memcpy( data1, data, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data2, data, size2 );
+ }
+ else
+ {
+ memcpy( data1, data, size1 );
+ }
+ RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+ return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numRead;
+ void *data1, *data2;
+ numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+ memcpy( data, data1, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data, data2, size2 );
+ }
+ else
+ {
+ memcpy( data, data1, size1 );
+ }
+ RingBuffer_AdvanceReadIndex( rbuf, numRead );
+ return numRead;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/ringbuffer.h b/pjmedia/src/pjmedia/portaudio/ringbuffer.h
new file mode 100644
index 00000000..dd769bee
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/ringbuffer.h
@@ -0,0 +1,101 @@
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * ringbuffer.h
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program is distributed with the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
+ long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
+ long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
+ long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
+ long smallMask; /* Used for fitting indices to buffer. */
+ char *buffer;
+}
+RingBuffer;
+/*
+ * Initialize Ring Buffer.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
+
+/* Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf );
+
+/* Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
+/* Return number of bytes available for read. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
+/* Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
+/* Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
+
+/* Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
+
+/* Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _RINGBUFFER_H */