summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-10-13 17:57:42 +0000
committerBenny Prijono <bennylp@teluu.com>2006-10-13 17:57:42 +0000
commit58d42b6fbe3277ec2de962353ee0eed4afcca627 (patch)
tree9e64fa2d9b955f0a4a90d7c4e3c967e9db1582ba /pjmedia
parent724b815aa990f9c5438841dfca5ea5d937f8231e (diff)
Updated PortAudio to latest version for Mac
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@770 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/build/os-auto.mak.in39
-rw-r--r--pjmedia/src/pjmedia/portaudio/notes.txt145
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core.c2581
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core.h118
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.c564
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.h133
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_internal.h162
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_old.c914
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.c612
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.h215
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_mac_hostapis.c18
-rw-r--r--pjmedia/src/pjmedia/portaudio/ringbuffer.c83
-rw-r--r--pjmedia/src/pjmedia/portaudio/ringbuffer.h30
13 files changed, 4867 insertions, 747 deletions
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index e35cebd3..9f444690 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -5,16 +5,19 @@ export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_g711_plc@ @ac_
# Define the desired sound device backend
# Valid values are:
-# - pa_unix: PortAudio on Unix (OSS or ALSA)
-# - pa_darwinos: PortAudio on MacOSX (CoreAudio)
-# - pa_win32: PortAudio on Win32 (WMME)
-# - ds: Win32 DirectSound (dsound.c)
-# - null: Null sound device (nullsound.c)
+# - pa_unix: PortAudio on Unix (OSS or ALSA)
+# - pa_darwinos: PortAudio on MacOSX (CoreAudio)
+# - pa_old_darwinos: PortAudio on MacOSX (old CoreAudio, for OSX 10.2)
+# - pa_win32: PortAudio on Win32 (WMME)
+# - ds: Win32 DirectSound (dsound.c)
+# - null: Null sound device (nullsound.c)
AC_PJMEDIA_SND=@ac_pjmedia_snd@
# For Unix, specify if ALSA should be supported
AC_PA_USE_ALSA=@ac_pa_use_alsa@
+# Additional PortAudio CFLAGS are in @ac_pa_cflags@
+
#
# Codecs
#
@@ -77,16 +80,34 @@ endif
#
-# PortAudio on MacOS X
+# PortAudio on MacOS X (using current PortAudio)
#
ifeq ($(AC_PJMEDIA_SND),pa_darwinos)
-export PJMEDIA_OBJS += $(PA_DIR)/pa_mac_hostapis.o $(PA_DIR)/pa_unix_util.o \
- $(PA_DIR)/pa_mac_core.o
-export CFLAGS += -DPA_USE_COREAUDIO=1 \
+export PJMEDIA_OBJS += $(PA_DIR)/pa_mac_hostapis.o \
+ $(PA_DIR)/pa_unix_util.o \
+ $(PA_DIR)/pa_mac_core.o \
+ $(PA_DIR)/pa_mac_core_blocking.o \
+ $(PA_DIR)/pa_mac_core_utilities.o \
+ $(PA_DIR)/ringbuffer.o
+export CFLAGS += -DPA_USE_COREAUDIO=1 \
-DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_PORTAUDIO_SOUND
+export CFLAGS += @ac_pa_cflags@
endif
#
+# PortAudio on MacOS X (using old PortAudio, for MacOS X 10.2.x)
+#
+ifeq ($(AC_PJMEDIA_SND),pa_old_darwinos)
+export PJMEDIA_OBJS += $(PA_DIR)/pa_mac_hostapis.o \
+ $(PA_DIR)/pa_unix_util.o \
+ $(PA_DIR)/pa_mac_core_old.o
+export CFLAGS += -DPA_USE_COREAUDIO=1 \
+ -DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_PORTAUDIO_SOUND
+export CFLAGS += @ac_pa_cflags@
+endif
+
+#
+#
# PortAudio on Win32 (WMME)
#
ifeq ($(AC_PJMEDIA_SND),pa_win32)
diff --git a/pjmedia/src/pjmedia/portaudio/notes.txt b/pjmedia/src/pjmedia/portaudio/notes.txt
new file mode 100644
index 00000000..ad66f358
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/notes.txt
@@ -0,0 +1,145 @@
+Notes on status of CoreAudio Implementation of PortAudio
+
+Document Last Updated December 9, 2005
+
+There are currently two implementations of PortAudio for Mac Core Audio.
+
+The original is in pa_mac_core_old.c, and the newer, default implementation
+is in pa_mac_core.c.
+Only pa_mac_core.c is currently developed and supported as it uses apple's
+current core audio technology. To select use the old implementation, replace
+pa_mac_core.c with pa_mac_core_old.c (eg. "cp pa_mac_core_auhal.c
+pa_mac_core.c"), then run configure and make as usual.
+
+----------------------------------------
+
+Notes on Original implementation:
+
+by Phil Burk and Darren Gibbs
+
+Last updated March 20, 2002
+
+WHAT WORKS
+
+Output with very low latency, <10 msec.
+Half duplex input or output.
+Full duplex on the same CoreAudio device.
+The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
+Pa_GetCPULoad()
+Pa_StreamTime()
+
+KNOWN BUGS OR LIMITATIONS
+
+We do not yet support simultaneous input and output on different
+devices. Note that some CoreAudio devices like the Roland UH30 look
+like one device but are actually two different CoreAudio devices. The
+Built-In audio is typically one CoreAudio device.
+
+Mono doesn't work.
+
+DEVICE MAPPING
+
+CoreAudio devices can support both input and output. But the sample
+rates supported may be different. So we have map one or two PortAudio
+device to each CoreAudio device depending on whether it supports
+input, output or both.
+
+When we query devices, we first get a list of CoreAudio devices. Then
+we scan the list and add a PortAudio device for each CoreAudio device
+that supports input. Then we make a scan for output devices.
+
+-------------------------------------------
+
+Notes on Newer/Default AUHAL implementation:
+
+by Bjorn Roche
+
+Last Updated December 9, 2005
+
+Principle of Operation:
+
+This implementation uses AUHAL for audio I/O. To some extent, it also
+operates at the "HAL" Layer, though this behavior can be limited by
+platform specific flags (see pa_mac_core.h for details). The default
+settings should be reasonable: they don't change the SR of the device and
+don't cause interruptions if other devices are using the device.
+
+Major Software Elements Used: Apple's HAL AUs provide output SR
+conversion transparently, however, only on output, so this
+implementation uses AudioConverters to convert the sample rate on input.
+A PortAudio ring buffer is used to buffer input when sample rate
+conversion is required or when separate audio units are used for duplex
+IO. Finally, a PortAudio buffer processor is used to convert formats and
+provide additional buffers if needed. Internally, interleaved floating
+point data streams are used exclusively - the audio unit converts from
+the audio hardware's native format to interleaved float PCM and
+PortAudio's Buffer processor is used for conversion to user formats.
+
+Simplex Input: Simplex input uses a single callback. If sample rate
+conversion is required, a ring buffer and AudioConverter are used as
+well.
+
+Simplex output: Simplex output uses a single callback. No ring buffer or
+audio converter is used because AUHAL does its own output SR conversion.
+
+Duplex, one device (no SR conversion): When one device is used, a single
+callback is used. This achieves very low latency.
+
+Duplex, separate devices or SR conversion: When SR conversion is
+required, data must be buffered before it is converted and data is not
+always available at the same times on input and output, so SR conversion
+requires the same treatment as separate devices. The input callback
+reads data and puts it in the ring buffer. The output callback reads the
+data off the ring buffer, into an audio converter and finally to the
+buffer processor.
+
+Platform Specific Options:
+
+By using the flags in pa_mac_core.h, the user may specify several options.
+For example, the user can specify the sample-rate conversion quality, and
+the extent to which PA will attempt to "play nice" and to what extent it
+will interrupt other apps to improve performance. For example, if 44100 Hz
+sample rate is requested but the device is set at 48000 Hz, PA can either
+change the device for optimal playback ("Pro" mode), which may interrupt
+other programs playing back audio, or simple use a sample-rate coversion,
+which allows for friendlier sharing of the device ("Play Nice" mode).
+
+
+Known issues:
+
+- Latency: Latency settings are ignored in most cases. Exceptions are when
+doing I/O between different devices and as a hint for selecting a realtively
+low or relatively high latency in conjunction with
+paHostFramesPerBufferUnspecified. Latency settings are always automatically
+bound to "safe" values, however, so setting extreme values here should not be
+an issue.
+
+- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
+are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
+where the buffer size and sample rate of the audio device is most likely
+to match the expected values.
+
+- Timing info. It reports on stream time, but I'm probably doing something
+wrong since patest_sine_time often reports negative latency numbers.
+
+- xrun detection: The only xrun detection performed is when reading
+and writing the ring buffer. There is probably more that can be done.
+
+- abort/stop issues: stopping a stream is always a complete operation,
+but latency should be low enough to make the lack of a separate abort
+unnecessary. Apple clarifies its AudioOutputUnitStop() call here:
+http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html
+
+- blocking interface: Not implemented.
+
+- multichannel: It has been tested successfully on multichannel hardware
+from MOTU: traveler and 896HD.
+
+- sample rate conversion quality: By default, SR conversion is the maximum
+available. This can be tweaked using flags pa_mac_core.h. Note that the AU
+render quyality property is used to set the sample rat conversion quality
+as "documented" here:
+http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
+
+- x86: I haven't tested it on an x86 Mac myself, but users have reported
+being able to comiple and run it.
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core.c b/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
index ce55b477..2b2b9cc1 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
@@ -1,13 +1,24 @@
/*
- * $Id$
- * pa_mac_core.c
- * Implementation of PortAudio for Mac OS X CoreAudio
- *
+ * Implementation of the PortAudio API for Apple AUHAL
+ *
* 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
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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
@@ -20,10 +31,6 @@
* 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.
@@ -31,603 +38,956 @@
* 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.
*/
-#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;
+/**
+ @file pa_mac_core
+ @ingroup hostapi_src
+ @author Bjorn Roche
+ @brief AUHAL implementation of PortAudio
+*/
-typedef struct PaMacCore_DI
-{
- PaDeviceInfo inheritedDeviceInfo;
-}
-PaMacCoreDeviceInfo;
+/* FIXME: not all error conditions call PaUtil_SetLastHostErrorInfo()
+ * PaMacCore_SetError() will do this.
+ */
-// 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;
+#include "pa_mac_core_internal.h"
-// 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;
+#include <string.h> /* strlen(), memcmp() etc. */
-// ===== CoreAudio-PortAudio bridge functions =====
-#pragma mark CoreAudio-PortAudio bridge functions
+#include "pa_mac_core.h"
+#include "pa_mac_core_utilities.h"
+#include "pa_mac_core_blocking.h"
-// 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)
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
{
- PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo));
-
- timeInfo->inputBufferAdcTime = inputTime->mSampleTime;
- timeInfo->currentTime = now->mSampleTime;
- timeInfo->outputBufferDacTime = outputTime->mSampleTime;
-
- return timeInfo;
+#endif /* __cplusplus */
+
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
}
+#endif /* __cplusplus */
-// ===== support functions =====
-#pragma mark support functions
+#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
-static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi)
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+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 );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static void setStreamStartTime( PaStream *stream );
+static OSStatus AudioIOProc( void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData );
+static double GetStreamCpuLoad( PaStream* stream );
+
+static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
+ PaDeviceInfo *deviceInfo,
+ AudioDeviceID macCoreDeviceId,
+ int isInput);
+
+static PaError OpenAndSetupOneAudioUnit(
+ const PaStreamParameters *inStreamParams,
+ const PaStreamParameters *outStreamParams,
+ const unsigned long requestedFramesPerBuffer,
+ unsigned long *actualInputFramesPerBuffer,
+ unsigned long *actualOutputFramesPerBuffer,
+ const PaMacAUHAL *auhalHostApi,
+ AudioUnit *audioUnit,
+ AudioConverterRef *srConverter,
+ AudioDeviceID *audioDevice,
+ const double sampleRate,
+ void *refCon );
+
+/* for setting errors. */
+#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+
+
+/*currently, this is only used in initialization, but it might be modified
+ to be used when the list of devices changes.*/
+static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
{
- if( macCoreHostApi->allocations )
+ UInt32 size;
+ UInt32 propsize;
+ VVDBUG(("gatherDeviceInfo()\n"));
+ /* -- free any previous allocations -- */
+ if( auhalHostApi->devIds )
+ PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
+ auhalHostApi->devIds = NULL;
+
+ /* -- figure out how many devices there are -- */
+ AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
+ &propsize,
+ NULL );
+ auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
+
+ VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
+
+ /* -- copy the device IDs -- */
+ auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations,
+ propsize );
+ if( !auhalHostApi->devIds )
+ return paInsufficientMemory;
+ AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
+ &propsize,
+ auhalHostApi->devIds );
+#ifdef MAC_CORE_VERBOSE_DEBUG
{
- PaUtil_FreeAllAllocations( macCoreHostApi->allocations );
- PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations );
+ int i;
+ for( i=0; i<auhalHostApi->devCount; ++i )
+ printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
}
-
- PaUtil_FreeMemory( macCoreHostApi );
+#endif
+
+ size = sizeof(AudioDeviceID);
+ auhalHostApi->defaultIn = kAudioDeviceUnknown;
+ auhalHostApi->defaultOut = kAudioDeviceUnknown;
+
+ /* determine the default device. */
+ /* I am not sure how these calls to AudioHardwareGetProperty()
+ could fail, but in case they do, we use the first available
+ device as the default. */
+ if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
+ &size,
+ &auhalHostApi->defaultIn) ) {
+ int i;
+ auhalHostApi->defaultIn = kAudioDeviceUnknown;
+ VDBUG(("Failed to get default input device from OS."));
+ VDBUG((" I will substitute the first available input Device."));
+ for( i=0; i<auhalHostApi->devCount; ++i ) {
+ PaDeviceInfo devInfo;
+ if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
+ auhalHostApi->devIds[i], TRUE ) )
+ if( devInfo.maxInputChannels ) {
+ auhalHostApi->defaultIn = auhalHostApi->devIds[i];
+ break;
+ }
+ }
+ }
+ if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+ &size,
+ &auhalHostApi->defaultOut) ) {
+ int i;
+ auhalHostApi->defaultIn = kAudioDeviceUnknown;
+ VDBUG(("Failed to get default output device from OS."));
+ VDBUG((" I will substitute the first available output Device."));
+ for( i=0; i<auhalHostApi->devCount; ++i ) {
+ PaDeviceInfo devInfo;
+ if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
+ auhalHostApi->devIds[i], FALSE ) )
+ if( devInfo.maxOutputChannels ) {
+ auhalHostApi->defaultOut = auhalHostApi->devIds[i];
+ break;
+ }
+ }
+ }
+
+ VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) );
+ VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
+
+ return paNoError;
}
-static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput)
+static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
+ PaDeviceInfo *deviceInfo,
+ AudioDeviceID macCoreDeviceId,
+ int isInput)
{
UInt32 propSize;
PaError err = paNoError;
UInt32 i;
int numChannels = 0;
- AudioBufferList *buflist;
+ AudioBufferList *buflist = NULL;
+ UInt32 frameLatency;
+
+ VVDBUG(("GetChannelInfo()\n"));
+
+ /* Get the number of channels from the stream configuration.
+ Fail if we can't get this. */
+
+ err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
+ if (err)
+ return err;
- 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;
- }
- }
+ if( !buflist )
+ return paInsufficientMemory;
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
+ if (err)
+ goto error;
+
+ for (i = 0; i < buflist->mNumberBuffers; ++i)
+ numChannels += buflist->mBuffers[i].mNumberChannels;
+
+ if (isInput)
+ deviceInfo->maxInputChannels = numChannels;
+ else
+ deviceInfo->maxOutputChannels = numChannels;
+
+ if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */
+ {
+ /* Get the latency. Don't fail if we can't get this. */
+ /* default to something reasonable */
+ deviceInfo->defaultLowInputLatency = .01;
+ deviceInfo->defaultHighInputLatency = .10;
+ deviceInfo->defaultLowOutputLatency = .01;
+ deviceInfo->defaultHighOutputLatency = .10;
+ propSize = sizeof(UInt32);
+ err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
+ if (!err)
+ {
+ /** FEEDBACK:
+ * This code was arrived at by trial and error, and some extentive, but not exhaustive
+ * testing. Sebastien Beaulieu <seb@plogue.com> has suggested using
+ * kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead.
+ * At the time this code was written, many users were reporting dropouts with audio
+ * programs that probably used this formula. This was probably
+ * around 10.4.4, and the problem is probably fixed now. So perhaps
+ * his formula should be reviewed and used.
+ * */
+ double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
+ if (isInput)
+ {
+ deviceInfo->defaultLowInputLatency = 3 * secondLatency;
+ deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency;
+ }
+ else
+ {
+ deviceInfo->defaultLowOutputLatency = 3 * secondLatency;
+ deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency;
+ }
+ }
}
- PaUtil_FreeMemory(buflist);
-
+ PaUtil_FreeMemory( buflist );
+ return paNoError;
+ error:
+ PaUtil_FreeMemory( buflist );
return err;
}
-static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex )
+static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
+ PaDeviceInfo *deviceInfo,
+ AudioDeviceID macCoreDeviceId,
+ PaHostApiIndex hostApiIndex )
{
- PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
+ Float64 sampleRate;
+ char *name;
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;
+ VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId));
+
+ memset(deviceInfo, 0, sizeof(deviceInfo));
+
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ /* Get the device name. Fail if we can't get it. */
+ err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
+ if (err)
+ return err;
+
+ name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize);
+ if ( !name )
+ return paInsufficientMemory;
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
+ if (err)
+ return err;
+ deviceInfo->name = name;
+
+ /* Try to get the default sample rate. Don't fail if we can't get this. */
propSize = sizeof(Float64);
- err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
- if (!err) {
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
+ if (err)
+ deviceInfo->defaultSampleRate = 0.0;
+ else
deviceInfo->defaultSampleRate = sampleRate;
- }
+ /* Get the maximum number of input and output channels. Fail if we can't get this. */
- // Get channel info
- err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1);
- err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
+ err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1);
+ if (err)
+ return err;
- return err;
+ err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0);
+ if (err)
+ return err;
+
+ return paNoError;
}
-static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex )
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, 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 )
+ int i;
+ PaMacAUHAL *auhalHostApi;
+ PaDeviceInfo *deviceInfoArray;
+
+ VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
+
+ auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
+ if( !auhalHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ auhalHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !auhalHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ auhalHostApi->devIds = NULL;
+ auhalHostApi->devCount = 0;
+
+ /* get the info we need about the devices */
+ result = gatherDeviceInfo( auhalHostApi );
+ if( result != paNoError )
+ goto error;
+
+ *hostApi = &auhalHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paCoreAudio;
+ (*hostApi)->info.name = "Core Audio";
+
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+ (*hostApi)->info.deviceCount = 0;
+
+ if( auhalHostApi->devCount > 0 )
+ {
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
+ if( !(*hostApi)->deviceInfos )
{
- return paInsufficientMemory;
+ result = paInsufficientMemory;
+ goto error;
}
- // allocate all device info structs in a contiguous block
- deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory(
- macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices );
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
if( !deviceInfoArray )
{
- return paInsufficientMemory;
+ result = paInsufficientMemory;
+ goto error;
}
-
- 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;
+ for( i=0; i < auhalHostApi->devCount; ++i )
+ {
+ int err;
+ err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
+ auhalHostApi->devIds[i],
+ hostApiIndex );
+ if (err == paNoError)
+ { /* copy some info and set the defaults */
+ (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i];
+ if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
+ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+ if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
+ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+ (*hostApi)->info.deviceCount++;
}
- if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) {
- hostApi->info.defaultOutputDevice = i;
+ else
+ { /* there was an error. we need to shift the devices down, so we ignore this one */
+ int j;
+ auhalHostApi->devCount--;
+ for( j=i; j<auhalHostApi->devCount; ++j )
+ auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
+ i--;
}
- InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex);
- hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo);
}
}
- return result;
-}
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped,
+ IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped,
+ IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream,
+ GetStreamReadAvailable,
+ GetStreamWriteAvailable );
-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;
+error:
+ if( auhalHostApi )
+ {
+ if( auhalHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( auhalHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
}
- destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);
- coreAudioBuffer += sizeof(Float32);
- portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;
+
+ PaUtil_FreeMemory( auhalHostApi );
}
- return 0;
+ return result;
}
-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;
- }
- return 0;
-}
-static OSStatus AudioIOProc( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
- 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));
+ PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
- 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);
- }
+ VVDBUG(("Terminate()\n"));
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
-
- if (result == paComplete || result == paAbort) {
- Pa_StopStream(clientData->stream);
- }
- return 0;
-}
-
-// 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 );
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ TODO: Double check that everything is handled by alloc group
+ */
- AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];
- unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));
+ if( auhalHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( auhalHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
+ }
- CopyInputData(clientData, inInputData, frameCount);
- clientData->callback(clientData->inputBuffer, NULL, frameCount, timeInfo, paNoFlag, clientData->userData);
-
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
- return 0;
+ PaUtil_FreeMemory( auhalHostApi );
}
-// 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)
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
{
- PaMacClientData *clientData = (PaMacClientData *)inClientData;
- PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n",
+ inputParameters ? inputParameters->channelCount : -1,
+ inputParameters ? inputParameters->sampleFormat : -1,
+ outputParameters ? outputParameters->channelCount : -1,
+ outputParameters ? outputParameters->sampleFormat : -1,
+ (float) sampleRate ));
+
+ /** These first checks are standard PA checks. We do some fancier checks
+ later. */
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
- PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
- AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
- unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
- clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
- CopyOutputData(outOutputData, clientData, frameCount);
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
- return 0;
-}
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /* FEEDBACK */
+ /* I think the only way to check a given format SR combo is */
+ /* to try opening it. This could be disruptive, is that Okay? */
+ /* The alternative is to just read off available sample rates, */
+ /* but this will not work %100 of the time (eg, a device that */
+ /* supports N output at one rate but only N/2 at a higher rate.)*/
+
+ /* The following code opens the device with the requested parameters to
+ see if it works. */
+ {
+ PaError err;
+ PaStream *s;
+ err = OpenStream( hostApi, &s, inputParameters, outputParameters,
+ sampleRate, 1024, 0, (PaStreamCallback *)1, NULL );
+ if( err != paNoError && err != paInvalidSampleRate )
+ DBUG( ( "OpenStream @ %g returned: %d: %s\n",
+ (float) sampleRate, err, Pa_GetErrorText( err ) ) );
+ if( err )
+ return err;
+ err = CloseStream( s );
+ if( err ) {
+ /* FEEDBACK: is this more serious? should we assert? */
+ DBUG( ( "WARNING: could not close Stream. %d: %s\n",
+ err, Pa_GetErrorText( err ) ) );
+ }
+ }
-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;
+ return paFormatIsSupported;
}
-static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)
+static PaError OpenAndSetupOneAudioUnit(
+ const PaStreamParameters *inStreamParams,
+ const PaStreamParameters *outStreamParams,
+ const unsigned long requestedFramesPerBuffer,
+ unsigned long *actualInputFramesPerBuffer,
+ unsigned long *actualOutputFramesPerBuffer,
+ const PaMacAUHAL *auhalHostApi,
+ AudioUnit *audioUnit,
+ AudioConverterRef *srConverter,
+ AudioDeviceID *audioDevice,
+ const double sampleRate,
+ void *refCon )
{
- 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;
-}
+ ComponentDescription desc;
+ Component comp;
+ /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
+ AudioStreamBasicDescription desiredFormat;
+ OSErr result = noErr;
+ PaError paResult = paNoError;
+ int line = 0;
+ UInt32 callbackKey;
+ AURenderCallbackStruct rcbs;
+ unsigned long macInputStreamFlags = paMacCorePlayNice;
+ unsigned long macOutputStreamFlags = paMacCorePlayNice;
+
+ VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n",
+ inStreamParams ? inStreamParams->channelCount : -1,
+ inStreamParams ? inStreamParams->sampleFormat : -1,
+ outStreamParams ? outStreamParams->channelCount : -1,
+ outStreamParams ? outStreamParams->sampleFormat : -1,
+ requestedFramesPerBuffer ));
+
+ /* -- handle the degenerate case -- */
+ if( !inStreamParams && !outStreamParams ) {
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return paNoError;
+ }
-// ===== PortAudio functions =====
-#pragma mark PortAudio functions
+ /* -- get the user's api specific info, if they set any -- */
+ if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo )
+ macInputStreamFlags=
+ ((paMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
+ ->flags;
+ if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo )
+ macOutputStreamFlags=
+ ((paMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
+ ->flags;
+ /* Override user's flags here, if desired for testing. */
+
+ /*
+ * The HAL AU is a Mac OS style "component".
+ * the first few steps deal with that.
+ * Later steps work on a combination of Mac OS
+ * components and the slightly lower level
+ * HAL.
+ */
+
+ /* -- describe the output type AudioUnit -- */
+ /* Note: for the default AudioUnit, we could use the
+ * componentSubType value kAudioUnitSubType_DefaultOutput;
+ * but I don't think that's relevant here.
+ */
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ /* -- find the component -- */
+ comp = FindNextComponent( NULL, &desc );
+ if( !comp )
+ {
+ DBUG( ( "AUHAL component not found." ) );
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return paUnanticipatedHostError;
+ }
+ /* -- open it -- */
+ result = OpenAComponent( comp, audioUnit );
+ if( result )
+ {
+ DBUG( ( "Failed to open AUHAL component." ) );
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return ERR( result );
+ }
+ /* -- prepare a little error handling logic / hackery -- */
+#define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0)
-#ifdef __cplusplus
-extern "C"
-{
-#endif // __cplusplus
-
- PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
+ /* -- if there is input, we have to explicitly enable input -- */
+ if( inStreamParams )
+ {
+ UInt32 enableIO;
+ enableIO = 1;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input,
+ INPUT_ELEMENT,
+ &enableIO,
+ sizeof(enableIO) ) );
+ }
+ /* -- if there is no output, we must explicitly disable output -- */
+ if( !outStreamParams )
+ {
+ UInt32 enableIO;
+ enableIO = 0;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ OUTPUT_ELEMENT,
+ &enableIO,
+ sizeof(enableIO) ) );
+ }
+ /* -- set the devices -- */
+ /* make sure input and output are the same device if we are doing input and
+ output. */
+ if( inStreamParams && outStreamParams )
+ assert( outStreamParams->device == inStreamParams->device );
+ if( inStreamParams )
+ {
+ *audioDevice = auhalHostApi->devIds[inStreamParams->device] ;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ INPUT_ELEMENT,
+ audioDevice,
+ sizeof(AudioDeviceID) ) );
+ }
+ if( outStreamParams )
+ {
+ *audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ audioDevice,
+ sizeof(AudioDeviceID) ) );
+ }
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
-
- CleanUp(macCoreHostApi);
-}
+ /* -- set format -- */
+ bzero( &desiredFormat, sizeof(desiredFormat) );
+ desiredFormat.mFormatID = kAudioFormatLinearPCM ;
+ desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
+ desiredFormat.mFramesPerPacket = 1;
+ desiredFormat.mBitsPerChannel = sizeof( float ) * 8;
+
+ result = 0;
+ /* set device format first, but only touch the device if the user asked */
+ if( inStreamParams ) {
+ /*The callback never calls back if we don't set the FPB */
+ /*This seems wierd, because I would think setting anything on the device
+ would be disruptive.*/
+ paResult = setBestFramesPerBuffer( *audioDevice, FALSE,
+ requestedFramesPerBuffer,
+ actualInputFramesPerBuffer );
+ if( paResult ) goto error;
+ if( macInputStreamFlags & paMacCore_ChangeDeviceParameters ) {
+ bool requireExact;
+ requireExact=macInputStreamFlags&paMacCore_FailIfConversionRequired;
+ paResult = setBestSampleRateForDevice( *audioDevice, FALSE,
+ requireExact, sampleRate );
+ if( paResult ) goto error;
+ }
+ if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer )
+ *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ;
+ }
+ if( outStreamParams && !inStreamParams ) {
+ /*The callback never calls back if we don't set the FPB */
+ /*This seems wierd, because I would think setting anything on the device
+ would be disruptive.*/
+ paResult = setBestFramesPerBuffer( *audioDevice, TRUE,
+ requestedFramesPerBuffer,
+ actualOutputFramesPerBuffer );
+ if( paResult ) goto error;
+ if( macOutputStreamFlags & paMacCore_ChangeDeviceParameters ) {
+ bool requireExact;
+ requireExact=macOutputStreamFlags&paMacCore_FailIfConversionRequired;
+ paResult = setBestSampleRateForDevice( *audioDevice, TRUE,
+ requireExact, sampleRate );
+ if( paResult ) goto error;
+ }
+ }
-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;
- }
+ /* -- set the quality of the output converter -- */
+ if( outStreamParams ) {
+ UInt32 value = kAudioConverterQuality_Max;
+ switch( macOutputStreamFlags & 0x0700 ) {
+ case 0x0100: /*paMacCore_ConversionQualityMin:*/
+ value=kRenderQuality_Min;
+ break;
+ case 0x0200: /*paMacCore_ConversionQualityLow:*/
+ value=kRenderQuality_Low;
+ break;
+ case 0x0300: /*paMacCore_ConversionQualityMedium:*/
+ value=kRenderQuality_Medium;
+ break;
+ case 0x0400: /*paMacCore_ConversionQualityHigh:*/
+ value=kRenderQuality_High;
+ break;
+ }
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_RenderQuality,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ &value,
+ sizeof(value) ) );
}
- 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;
- }
+ /* now set the format on the Audio Units. */
+ if( outStreamParams )
+ {
+ desiredFormat.mSampleRate =sampleRate;
+ desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = outStreamParams->channelCount;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ OUTPUT_ELEMENT,
+ &desiredFormat,
+ sizeof(AudioStreamBasicDescription) ) );
+ }
+ if( inStreamParams )
+ {
+ AudioStreamBasicDescription sourceFormat;
+ UInt32 size = sizeof( AudioStreamBasicDescription );
+
+ /* keep the sample rate of the device, or we confuse AUHAL */
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ INPUT_ELEMENT,
+ &sourceFormat,
+ &size ) );
+ desiredFormat.mSampleRate = sourceFormat.mSampleRate;
+ desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &desiredFormat,
+ sizeof(AudioStreamBasicDescription) ) );
+ }
+ /* set the maximumFramesPerSlice */
+ /* not doing this causes real problems
+ (eg. the callback might not be called). The idea of setting both this
+ and the frames per buffer on the device is that we'll be most likely
+ to actually get the frame size we requested in the callback with the
+ minimum latency. */
+ if( outStreamParams ) {
+ UInt32 size = sizeof( *actualOutputFramesPerBuffer );
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Input,
+ OUTPUT_ELEMENT,
+ actualOutputFramesPerBuffer,
+ sizeof(unsigned long) ) );
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ actualOutputFramesPerBuffer,
+ &size ) );
+ }
+ if( inStreamParams ) {
+ /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ actualInputFramesPerBuffer,
+ sizeof(unsigned long) ) );
+/* Don't know why this causes problems
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global, //Output,
+ INPUT_ELEMENT,
+ actualInputFramesPerBuffer,
+ &size ) );
+*/
}
- return result;
+ /* -- if we have input, we may need to setup an SR converter -- */
+ /* even if we got the sample rate we asked for, we need to do
+ the conversion in case another program changes the underlying SR. */
+ /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */
+ if( inStreamParams ) {
+ AudioStreamBasicDescription desiredFormat;
+ AudioStreamBasicDescription sourceFormat;
+ UInt32 sourceSize = sizeof( sourceFormat );
+ bzero( &desiredFormat, sizeof(desiredFormat) );
+ desiredFormat.mSampleRate = sampleRate;
+ desiredFormat.mFormatID = kAudioFormatLinearPCM ;
+ desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
+ desiredFormat.mFramesPerPacket = 1;
+ desiredFormat.mBitsPerChannel = sizeof( float ) * 8;
+ desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
+
+ /* get the source format */
+ ERR_WRAP( AudioUnitGetProperty(
+ *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &sourceFormat,
+ &sourceSize ) );
+
+ if( desiredFormat.mSampleRate != sourceFormat.mSampleRate )
+ {
+ UInt32 value = kAudioConverterQuality_Max;
+ switch( macInputStreamFlags & 0x0700 ) {
+ case 0x0100: /*paMacCore_ConversionQualityMin:*/
+ value=kAudioConverterQuality_Min;
+ break;
+ case 0x0200: /*paMacCore_ConversionQualityLow:*/
+ value=kAudioConverterQuality_Low;
+ break;
+ case 0x0300: /*paMacCore_ConversionQualityMedium:*/
+ value=kAudioConverterQuality_Medium;
+ break;
+ case 0x0400: /*paMacCore_ConversionQualityHigh:*/
+ value=kAudioConverterQuality_High;
+ break;
+ }
+ VDBUG(( "Creating sample rate converter for input"
+ " to convert from %g to %g\n",
+ (float)sourceFormat.mSampleRate,
+ (float)desiredFormat.mSampleRate ) );
+ /* create our converter */
+ ERR_WRAP( AudioConverterNew(
+ &sourceFormat,
+ &desiredFormat,
+ srConverter ) );
+ /* Set quality */
+ ERR_WRAP( AudioConverterSetProperty(
+ *srConverter,
+ kAudioConverterSampleRateConverterQuality,
+ sizeof( value ),
+ &value ) );
+ }
+ }
+ /* -- set IOProc (callback) -- */
+ callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback
+ : kAudioOutputUnitProperty_SetInputCallback ;
+ rcbs.inputProc = AudioIOProc;
+ rcbs.inputProcRefCon = refCon;
+ ERR_WRAP( AudioUnitSetProperty(
+ *audioUnit,
+ callbackKey,
+ kAudioUnitScope_Output,
+ outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT,
+ &rcbs,
+ sizeof(rcbs)) );
+
+ if( inStreamParams && outStreamParams && *srConverter )
+ ERR_WRAP( AudioUnitSetProperty(
+ *audioUnit,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &rcbs,
+ sizeof(rcbs)) );
+
+ /*IMPLEMENTME: may need to worry about channel mapping.*/
+
+ /* initialize the audio unit */
+ ERR_WRAP( AudioUnitInitialize(*audioUnit) );
+
+ if( inStreamParams && outStreamParams )
+ VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
+ else if( inStreamParams )
+ VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
+ else if( outStreamParams )
+ VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
+ return paNoError;
+#undef ERR_WRAP
+
+ error:
+ CloseComponent( *audioUnit );
+ *audioUnit = NULL;
+ if( result )
+ return PaMacCore_SetError( result, line, 1 );
+ return paResult;
}
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
@@ -638,264 +998,1045 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
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;
-}
+ PaError result = paNoError;
+ PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
+ PaMacCoreStream *stream = 0;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n",
+ inputParameters ? inputParameters->channelCount : -1,
+ inputParameters ? inputParameters->sampleFormat : -1,
+ outputParameters ? outputParameters->channelCount : -1,
+ outputParameters ? outputParameters->sampleFormat : -1,
+ (float) sampleRate,
+ framesPerBuffer ));
+ VDBUG( ("Opening Stream.\n") );
+
+ /*These first few bits of code are from paSkeleton with few modifications.*/
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
-// 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;
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
- 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));
- }
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* Host supports interleaved float32 */
+ hostInputSampleFormat = paFloat32;
}
- else {
- err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc));
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */
}
-
- return err;
-}
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
-static PaError StartStream( PaStream *s )
-{
- PaError err = paNoError;
- PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
- 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));
- }
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* Host supports interleaved float32 */
+ hostOutputSampleFormat = paFloat32;
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */
}
- else {
- err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc));
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
}
-
- stream->isActive = 1;
- stream->isStopped = 0;
- return err;
+
+ /* If we fail after this point, we my be left in a bad state, with
+ some data structures setup and others not. So, first thing we
+ do is initialize everything so that if we fail, we know what hasn't
+ been touched.
+ */
+
+ stream->inputAudioBufferList.mBuffers[0].mData = NULL;
+ stream->inputRingBuffer.buffer = NULL;
+ bzero( &stream->blio, sizeof( PaMacBlio ) );
+/*
+ stream->blio.inputRingBuffer.buffer = NULL;
+ stream->blio.outputRingBuffer.buffer = NULL;
+ stream->blio.inputSampleFormat = inputParameters?inputParameters->sampleFormat:0;
+ stream->blio.inputSampleSize = computeSampleSizeFromFormat(stream->blio.inputSampleFormat);
+ stream->blio.outputSampleFormat=outputParameters?outputParameters->sampleFormat:0;
+ stream->blio.outputSampleSize = computeSampleSizeFromFormat(stream->blio.outputSampleFormat);
+*/
+ stream->inputSRConverter = NULL;
+ stream->inputUnit = NULL;
+ stream->outputUnit = NULL;
+ stream->inputFramesPerBuffer = 0;
+ stream->outputFramesPerBuffer = 0;
+ stream->bufferProcessorIsInitialized = FALSE;
+
+ /* assert( streamCallback ) ; */ /* only callback mode is implemented */
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &auhalHostApi->callbackStreamInterface,
+ streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &auhalHostApi->blockingStreamInterface,
+ BlioCallback, &stream->blio );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ /* -- handle paFramesPerBufferUnspecified -- */
+ if( framesPerBuffer == paFramesPerBufferUnspecified ) {
+ long requested = 64;
+ if( inputParameters )
+ requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 );
+ if( outputParameters )
+ requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 );
+ VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
+ requested ) );
+ if( requested <= 64 ) {
+ /*requested a realtively low latency. make sure this is in range of devices */
+ /*try to get the device's min natural buffer size and use that (but no smaller than 64).*/
+ AudioValueRange audioRange;
+ size_t size = sizeof( audioRange );
+ if( inputParameters ) {
+ WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size, &audioRange ) );
+ if( result )
+ requested = MAX( requested, audioRange.mMinimum );
+ }
+ if( outputParameters ) {
+ WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size, &audioRange ) );
+ if( result )
+ requested = MAX( requested, audioRange.mMinimum );
+ }
+ } else {
+ /* requested a realtively high latency. make sure this is in range of devices */
+ /*try to get the device's max natural buffer size and use that (but no larger than 1024).*/
+ AudioValueRange audioRange;
+ size_t size = sizeof( audioRange );
+ requested = MIN( requested, 1024 );
+ if( inputParameters ) {
+ WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size, &audioRange ) );
+ if( result )
+ requested = MIN( requested, audioRange.mMaximum );
+ }
+ if( outputParameters ) {
+ WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size, &audioRange ) );
+ if( result )
+ requested = MIN( requested, audioRange.mMaximum );
+ }
+ }
+ /* -- double check ranges -- */
+ if( requested > 1024 ) requested = 1024;
+ if( requested < 64 ) requested = 64;
+ VDBUG(("After querying hardware, setting block size to %ld.\n", requested));
+ framesPerBuffer = requested;
+ }
+
+ /* -- Now we actually open and setup streams. -- */
+ if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
+ { /* full duplex. One device. */
+ result = OpenAndSetupOneAudioUnit( inputParameters,
+ outputParameters,
+ framesPerBuffer,
+ &(stream->inputFramesPerBuffer),
+ &(stream->outputFramesPerBuffer),
+ auhalHostApi,
+ &(stream->inputUnit),
+ &(stream->inputSRConverter),
+ &(stream->inputDevice),
+ sampleRate,
+ stream );
+ stream->outputUnit = stream->inputUnit;
+ stream->outputDevice = stream->inputDevice;
+ if( result != paNoError )
+ goto error;
+ }
+ else
+ { /* full duplex, different devices OR simplex */
+ result = OpenAndSetupOneAudioUnit( NULL,
+ outputParameters,
+ framesPerBuffer,
+ NULL,
+ &(stream->outputFramesPerBuffer),
+ auhalHostApi,
+ &(stream->outputUnit),
+ NULL,
+ &(stream->outputDevice),
+ sampleRate,
+ stream );
+ if( result != paNoError )
+ goto error;
+ result = OpenAndSetupOneAudioUnit( inputParameters,
+ NULL,
+ framesPerBuffer,
+ &(stream->inputFramesPerBuffer),
+ NULL,
+ auhalHostApi,
+ &(stream->inputUnit),
+ &(stream->inputSRConverter),
+ &(stream->inputDevice),
+ sampleRate,
+ stream );
+ if( result != paNoError )
+ goto error;
+ }
+
+ if( stream->inputUnit ) {
+ const size_t szfl = sizeof(float);
+ /* setup the AudioBufferList used for input */
+ bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) );
+ stream->inputAudioBufferList.mNumberBuffers = 1;
+ stream->inputAudioBufferList.mBuffers[0].mNumberChannels
+ = inputChannelCount;
+ stream->inputAudioBufferList.mBuffers[0].mDataByteSize
+ = stream->inputFramesPerBuffer*inputChannelCount*szfl;
+ stream->inputAudioBufferList.mBuffers[0].mData
+ = (float *) calloc(
+ stream->inputFramesPerBuffer*inputChannelCount,
+ szfl );
+ if( !stream->inputAudioBufferList.mBuffers[0].mData )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /*
+ * If input and output devs are different or we are doing SR conversion,
+ * we also need a
+ * ring buffer to store inpt data while waiting for output
+ * data.
+ */
+ if( (stream->outputUnit && stream->inputUnit != stream->outputUnit)
+ || stream->inputSRConverter )
+ {
+ /* May want the ringSize ot initial position in
+ ring buffer to depend somewhat on sample rate change */
+
+ void *data;
+ long ringSize;
+
+ ringSize = computeRingBufferSize( inputParameters,
+ outputParameters,
+ stream->inputFramesPerBuffer,
+ stream->outputFramesPerBuffer,
+ sampleRate );
+ /*ringSize <<= 4; *//*16x bigger, for testing */
+
+
+ /*now, we need to allocate memory for the ring buffer*/
+ data = calloc( ringSize, szfl );
+ if( !data )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* now we can initialize the ring buffer */
+ assert( 0 ==
+ RingBuffer_Init( &stream->inputRingBuffer,
+ ringSize*szfl, data ) );
+ /* advance the read point a little, so we are reading from the
+ middle of the buffer */
+ if( stream->outputUnit )
+ RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR );
+ }
+ }
+
+ /* -- initialize Blio Buffer Processors -- */
+ if( !streamCallback )
+ {
+ long ringSize;
+
+ ringSize = computeRingBufferSize( inputParameters,
+ outputParameters,
+ stream->inputFramesPerBuffer,
+ stream->outputFramesPerBuffer,
+ sampleRate );
+ result = initializeBlioRingBuffers( &stream->blio,
+ inputParameters?inputParameters->sampleFormat:0 ,
+ outputParameters?outputParameters->sampleFormat:0 ,
+ MAX(stream->inputFramesPerBuffer,stream->outputFramesPerBuffer),
+ ringSize,
+ inputParameters?inputChannelCount:0 ,
+ outputParameters?outputChannelCount:0 ) ;
+ if( result != paNoError )
+ goto error;
+ }
+
+ /* -- initialize Buffer Processor -- */
+ {
+ unsigned long maxHostFrames = stream->inputFramesPerBuffer;
+ if( stream->outputFramesPerBuffer > maxHostFrames )
+ maxHostFrames = stream->outputFramesPerBuffer;
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat,
+ hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat,
+ hostOutputSampleFormat,
+ sampleRate,
+ streamFlags,
+ framesPerBuffer,
+ /* If sample rate conversion takes place, the buffer size
+ will not be known. */
+ maxHostFrames,
+ stream->inputSRConverter
+ ? paUtilUnknownHostBufferSize
+ : paUtilBoundedHostBufferSize,
+ streamCallback ? streamCallback : BlioCallback,
+ streamCallback ? userData : &stream->blio );
+ if( result != paNoError )
+ goto error;
+ }
+ stream->bufferProcessorIsInitialized = TRUE;
+
+ /*
+ IMPLEMENT ME: initialise the following fields with estimated or actual
+ values.
+ I think this is okay the way it is br 12/1/05
+ maybe need to change input latency estimate if IO devs differ
+ */
+ stream->streamRepresentation.streamInfo.inputLatency =
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate;
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate;
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ stream->sampleRate = sampleRate;
+ stream->outDeviceSampleRate = 0;
+ if( stream->outputUnit ) {
+ Float64 rate;
+ UInt32 size = sizeof( rate );
+ result = ERR( AudioDeviceGetProperty( stream->outputDevice,
+ 0,
+ FALSE,
+ kAudioDevicePropertyNominalSampleRate,
+ &size, &rate ) );
+ if( result )
+ goto error;
+ stream->outDeviceSampleRate = rate;
+ }
+ stream->inDeviceSampleRate = 0;
+ if( stream->inputUnit ) {
+ Float64 rate;
+ UInt32 size = sizeof( rate );
+ result = ERR( AudioDeviceGetProperty( stream->inputDevice,
+ 0,
+ TRUE,
+ kAudioDevicePropertyNominalSampleRate,
+ &size, &rate ) );
+ if( result )
+ goto error;
+ stream->inDeviceSampleRate = rate;
+ }
+ stream->userInChan = inputChannelCount;
+ stream->userOutChan = outputChannelCount;
+
+ stream->isTimeSet = FALSE;
+ stream->state = STOPPED;
+ stream->xrunFlags = 0;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ CloseStream( stream );
+ return result;
}
-static PaError AbortStream( PaStream *s )
+PaTime GetStreamTime( PaStream *s )
{
- PaError err = paNoError;
+ /* FIXME: I am not at all sure this timing info stuff is right.
+ patest_sine_time reports negative latencies, which is wierd.*/
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));
+ AudioTimeStamp timeStamp;
+
+ VVDBUG(("GetStreamTime()\n"));
+
+ if ( !stream->isTimeSet )
+ return (PaTime)0;
+
+ if ( stream->outputDevice ) {
+ AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp);
+ return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->outDeviceSampleRate;
+ } else if ( stream->inputDevice ) {
+ AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp);
+ return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->inDeviceSampleRate;
+ } else {
+ return (PaTime)0;
}
-
- stream->isActive = 0;
- stream->isStopped = 1;
- return err;
-}
+}
-static PaError StopStream( PaStream *s )
+static void setStreamStartTime( PaStream *stream )
{
- // TODO: this should be nicer than abort
- return AbortStream(s);
+ /* FIXME: I am not at all sure this timing info stuff is right.
+ patest_sine_time reports negative latencies, which is wierd.*/
+ PaMacCoreStream *s = (PaMacCoreStream *) stream;
+ VVDBUG(("setStreamStartTime()\n"));
+ if( s->outputDevice )
+ AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime);
+ else if( s->inputDevice )
+ AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime);
+ else
+ bzero( &s->startTime, sizeof( s->startTime ) );
+
+ //FIXME: we need a memory barier here
+
+ s->isTimeSet = TRUE;
}
-static PaError IsStreamStopped( PaStream *s )
+
+static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp)
{
- PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
- return stream->isStopped;
+ VVDBUG(("TimeStampToSecs()\n"));
+ //printf( "ATS: %lu, %g, %g\n", timeStamp->mFlags, timeStamp->mSampleTime, timeStamp->mRateScalar );
+ if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid)
+ return (timeStamp->mSampleTime / stream->sampleRate);
+ else
+ return 0;
}
+#define RING_BUFFER_EMPTY (1000)
-static PaError IsStreamActive( PaStream *s )
+static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
+ UInt32*ioDataSize,
+ void** outData,
+ void*inUserData )
{
- PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ void *dummyData;
+ long dummySize;
+ RingBuffer *rb = (RingBuffer *) inUserData;
+
+ VVDBUG(("ringBufferIOProc()\n"));
+
+ assert( sizeof( UInt32 ) == sizeof( long ) );
+ if( RingBuffer_GetReadAvailable( rb ) == 0 ) {
+ *outData = NULL;
+ *ioDataSize = 0;
+ return RING_BUFFER_EMPTY;
+ }
+ RingBuffer_GetReadRegions( rb, *ioDataSize,
+ outData, (long *)ioDataSize,
+ &dummyData, &dummySize );
+
+ assert( *ioDataSize );
+ RingBuffer_AdvanceReadIndex( rb, *ioDataSize );
+
+ return noErr;
+}
- return stream->isActive;
+/*
+ * Called by the AudioUnit API to process audio from the sound card.
+ * This is where the magic happens.
+ */
+/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */
+static OSStatus AudioIOProc( void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData )
+{
+ unsigned long framesProcessed = 0;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0};
+ PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
+ const bool isRender = inBusNumber == OUTPUT_ELEMENT;
+ int callbackResult = paContinue ;
+
+ VVDBUG(("AudioIOProc()\n"));
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /* -----------------------------------------------------------------*\
+ This output may be useful for debugging,
+ But printing durring the callback is a bad enough idea that
+ this is not enabled by enableing the usual debugging calls.
+ \* -----------------------------------------------------------------*/
+ /*
+ static int renderCount = 0;
+ static int inputCount = 0;
+ printf( "------------------- starting reder/input\n" );
+ if( isRender )
+ printf("Render callback (%d):\t", ++renderCount);
+ else
+ printf("Input callback (%d):\t", ++inputCount);
+ printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount );
+
+ printf( "--- inBusNumber: %lu\n", inBusNumber );
+ printf( "--- inNumberFrames: %lu\n", inNumberFrames );
+ printf( "--- %x ioData\n", (unsigned) ioData );
+ if( ioData )
+ {
+ int i=0;
+ printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers );
+ for( i=0; i<ioData->mNumberBuffers; ++i )
+ printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize );
+ }
+ ----------------------------------------------------------------- */
+
+ if( !stream->isTimeSet )
+ setStreamStartTime( stream );
+
+ if( isRender ) {
+ AudioTimeStamp currentTime;
+ timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp);
+ AudioDeviceGetCurrentTime(stream->outputDevice, &currentTime);
+ timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
+ }
+ if( isRender && stream->inputUnit == stream->outputUnit )
+ timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
+ if( !isRender ) {
+ AudioTimeStamp currentTime;
+ timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
+ AudioDeviceGetCurrentTime(stream->inputDevice, &currentTime);
+ timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
+ }
+
+ //printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime );
+
+ if( isRender && stream->inputUnit == stream->outputUnit
+ && !stream->inputSRConverter )
+ {
+ /* --------- Full Duplex, One Device, no SR Conversion -------
+ *
+ * This is the lowest latency case, and also the simplest.
+ * Input data and output data are available at the same time.
+ * we do not use the input SR converter or the input ring buffer.
+ *
+ */
+ OSErr err = 0;
+ unsigned long frames;
+
+ /* -- start processing -- */
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ /* -- compute frames. do some checks -- */
+ assert( ioData->mNumberBuffers == 1 );
+ assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
+ frames = ioData->mBuffers[0].mDataByteSize;
+ frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
+ /* -- copy and process input data -- */
+ err= AudioUnitRender(stream->inputUnit,
+ ioActionFlags,
+ inTimeStamp,
+ INPUT_ELEMENT,
+ inNumberFrames,
+ &stream->inputAudioBufferList );
+ /* FEEDBACK: I'm not sure what to do when this call fails */
+ assert( !err );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ stream->inputAudioBufferList.mBuffers[0].mNumberChannels);
+ /* -- Copy and process output data -- */
+ PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
+ 0,
+ ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mNumberChannels);
+ /* -- complete processing -- */
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ else if( isRender )
+ {
+ /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion)
+ * -- OR Simplex Output
+ *
+ * This case handles output data as in the full duplex case,
+ * and, if there is input data, reads it off the ring buffer
+ * and into the PA buffer processor. If sample rate conversion
+ * is required on input, that is done here as well.
+ */
+ unsigned long frames;
+
+ /* Sometimes, when stopping a duplex stream we get erroneous
+ xrun flags, so if this is our last run, clear the flags. */
+ int xrunFlags = stream->xrunFlags;
+/*
+ if( xrunFlags & paInputUnderflow )
+ printf( "input underflow.\n" );
+ if( xrunFlags & paInputOverflow )
+ printf( "input overflow.\n" );
+*/
+ if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED )
+ xrunFlags = 0;
+
+ /* -- start processing -- */
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ xrunFlags );
+ stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */
+
+ /* -- Copy and process output data -- */
+ assert( ioData->mNumberBuffers == 1 );
+ frames = ioData->mBuffers[0].mDataByteSize;
+ frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
+ assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
+ PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
+ 0,
+ ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mNumberChannels);
+
+ /* -- copy and process input data, and complete processing -- */
+ if( stream->inputUnit ) {
+ const int flsz = sizeof( float );
+ /* Here, we read the data out of the ring buffer, through the
+ audio converter. */
+ int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels;
+ if( stream->inputSRConverter )
+ {
+ OSStatus err;
+ UInt32 size;
+ float data[ inChan * frames ];
+ size = sizeof( data );
+ err = AudioConverterFillBuffer(
+ stream->inputSRConverter,
+ ringBufferIOProc,
+ &stream->inputRingBuffer,
+ &size,
+ (void *)&data );
+ if( err == RING_BUFFER_EMPTY )
+ { /*the ring buffer callback underflowed */
+ err = 0;
+ bzero( ((char *)data) + size, sizeof(data)-size );
+ stream->xrunFlags |= paInputUnderflow;
+ }
+ ERR( err );
+ assert( !err );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ else
+ {
+ /* Without the AudioConverter is actually a bit more complex
+ because we have to do a little buffer processing that the
+ AudioConverter would otherwise handle for us. */
+ void *data1, *data2;
+ long size1, size2;
+ RingBuffer_GetReadRegions( &stream->inputRingBuffer,
+ inChan*frames*flsz,
+ &data1, &size1,
+ &data2, &size2 );
+ if( size1 / ( flsz * inChan ) == frames ) {
+ /* simplest case: all in first buffer */
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data1,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1 );
+ } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
+ /*we underflowed. take what data we can, zero the rest.*/
+ float data[frames*inChan];
+ if( size1 )
+ memcpy( data, data1, size1 );
+ if( size2 )
+ memcpy( data+size1, data2, size2 );
+ bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex( &stream->inputRingBuffer,
+ size1+size2 );
+ /* flag underflow */
+ stream->xrunFlags |= paInputUnderflow;
+ } else {
+ /*we got all the data, but split between buffers*/
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor),
+ size1 / ( flsz * inChan ) );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data1,
+ inChan );
+ PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor),
+ size2 / ( flsz * inChan ) );
+ PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data2,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1+size2 );
+ }
+ }
+ } else {
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+
+ }
+ else
+ {
+ /* ------------------ Input
+ *
+ * First, we read off the audio data and put it in the ring buffer.
+ * if this is an input-only stream, we need to process it more,
+ * otherwise, we let the output case deal with it.
+ */
+ OSErr err = 0;
+ int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
+ /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
+ do {
+ err= AudioUnitRender(stream->inputUnit,
+ ioActionFlags,
+ inTimeStamp,
+ INPUT_ELEMENT,
+ inNumberFrames,
+ &stream->inputAudioBufferList );
+ if( err == -10874 )
+ inNumberFrames /= 2;
+ } while( err == -10874 && inNumberFrames > 1 );
+ /* FEEDBACK: I'm not sure what to do when this call fails */
+ ERR( err );
+ assert( !err );
+ if( stream->inputSRConverter || stream->outputUnit )
+ {
+ /* If this is duplex or we use a converter, put the data
+ into the ring buffer. */
+ long bytesIn, bytesOut;
+ bytesIn = sizeof( float ) * inNumberFrames * chan;
+ bytesOut = RingBuffer_Write( &stream->inputRingBuffer,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ bytesIn );
+ if( bytesIn != bytesOut )
+ stream->xrunFlags |= paInputOverflow ;
+ }
+ else
+ {
+ /* for simplex input w/o SR conversion,
+ just pop the data into the buffer processor.*/
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames);
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ chan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ if( !stream->outputUnit && stream->inputSRConverter )
+ {
+ /* ------------------ Simplex Input w/ SR Conversion
+ *
+ * if this is a simplex input stream, we need to read off the buffer,
+ * do our sample rate conversion and pass the results to the buffer
+ * processor.
+ * The logic here is complicated somewhat by the fact that we don't
+ * know how much data is available, so we loop on reasonably sized
+ * chunks, and let the BufferProcessor deal with the rest.
+ *
+ */
+ /*This might be too big or small depending on SR conversion*/
+ float data[ chan * inNumberFrames ];
+ OSStatus err;
+ do
+ { /*Run the buffer processor until we are out of data*/
+ UInt32 size;
+ long f;
+
+ size = sizeof( data );
+ err = AudioConverterFillBuffer(
+ stream->inputSRConverter,
+ ringBufferIOProc,
+ &stream->inputRingBuffer,
+ &size,
+ (void *)data );
+ if( err != RING_BUFFER_EMPTY )
+ ERR( err );
+ assert( err == 0 || err == RING_BUFFER_EMPTY );
+
+ f = size / ( chan * sizeof(float) );
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
+ if( f )
+ {
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ chan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ } while( callbackResult == paContinue && !err );
+ }
+ }
+
+ switch( callbackResult )
+ {
+ case paContinue: break;
+ case paComplete:
+ case paAbort:
+ stream->isTimeSet = FALSE;
+ stream->state = CALLBACK_STOPPED ;
+ if( stream->outputUnit )
+ AudioOutputUnitStop(stream->outputUnit);
+ if( stream->inputUnit )
+ AudioOutputUnitStop(stream->inputUnit);
+ break;
+ }
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+ return noErr;
}
-static PaTime GetStreamTime( PaStream *s )
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
{
- OSStatus err;
- PaTime result;
+ /* This may be called from a failed OpenStream.
+ Therefore, each piece of info is treated seperately. */
+ PaError result = paNoError;
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);
+ VVDBUG(("CloseStream()\n"));
+ VDBUG( ( "Closing stream.\n" ) );
+
+ if( stream ) {
+ if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
+ AudioUnitUninitialize( stream->outputUnit );
+ CloseComponent( stream->outputUnit );
+ }
+ stream->outputUnit = NULL;
+ if( stream->inputUnit )
+ {
+ AudioUnitUninitialize( stream->inputUnit );
+ CloseComponent( stream->inputUnit );
+ stream->inputUnit = NULL;
+ }
+ if( stream->inputRingBuffer.buffer )
+ free( (void *) stream->inputRingBuffer.buffer );
+ stream->inputRingBuffer.buffer = NULL;
+ /*TODO: is there more that needs to be done on error
+ from AudioConverterDispose?*/
+ if( stream->inputSRConverter )
+ ERR( AudioConverterDispose( stream->inputSRConverter ) );
+ stream->inputSRConverter = NULL;
+ if( stream->inputAudioBufferList.mBuffers[0].mData )
+ free( stream->inputAudioBufferList.mBuffers[0].mData );
+ stream->inputAudioBufferList.mBuffers[0].mData = NULL;
+
+ result = destroyBlioRingBuffers( &stream->blio );
+ if( result )
+ return result;
+ if( stream->bufferProcessorIsInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
}
-
- result = err ? 0 : timeStamp->mSampleTime;
- PaUtil_FreeMemory(timeStamp);
return result;
}
-static double GetStreamCpuLoad( PaStream* s )
+static PaError StartStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
+ OSErr result = noErr;
+ VVDBUG(("StartStream()\n"));
+ VDBUG( ( "Starting stream.\n" ) );
+#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
-// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.
+ /*FIXME: maybe want to do this on close/abort for faster start? */
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+ if( stream->inputSRConverter )
+ ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) );
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- return paInternalError;
+ /* -- start -- */
+ stream->state = ACTIVE;
+ if( stream->inputUnit ) {
+ ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) );
+ }
+ if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
+ ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) );
+ }
+
+ //setStreamStartTime( stream );
+ //stream->isTimeSet = TRUE;
+
+ return paNoError;
+#undef ERR_WRAP
}
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
+static PaError StopStream( PaStream *s )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ OSErr result = noErr;
+ PaError paErr;
+ VVDBUG(("StopStream()\n"));
+
+ VDBUG( ("Waiting for BLIO.\n") );
+ waitUntilBlioWriteBufferIsFlushed( &stream->blio );
+ VDBUG( ( "Stopping stream.\n" ) );
+
+ stream->isTimeSet = FALSE;
+ stream->state = STOPPING;
+
+#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
+ /* -- stop and reset -- */
+ if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
+ {
+ ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
+ ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
+ ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
+ }
+ else
+ {
+ if( stream->inputUnit )
+ {
+ ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
+ ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
+ }
+ if( stream->outputUnit )
+ {
+ ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
+ ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
+ }
+ }
+ if( stream->inputRingBuffer.buffer ) {
+ RingBuffer_Flush( &stream->inputRingBuffer );
+ bzero( (void *)stream->inputRingBuffer.buffer,
+ stream->inputRingBuffer.bufferSize );
+ /* advance the write point a little, so we are reading from the
+ middle of the buffer. We'll need extra at the end because
+ testing has shown that this helps. */
+ if( stream->outputUnit )
+ RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer,
+ stream->inputRingBuffer.bufferSize
+ / RING_BUFFER_ADVANCE_DENOMINATOR );
+ }
+
+ stream->xrunFlags = 0;
+ stream->state = STOPPED;
+
+ paErr = resetBlioRingBuffers( &stream->blio );
+ if( paErr )
+ return paErr;
+
+/*
+ //stream->isTimeSet = FALSE;
+*/
+
+ VDBUG( ( "Stream Stopped.\n" ) );
+ return paNoError;
+#undef ERR_WRAP
+}
+
+static PaError AbortStream( PaStream *s )
+{
+ VVDBUG(("AbortStream()->StopStream()\n"));
+ VDBUG( ( "Aborting stream.\n" ) );
+ /* We have nothing faster than StopStream. */
+ return StopStream(s);
}
-static signed long GetStreamReadAvailable( PaStream* s )
+static PaError IsStreamStopped( PaStream *s )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("IsStreamStopped()\n"));
+
+ return stream->state == STOPPED ? 1 : 0;
}
-static signed long GetStreamWriteAvailable( PaStream* s )
+static PaError IsStreamActive( PaStream *s )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("IsStreamActive()\n"));
+ return ( stream->state == ACTIVE || stream->state == STOPPING );
}
-// HostAPI-specific initialization function
-PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+
+static double GetStreamCpuLoad( PaStream* s )
{
- 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";
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("GetStreamCpuLoad()\n"));
- 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;
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core.h b/pjmedia/src/pjmedia/portaudio/pa_mac_core.h
new file mode 100644
index 00000000..f810e2f6
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core.h
@@ -0,0 +1,118 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Macintosh Core Audio specific extensions
+ * portaudio.h should be included before this file.
+ *
+ * Copyright (c) 2005-2006 Bjorn Roche
+ *
+ * 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.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * A pointer to a paMacCoreStreamInfo may be passed as
+ * the hostApiSpecificStreamInfo in the PaStreamParameters struct
+ * when opening a stream or querying the format. Use NULL, for the
+ * defaults. Note that for duplex streams, both infos should be the
+ * same or behaviour is undefined.
+ */
+typedef struct paMacCoreStreamInfo
+{
+ unsigned long size; /**size of whole structure including this header */
+ PaHostApiTypeId hostApiType;/**host API for which this data is intended */
+ unsigned long version; /**structure version */
+ unsigned long flags; /* flags to modify behaviour */
+} paMacCoreStreamInfo;
+
+/* Use this function to initialize a paMacCoreStreamInfo struct
+ using the requested flags. */
+void paSetupMacCoreStreamInfo( paMacCoreStreamInfo *data, unsigned long flags )
+{
+ bzero( data, sizeof( paMacCoreStreamInfo ) );
+ data->size = sizeof( paMacCoreStreamInfo );
+ data->hostApiType = paCoreAudio;
+ data->version = 0x01;
+ data->flags = flags;
+}
+
+/*
+ * The following flags alter the behaviour of PA on the mac platform.
+ * they can be ORed together. These should work both for opening and
+ * checking a device.
+ */
+/* Allows PortAudio to change things like the device's frame size,
+ * which allows for much lower latency, but might disrupt the device
+ * if other programs are using it, even when you are just Querying
+ * the device. */
+const unsigned long paMacCore_ChangeDeviceParameters = 0x01;
+
+/* In combination with the above flag,
+ * causes the stream opening to fail, unless the exact sample rates
+ * are supported by the device. */
+const unsigned long paMacCore_FailIfConversionRequired = 0x02;
+
+/* These flags set the SR conversion quality, if required. The wierd ordering
+ * allows Maximum Quality to be the default.*/
+const unsigned long paMacCore_ConversionQualityMin = 0x0100;
+const unsigned long paMacCore_ConversionQualityMedium = 0x0200;
+const unsigned long paMacCore_ConversionQualityLow = 0x0300;
+const unsigned long paMacCore_ConversionQualityHigh = 0x0400;
+const unsigned long paMacCore_ConversionQualityMax = 0x0000;
+
+/*
+ * Here are some "preset" combinations of flags (above) to get to some
+ * common configurations. THIS IS OVERKILL, but if more flags are added
+ * it won't be.
+ */
+/*This is the default setting: do as much sample rate conversion as possible
+ * and as little mucking with the device as possible. */
+const unsigned long paMacCorePlayNice = 0x00;
+/*This setting is tuned for pro audio apps. It allows SR conversion on input
+ and output, but it tries to set the appropriate SR on the device.*/
+const unsigned long paMacCorePro = 0x01;
+/*This is a setting to minimize CPU usage and still play nice.*/
+const unsigned long paMacCoreMinimizeCPUButPlayNice = 0x0100;
+/*This is a setting to minimize CPU usage, even if that means interrupting the device. */
+const unsigned long paMacCoreMinimizeCPU = 0x0101;
+
+#ifdef PA_OLD_CORE_AUDIO
+# define kAudioOutputUnitProperty_EnableIO 2003
+# define kAudioOutputUnitProperty_SetInputCallback 2005
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.c b/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.c
new file mode 100644
index 00000000..f474e7b0
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.c
@@ -0,0 +1,564 @@
+/*
+ * Implementation of the PortAudio API for Apple AUHAL
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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 hostaip_src
+
+ This file contains the implementation
+ required for blocking I/O. It is separated from pa_mac_core.c simply to ease
+ development.
+*/
+
+#include "pa_mac_core_blocking.h"
+#include "pa_mac_core_internal.h"
+#include <assert.h>
+#ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS
+# define OSAtomicOr32( a, b ) ( (*(b)) |= (a) )
+# define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) )
+#else
+# include <libkern/OSAtomic.h>
+#endif
+
+/*
+ * This fnuction determines the size of a particular sample format.
+ * if the format is not recognized, this returns zero.
+ */
+static size_t computeSampleSizeFromFormat( PaSampleFormat format )
+{
+ switch( format ) {
+ case paFloat32: return 4;
+ case paInt32: return 4;
+ case paInt24: return 3;
+ case paInt16: return 2;
+ case paInt8: case paUInt8: return 1;
+ default: return 0;
+ }
+}
+
+
+/*
+ * Functions for initializing, resetting, and destroying BLIO structures.
+ *
+ */
+
+/* This should be called with the relevant info when initializing a stream for
+ callback. */
+PaError initializeBlioRingBuffers(
+ PaMacBlio *blio,
+ PaSampleFormat inputSampleFormat,
+ PaSampleFormat outputSampleFormat,
+ size_t framesPerBuffer,
+ long ringBufferSize,
+ int inChan,
+ int outChan )
+{
+ void *data;
+ int result;
+
+ /* zeroify things */
+ bzero( blio, sizeof( PaMacBlio ) );
+ /* this is redundant, but the buffers are used to check
+ if the bufffers have been initialized, so we do it explicitly. */
+ blio->inputRingBuffer.buffer = NULL;
+ blio->outputRingBuffer.buffer = NULL;
+
+ /* initialize simple data */
+ blio->inputSampleFormat = inputSampleFormat;
+ blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
+ blio->outputSampleFormat = outputSampleFormat;
+ blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
+ blio->framesPerBuffer = framesPerBuffer;
+ blio->inChan = inChan;
+ blio->outChan = outChan;
+ blio->statusFlags = 0;
+ blio->errors = paNoError;
+#ifdef PA_MAC_BLIO_MUTEX
+ blio->isInputEmpty = false;
+ blio->isOutputFull = false;
+#endif
+
+ /* setup ring buffers */
+#ifdef PA_MAC_BLIO_MUTEX
+ result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 );
+ if( result )
+ goto error;
+ result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) );
+ if( result )
+ goto error;
+ result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) );
+ if( result )
+ goto error;
+ result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
+#endif
+ if( inChan ) {
+ data = calloc( ringBufferSize, blio->inputSampleSize );
+ if( !data )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ assert( 0 == RingBuffer_Init(
+ &blio->inputRingBuffer,
+ ringBufferSize*blio->inputSampleSize,
+ data ) );
+ }
+ if( outChan ) {
+ data = calloc( ringBufferSize, blio->outputSampleSize );
+ if( !data )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ assert( 0 == RingBuffer_Init(
+ &blio->outputRingBuffer,
+ ringBufferSize*blio->outputSampleSize,
+ data ) );
+ }
+
+ result = resetBlioRingBuffers( blio );
+ if( result )
+ goto error;
+
+ return 0;
+
+ error:
+ destroyBlioRingBuffers( blio );
+ return result;
+}
+
+#ifdef PA_MAC_BLIO_MUTEX
+PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty )
+{
+ PaError result = paNoError;
+ if( isEmpty == blio->isInputEmpty )
+ goto done;
+
+ /* we need to update the value. Here's what we do:
+ * - Lock the mutex, so noone else can write.
+ * - update the value.
+ * - unlock.
+ * - broadcast to all listeners.
+ */
+ result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
+ if( result )
+ goto done;
+ blio->isInputEmpty = isEmpty;
+ result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
+ if( result )
+ goto done;
+ result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) );
+ if( result )
+ goto done;
+
+ done:
+ return result;
+}
+PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull )
+{
+ PaError result = paNoError;
+ if( isFull == blio->isOutputFull )
+ goto done;
+
+ /* we need to update the value. Here's what we do:
+ * - Lock the mutex, so noone else can write.
+ * - update the value.
+ * - unlock.
+ * - broadcast to all listeners.
+ */
+ result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
+ if( result )
+ goto done;
+ blio->isOutputFull = isFull;
+ result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
+ if( result )
+ goto done;
+ result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) );
+ if( result )
+ goto done;
+
+ done:
+ return result;
+}
+#endif
+
+/* This should be called after stopping or aborting the stream, so that on next
+ start, the buffers will be ready. */
+PaError resetBlioRingBuffers( PaMacBlio *blio )
+{
+#ifdef PA_MAC__BLIO_MUTEX
+ int result;
+#endif
+ blio->statusFlags = 0;
+ if( blio->outputRingBuffer.buffer ) {
+ RingBuffer_Flush( &blio->outputRingBuffer );
+ bzero( blio->outputRingBuffer.buffer,
+ blio->outputRingBuffer.bufferSize );
+ /* Advance buffer */
+ RingBuffer_AdvanceWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
+
+ /* Update isOutputFull. */
+#ifdef PA_MAC__BLIO_MUTEX
+ result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize );
+ if( result )
+ goto error;
+#endif
+/*
+ printf( "------%d\n" , blio->framesPerBuffer );
+ printf( "------%d\n" , blio->outChan );
+ printf( "------%d\n" , blio->outputSampleSize );
+ printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize );
+*/
+ }
+ if( blio->inputRingBuffer.buffer ) {
+ RingBuffer_Flush( &blio->inputRingBuffer );
+ bzero( blio->inputRingBuffer.buffer,
+ blio->inputRingBuffer.bufferSize );
+ /* Update isInputEmpty. */
+#ifdef PA_MAC__BLIO_MUTEX
+ result = blioSetIsInputEmpty( blio, true );
+ if( result )
+ goto error;
+#endif
+ }
+ return paNoError;
+#ifdef PA_MAC__BLIO_MUTEX
+ error:
+ return result;
+#endif
+}
+
+/*This should be called when you are done with the blio. It can safely be called
+ multiple times if there are no exceptions. */
+PaError destroyBlioRingBuffers( PaMacBlio *blio )
+{
+ PaError result = paNoError;
+ if( blio->inputRingBuffer.buffer ) {
+ free( blio->inputRingBuffer.buffer );
+#ifdef PA_MAC__BLIO_MUTEX
+ result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) );
+ if( result ) return result;
+ result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) );
+ if( result ) return result;
+#endif
+ }
+ blio->inputRingBuffer.buffer = NULL;
+ if( blio->outputRingBuffer.buffer ) {
+ free( blio->outputRingBuffer.buffer );
+#ifdef PA_MAC__BLIO_MUTEX
+ result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) );
+ if( result ) return result;
+ result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) );
+ if( result ) return result;
+#endif
+ }
+ blio->outputRingBuffer.buffer = NULL;
+
+ return result;
+}
+
+/*
+ * this is the BlioCallback function. It expects to recieve a PaMacBlio Object
+ * pointer as userData.
+ *
+ */
+int BlioCallback( const void *input, void *output, unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ PaMacBlio *blio = (PaMacBlio*)userData;
+ long avail;
+ long toRead;
+ long toWrite;
+
+ /* set flags returned by OS: */
+ OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
+
+ /* --- Handle Input Buffer --- */
+ if( blio->inChan ) {
+ avail = RingBuffer_GetWriteAvailable( &blio->inputRingBuffer );
+
+ /* check for underflow */
+ if( avail < frameCount * blio->inputSampleSize * blio->inChan )
+ OSAtomicOr32( paInputOverflow, &blio->statusFlags );
+
+ toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
+
+ /* copy the data */
+ /*printf( "reading %d\n", toRead );*/
+ assert( toRead == RingBuffer_Write( &blio->inputRingBuffer, input, toRead ) );
+#ifdef PA_MAC__BLIO_MUTEX
+ /* Priority inversion. See notes below. */
+ blioSetIsInputEmpty( blio, false );
+#endif
+ }
+
+
+ /* --- Handle Output Buffer --- */
+ if( blio->outChan ) {
+ avail = RingBuffer_GetReadAvailable( &blio->outputRingBuffer );
+
+ /* check for underflow */
+ if( avail < frameCount * blio->outputSampleSize * blio->outChan )
+ OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
+
+ toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
+
+ if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
+ bzero( ((char *)output)+toWrite,
+ frameCount * blio->outputSampleSize * blio->outChan - toWrite );
+ /* copy the data */
+ /*printf( "writing %d\n", toWrite );*/
+ assert( toWrite == RingBuffer_Read( &blio->outputRingBuffer, output, toWrite ) );
+#ifdef PA_MAC__BLIO_MUTEX
+ /* We have a priority inversion here. However, we will only have to
+ wait if this was true and is now false, which means we've got
+ some room in the buffer.
+ Hopefully problems will be minimized. */
+ blioSetIsOutputFull( blio, false );
+#endif
+ }
+
+ return paContinue;
+}
+
+PaError ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames )
+{
+ PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
+ char *cbuf = (char *) buffer;
+ PaError ret = paNoError;
+ VVDBUG(("ReadStream()\n"));
+
+ while( frames > 0 ) {
+ long avail;
+ long toRead;
+ do {
+ avail = RingBuffer_GetReadAvailable( &blio->inputRingBuffer );
+/*
+ printf( "Read Buffer is %%%g full: %ld of %ld.\n",
+ 100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
+ avail, blio->inputRingBuffer.bufferSize );
+*/
+ if( avail == 0 ) {
+#ifdef PA_MAC_BLIO_MUTEX
+ /**block when empty*/
+ ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
+ if( ret )
+ return ret;
+ while( blio->isInputEmpty ) {
+ ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) );
+ if( ret )
+ return ret;
+ }
+ ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
+ if( ret )
+ return ret;
+#else
+ Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
+#endif
+ }
+ } while( avail == 0 );
+ toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
+ toRead -= toRead % blio->inputSampleSize * blio->inChan ;
+ RingBuffer_Read( &blio->inputRingBuffer, (void *)cbuf, toRead );
+ cbuf += toRead;
+ frames -= toRead / ( blio->inputSampleSize * blio->inChan );
+
+ if( toRead == avail ) {
+#ifdef PA_MAC_BLIO_MUTEX
+ /* we just emptied the buffer, so we need to mark it as empty. */
+ ret = blioSetIsInputEmpty( blio, true );
+ if( ret )
+ return ret;
+ /* of course, in the meantime, the callback may have put some sats
+ in, so
+ so check for that, too, to avoid a race condition. */
+ if( RingBuffer_GetReadAvailable( &blio->inputRingBuffer ) ) {
+ blioSetIsInputEmpty( blio, false );
+ if( ret )
+ return ret;
+ }
+#endif
+ }
+ }
+
+ /* Report either paNoError or paInputOverflowed. */
+ /* may also want to report other errors, but this is non-standard. */
+ ret = blio->statusFlags & paInputOverflow;
+
+ /* report underflow only once: */
+ if( ret ) {
+ OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags );
+ ret = paInputOverflowed;
+ }
+
+ return ret;
+}
+
+
+PaError WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
+ char *cbuf = (char *) buffer;
+ PaError ret = paNoError;
+ VVDBUG(("WriteStream()\n"));
+
+ while( frames > 0 ) {
+ long avail = 0;
+ long toWrite;
+
+ do {
+ avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
+/*
+ printf( "Write Buffer is %%%g full: %ld of %ld.\n",
+ 100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize,
+ avail, blio->outputRingBuffer.bufferSize );
+*/
+ if( avail == 0 ) {
+#ifdef PA_MAC_BLIO_MUTEX
+ /*block while full*/
+ ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
+ if( ret )
+ return ret;
+ while( blio->isOutputFull ) {
+ ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) );
+ if( ret )
+ return ret;
+ }
+ ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
+ if( ret )
+ return ret;
+#else
+ Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
+#endif
+ }
+ } while( avail == 0 );
+
+ toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
+ toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
+ RingBuffer_Write( &blio->outputRingBuffer, (void *)cbuf, toWrite );
+ cbuf += toWrite;
+ frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
+
+#ifdef PA_MAC_BLIO_MUTEX
+ if( toWrite == avail ) {
+ /* we just filled up the buffer, so we need to mark it as filled. */
+ ret = blioSetIsOutputFull( blio, true );
+ if( ret )
+ return ret;
+ /* of course, in the meantime, we may have emptied the buffer, so
+ so check for that, too, to avoid a race condition. */
+ if( RingBuffer_GetWriteAvailable( &blio->outputRingBuffer ) ) {
+ blioSetIsOutputFull( blio, false );
+ if( ret )
+ return ret;
+ }
+ }
+#endif
+ }
+
+ /* Report either paNoError or paOutputUnderflowed. */
+ /* may also want to report other errors, but this is non-standard. */
+ ret = blio->statusFlags & paOutputUnderflow;
+
+ /* report underflow only once: */
+ if( ret ) {
+ OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags );
+ ret = paOutputUnderflowed;
+ }
+
+ return ret;
+}
+
+/*
+ *
+ */
+void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
+{
+ if( blio->outputRingBuffer.buffer ) {
+ long avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
+ while( avail != blio->outputRingBuffer.bufferSize ) {
+ if( avail == 0 )
+ Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
+ avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
+ }
+ }
+}
+
+
+signed long GetStreamReadAvailable( PaStream* stream )
+{
+ PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
+ VVDBUG(("GetStreamReadAvailable()\n"));
+
+ return RingBuffer_GetReadAvailable( &blio->inputRingBuffer )
+ / ( blio->outputSampleSize * blio->outChan );
+}
+
+
+signed long GetStreamWriteAvailable( PaStream* stream )
+{
+ PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
+ VVDBUG(("GetStreamWriteAvailable()\n"));
+
+ return RingBuffer_GetWriteAvailable( &blio->outputRingBuffer )
+ / ( blio->outputSampleSize * blio->outChan );
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.h b/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.h
new file mode 100644
index 00000000..8185e203
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_blocking.h
@@ -0,0 +1,133 @@
+/*
+ * Internal blocking interfaces for PortAudio Apple AUHAL implementation
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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 hostaip_src
+*/
+
+#ifndef PA_MAC_CORE_BLOCKING_H_
+#define PA_MAC_CORE_BLOCKING_H_
+
+#include "ringbuffer.h"
+#include "portaudio.h"
+#include "pa_mac_core_utilities.h"
+
+/*
+ * Number of miliseconds to busy wait whil waiting for data in blocking calls.
+ */
+#define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5)
+/*
+ * Define exactly one of these blocking methods
+ * PA_MAC_BLIO_MUTEX is not actively maintained.
+ */
+#define PA_MAC_BLIO_BUSY_WAIT
+/*
+#define PA_MAC_BLIO_MUTEX
+*/
+
+typedef struct {
+ RingBuffer inputRingBuffer;
+ RingBuffer outputRingBuffer;
+ PaSampleFormat inputSampleFormat;
+ size_t inputSampleSize;
+ PaSampleFormat outputSampleFormat;
+ size_t outputSampleSize;
+
+ size_t framesPerBuffer;
+
+ int inChan;
+ int outChan;
+
+ //PaStreamCallbackFlags statusFlags;
+ uint32_t statusFlags;
+ PaError errors;
+
+ /* Here we handle blocking, using condition variables. */
+#ifdef PA_MAC_BLIO_MUTEX
+ volatile bool isInputEmpty;
+ pthread_mutex_t inputMutex;
+ pthread_cond_t inputCond;
+
+ volatile bool isOutputFull;
+ pthread_mutex_t outputMutex;
+ pthread_cond_t outputCond;
+#endif
+}
+PaMacBlio;
+
+/*
+ * These functions operate on condition and related variables.
+ */
+
+PaError initializeBlioRingBuffers(
+ PaMacBlio *blio,
+ PaSampleFormat inputSampleFormat,
+ PaSampleFormat outputSampleFormat,
+ size_t framesPerBuffer,
+ long ringBufferSize,
+ int inChan,
+ int outChan );
+PaError destroyBlioRingBuffers( PaMacBlio *blio );
+PaError resetBlioRingBuffers( PaMacBlio *blio );
+
+int BlioCallback(
+ const void *input, void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData );
+
+void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio );
+
+#endif /*PA_MAC_CORE_BLOCKING_H_*/
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_internal.h b/pjmedia/src/pjmedia/portaudio/pa_mac_core_internal.h
new file mode 100644
index 00000000..095b3b38
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_internal.h
@@ -0,0 +1,162 @@
+/*
+ * Internal interfaces for PortAudio Apple AUHAL implementation
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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 pa_mac_core
+ @ingroup hostapi_src
+ @author Bjorn Roche
+ @brief AUHAL implementation of PortAudio
+*/
+
+#ifndef PA_MAC_CORE_INTERNAL_H__
+#define PA_MAC_CORE_INTERNAL_H__
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_allocation.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "ringbuffer.h"
+
+#include "pa_mac_core_blocking.h"
+
+/* function prototypes */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
+
+PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+signed long GetStreamReadAvailable( PaStream* stream );
+signed long GetStreamWriteAvailable( PaStream* stream );
+/* PaMacAUHAL - host api datastructure specific to this implementation */
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ /* implementation specific data goes here */
+ long devCount;
+ AudioDeviceID *devIds; /*array of all audio devices*/
+ AudioDeviceID defaultIn;
+ AudioDeviceID defaultOut;
+}
+PaMacAUHAL;
+
+
+
+/* stream data structure specifically for this implementation */
+typedef struct PaMacCoreStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ /* implementation specific data goes here */
+ bool bufferProcessorIsInitialized;
+ AudioUnit inputUnit;
+ AudioUnit outputUnit;
+ AudioDeviceID inputDevice;
+ AudioDeviceID outputDevice;
+ size_t userInChan;
+ size_t userOutChan;
+ size_t inputFramesPerBuffer;
+ size_t outputFramesPerBuffer;
+ PaMacBlio blio;
+ /* We use this ring buffer when input and out devs are different. */
+ RingBuffer inputRingBuffer;
+ /* We may need to do SR conversion on input. */
+ AudioConverterRef inputSRConverter;
+ /* We need to preallocate an inputBuffer for reading data. */
+ AudioBufferList inputAudioBufferList;
+ AudioTimeStamp startTime;
+ volatile PaStreamCallbackFlags xrunFlags;
+ volatile bool isTimeSet;
+ volatile enum {
+ STOPPED = 0, /* playback is completely stopped,
+ and the user has called StopStream(). */
+ CALLBACK_STOPPED = 1, /* callback has requested stop,
+ but user has not yet called StopStream(). */
+ STOPPING = 2, /* The stream is in the process of closing.
+ This state is just used internally;
+ externally it is indistinguishable from
+ ACTIVE.*/
+ ACTIVE = 3 /* The stream is active and running. */
+ } state;
+ double sampleRate;
+ //these may be different from the stream sample rate due to SR conversion:
+ double outDeviceSampleRate;
+ double inDeviceSampleRate;
+}
+PaMacCoreStream;
+
+#endif /* PA_MAC_CORE_INTERNAL_H__ */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_old.c b/pjmedia/src/pjmedia/portaudio/pa_mac_core_old.c
new file mode 100644
index 00000000..acb02c4a
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_old.c
@@ -0,0 +1,914 @@
+/*
+ * $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.
+ *
+ * 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.
+ */
+
+#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;
+ //bennylp:
+ //case kAudioHardwareUnsupportedOperationError:
+ // result = paInternalError; break;
+ case kAudioDeviceUnsupportedFormatError:
+ result = paSampleFormatNotSupported; break;
+ case kAudioDevicePermissionsError:
+ result = paDeviceUnavailable; break;
+ default:
+ result = paInternalError;
+ }
+
+ return result;
+}
+
+/* This function is unused
+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;
+ }
+ return noErr;
+}
+
+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;
+ }
+ return noErr;
+}
+
+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);
+ }
+
+ PaUtil_FreeMemory( timeInfo );
+ return noErr;
+}
+
+// This is not for input-only streams, this is for streams where the input device is different from the output device
+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);
+ PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+
+ PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+ if( result == paComplete || result == paAbort )
+ Pa_StopStream(clientData->stream);
+ PaUtil_FreeMemory( timeInfo );
+ return noErr;
+}
+
+// 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 );
+ return noErr;
+}
+
+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;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.c b/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.c
new file mode 100644
index 00000000..5a0f8b8d
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.c
@@ -0,0 +1,612 @@
+/*
+ * Helper and utility functions for pa_mac_core.c (Apple AUHAL implementation)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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 hostapi_src
+*/
+
+#include "pa_mac_core_utilities.h"
+
+
+PaError PaMacCore_SetUnixError( int err, int line )
+{
+ PaError ret;
+ const char *errorText;
+
+ if( err == 0 )
+ {
+ return paNoError;
+ }
+
+ ret = paNoError;
+ errorText = strerror( err );
+
+ /** Map Unix error to PaError. Pretty much the only one that maps
+ is ENOMEM. */
+ if( err == ENOMEM )
+ ret = paInsufficientMemory;
+ else
+ ret = paInternalError;
+
+ DBUG(("%d on line %d: msg='%s'\n", err, line, errorText));
+ PaUtil_SetLastHostErrorInfo( paCoreAudio, err, errorText );
+
+ return ret;
+}
+
+/*
+ * Translates MacOS generated errors into PaErrors
+ */
+PaError PaMacCore_SetError(OSStatus error, int line, int isError)
+{
+ /*FIXME: still need to handle possible ComponentResult values.*/
+ /* unfortunately, they don't seem to be documented anywhere.*/
+ PaError result;
+ const char *errorType;
+ const char *errorText;
+
+ switch (error) {
+ case kAudioHardwareNoError:
+ return paNoError;
+ case kAudioHardwareNotRunningError:
+ errorText = "Audio Hardware Not Running";
+ result = paInternalError; break;
+ case kAudioHardwareUnspecifiedError:
+ errorText = "Unspecified Audio Hardware Error";
+ result = paInternalError; break;
+ case kAudioHardwareUnknownPropertyError:
+ errorText = "Audio Hardware: Unknown Property";
+ result = paInternalError; break;
+ case kAudioHardwareBadPropertySizeError:
+ errorText = "Audio Hardware: Bad Property Size";
+ result = paInternalError; break;
+ case kAudioHardwareIllegalOperationError:
+ errorText = "Audio Hardware: Illegal Operation";
+ result = paInternalError; break;
+ case kAudioHardwareBadDeviceError:
+ errorText = "Audio Hardware: Bad Device";
+ result = paInvalidDevice; break;
+ case kAudioHardwareBadStreamError:
+ errorText = "Audio Hardware: BadStream";
+ result = paBadStreamPtr; break;
+ case kAudioHardwareUnsupportedOperationError:
+ errorText = "Audio Hardware: Unsupported Operation";
+ result = paInternalError; break;
+ case kAudioDeviceUnsupportedFormatError:
+ errorText = "Audio Device: Unsupported Format";
+ result = paSampleFormatNotSupported; break;
+ case kAudioDevicePermissionsError:
+ errorText = "Audio Device: Permissions Error";
+ result = paDeviceUnavailable; break;
+ /* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */
+ case kAudioUnitErr_InvalidProperty:
+ errorText = "Audio Unit: Invalid Property";
+ result = paInternalError; break;
+ case kAudioUnitErr_InvalidParameter:
+ errorText = "Audio Unit: Invalid Parameter";
+ result = paInternalError; break;
+ case kAudioUnitErr_NoConnection:
+ errorText = "Audio Unit: No Connection";
+ result = paInternalError; break;
+ case kAudioUnitErr_FailedInitialization:
+ errorText = "Audio Unit: Initialization Failed";
+ result = paInternalError; break;
+ case kAudioUnitErr_TooManyFramesToProcess:
+ errorText = "Audio Unit: Too Many Frames";
+ result = paInternalError; break;
+ case kAudioUnitErr_IllegalInstrument:
+ errorText = "Audio Unit: Illegal Instrument";
+ result = paInternalError; break;
+ case kAudioUnitErr_InstrumentTypeNotFound:
+ errorText = "Audio Unit: Instrument Type Not Found";
+ result = paInternalError; break;
+ case kAudioUnitErr_InvalidFile:
+ errorText = "Audio Unit: Invalid File";
+ result = paInternalError; break;
+ case kAudioUnitErr_UnknownFileType:
+ errorText = "Audio Unit: Unknown File Type";
+ result = paInternalError; break;
+ case kAudioUnitErr_FileNotSpecified:
+ errorText = "Audio Unit: File Not Specified";
+ result = paInternalError; break;
+ case kAudioUnitErr_FormatNotSupported:
+ errorText = "Audio Unit: Format Not Supported";
+ result = paInternalError; break;
+ case kAudioUnitErr_Uninitialized:
+ errorText = "Audio Unit: Unitialized";
+ result = paInternalError; break;
+ case kAudioUnitErr_InvalidScope:
+ errorText = "Audio Unit: Invalid Scope";
+ result = paInternalError; break;
+ case kAudioUnitErr_PropertyNotWritable:
+ errorText = "Audio Unit: PropertyNotWritable";
+ result = paInternalError; break;
+ case kAudioUnitErr_InvalidPropertyValue:
+ errorText = "Audio Unit: Invalid Property Value";
+ result = paInternalError; break;
+ case kAudioUnitErr_PropertyNotInUse:
+ errorText = "Audio Unit: Property Not In Use";
+ result = paInternalError; break;
+ case kAudioUnitErr_Initialized:
+ errorText = "Audio Unit: Initialized";
+ result = paInternalError; break;
+ case kAudioUnitErr_InvalidOfflineRender:
+ errorText = "Audio Unit: Invalid Offline Render";
+ result = paInternalError; break;
+ case kAudioUnitErr_Unauthorized:
+ errorText = "Audio Unit: Unauthorized";
+ result = paInternalError; break;
+ case kAudioUnitErr_CannotDoInCurrentContext:
+ errorText = "Audio Unit: cannot do in current context";
+ result = paInternalError; break;
+ default:
+ errorText = "Unknown Error";
+ result = paInternalError;
+ }
+
+ if (isError)
+ errorType = "Error";
+ else
+ errorType = "Warning";
+
+ if ((int)error < -99999 || (int)error > 99999)
+ DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
+ else
+ DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
+
+ PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
+
+ return result;
+}
+
+/*
+ * This function computes an appropriate ring buffer size given
+ * a requested latency (in seconds), sample rate and framesPerBuffer.
+ *
+ * The returned ringBufferSize is computed using the following
+ * constraints:
+ * - it must be at least 4.
+ * - it must be at least 3x framesPerBuffer.
+ * - it must be at least 2x the suggestedLatency.
+ * - it must be a power of 2.
+ * This function attempts to compute the minimum such size.
+ *
+ * FEEDBACK: too liberal/conservative/another way?
+ */
+long computeRingBufferSize( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ long inputFramesPerBuffer,
+ long outputFramesPerBuffer,
+ double sampleRate )
+{
+ long ringSize;
+ int index;
+ int i;
+ double latencyTimesChannelCount ;
+ long framesPerBufferTimesChannelCount ;
+
+ VVDBUG(( "computeRingBufferSize()\n" ));
+
+ assert( inputParameters || outputParameters );
+
+ if( outputParameters && inputParameters )
+ {
+ latencyTimesChannelCount = MAX(
+ inputParameters->suggestedLatency * inputParameters->channelCount,
+ outputParameters->suggestedLatency * outputParameters->channelCount );
+ framesPerBufferTimesChannelCount = MAX(
+ inputFramesPerBuffer * inputParameters->channelCount,
+ outputFramesPerBuffer * outputParameters->channelCount );
+ }
+ else if( outputParameters )
+ {
+ latencyTimesChannelCount
+ = outputParameters->suggestedLatency * outputParameters->channelCount;
+ framesPerBufferTimesChannelCount
+ = outputFramesPerBuffer * outputParameters->channelCount;
+ }
+ else /* we have inputParameters */
+ {
+ latencyTimesChannelCount
+ = inputParameters->suggestedLatency * inputParameters->channelCount;
+ framesPerBufferTimesChannelCount
+ = inputFramesPerBuffer * inputParameters->channelCount;
+ }
+
+ ringSize = (long) ( latencyTimesChannelCount * sampleRate * 2 + .5);
+ VDBUG( ( "suggested latency * channelCount: %d\n", (int) (latencyTimesChannelCount*sampleRate) ) );
+ if( ringSize < framesPerBufferTimesChannelCount * 3 )
+ ringSize = framesPerBufferTimesChannelCount * 3 ;
+ VDBUG(("framesPerBuffer*channelCount:%d\n",(int)framesPerBufferTimesChannelCount));
+ VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize ));
+
+ /* make sure it's at least 4 */
+ ringSize = MAX( ringSize, 4 );
+
+ /* round up to the next power of 2 */
+ index = -1;
+ for( i=0; i<sizeof(long)*8; ++i )
+ if( ringSize >> i & 0x01 )
+ index = i;
+ assert( index > 0 );
+ if( ringSize <= ( 0x01 << index ) )
+ ringSize = 0x01 << index ;
+ else
+ ringSize = 0x01 << ( index + 1 );
+
+ VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize ));
+ return ringSize;
+}
+
+
+/*
+ * Durring testing of core audio, I found that serious crashes could occur
+ * if properties such as sample rate were changed multiple times in rapid
+ * succession. The function below has some fancy logic to make sure that changes
+ * are acknowledged before another is requested. That seems to help a lot.
+ */
+
+OSStatus propertyProc(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData )
+{
+ MutexAndBool *mab = (MutexAndBool *) inClientData;
+ mab->once = TRUE;
+ pthread_mutex_unlock( &(mab->mutex) );
+ return noErr;
+}
+
+/* sets the value of the given property and waits for the change to
+ be acknowledged, and returns the final value, which is not guaranteed
+ by this function to be the same as the desired value. Obviously, this
+ function can only be used for data whose input and output are the
+ same size and format, and their size and format are known in advance.*/
+PaError AudioDeviceSetPropertyNowAndWaitForChange(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ UInt32 inPropertyDataSize,
+ const void *inPropertyData,
+ void *outPropertyData )
+{
+ OSStatus macErr;
+ int unixErr;
+ MutexAndBool mab;
+ UInt32 outPropertyDataSize = inPropertyDataSize;
+
+ /* First, see if it already has that value. If so, return. */
+ macErr = AudioDeviceGetProperty( inDevice, inChannel,
+ isInput, inPropertyID,
+ &outPropertyDataSize, outPropertyData );
+ if( macErr )
+ goto failMac2;
+ if( inPropertyDataSize!=outPropertyDataSize )
+ return paInternalError;
+ if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) )
+ return paNoError;
+
+ /* setup and lock mutex */
+ mab.once = FALSE;
+ unixErr = pthread_mutex_init( &mab.mutex, NULL );
+ if( unixErr )
+ goto failUnix2;
+ unixErr = pthread_mutex_lock( &mab.mutex );
+ if( unixErr )
+ goto failUnix;
+
+ /* add property listener */
+ macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc,
+ &mab );
+ if( macErr )
+ goto failMac;
+ /* set property */
+ macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel,
+ isInput, inPropertyID,
+ inPropertyDataSize, inPropertyData );
+ if( macErr ) {
+ /* we couldn't set the property, so we'll just unlock the mutex
+ and move on. */
+ pthread_mutex_unlock( &mab.mutex );
+ }
+
+ /* wait for property to change */
+ unixErr = pthread_mutex_lock( &mab.mutex );
+ if( unixErr )
+ goto failUnix;
+
+ /* now read the property back out */
+ macErr = AudioDeviceGetProperty( inDevice, inChannel,
+ isInput, inPropertyID,
+ &outPropertyDataSize, outPropertyData );
+ if( macErr )
+ goto failMac;
+ /* cleanup */
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+ unixErr = pthread_mutex_unlock( &mab.mutex );
+ if( unixErr )
+ goto failUnix2;
+ unixErr = pthread_mutex_destroy( &mab.mutex );
+ if( unixErr )
+ goto failUnix2;
+
+ return paNoError;
+
+ failUnix:
+ pthread_mutex_destroy( &mab.mutex );
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+
+ failUnix2:
+ DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) );
+ return paUnanticipatedHostError;
+
+ failMac:
+ pthread_mutex_destroy( &mab.mutex );
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+ failMac2:
+ return ERR( macErr );
+}
+
+/*
+ * Sets the sample rate the HAL device.
+ * if requireExact: set the sample rate or fail.
+ *
+ * otherwise : set the exact sample rate.
+ * If that fails, check for available sample rates, and choose one
+ * higher than the requested rate. If there isn't a higher one,
+ * just use the highest available.
+ */
+PaError setBestSampleRateForDevice( const AudioDeviceID device,
+ const bool isOutput,
+ const bool requireExact,
+ const Float64 desiredSrate )
+{
+ /*FIXME: changing the sample rate is disruptive to other programs using the
+ device, so it might be good to offer a custom flag to not change the
+ sample rate and just do conversion. (in my casual tests, there is
+ no disruption unless the sample rate really does need to change) */
+ const bool isInput = isOutput ? 0 : 1;
+ Float64 srate;
+ UInt32 propsize = sizeof( Float64 );
+ OSErr err;
+ AudioValueRange *ranges;
+ int i=0;
+ Float64 max = -1; /*the maximum rate available*/
+ Float64 best = -1; /*the lowest sample rate still greater than desired rate*/
+ VDBUG(("Setting sample rate for device %ld to %g.\n",device,(float)desiredSrate));
+
+ /* -- try setting the sample rate -- */
+ err = AudioDeviceSetPropertyNowAndWaitForChange(
+ device, 0, isInput,
+ kAudioDevicePropertyNominalSampleRate,
+ propsize, &desiredSrate, &srate );
+ if( err )
+ return err;
+
+ /* -- if the rate agrees, and we got no errors, we are done -- */
+ if( !err && srate == desiredSrate )
+ return paNoError;
+ /* -- we've failed if the rates disagree and we are setting input -- */
+ if( requireExact )
+ return paInvalidSampleRate;
+
+ /* -- generate a list of available sample rates -- */
+ err = AudioDeviceGetPropertyInfo( device, 0, isInput,
+ kAudioDevicePropertyAvailableNominalSampleRates,
+ &propsize, NULL );
+ if( err )
+ return ERR( err );
+ ranges = (AudioValueRange *)calloc( 1, propsize );
+ if( !ranges )
+ return paInsufficientMemory;
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyAvailableNominalSampleRates,
+ &propsize, ranges );
+ if( err )
+ {
+ free( ranges );
+ return ERR( err );
+ }
+ VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate));
+ VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange)));
+#ifdef MAC_CORE_VERBOSE_DEBUG
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ VDBUG( ("\t%g-%g\n",
+ (float) ranges[i].mMinimum,
+ (float) ranges[i].mMaximum ) );
+#endif
+ VDBUG(("-----\n"));
+
+ /* -- now pick the best available sample rate -- */
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ {
+ if( ranges[i].mMaximum > max ) max = ranges[i].mMaximum;
+ if( ranges[i].mMinimum > desiredSrate ) {
+ if( best < 0 )
+ best = ranges[i].mMinimum;
+ else if( ranges[i].mMinimum < best )
+ best = ranges[i].mMinimum;
+ }
+ }
+ if( best < 0 )
+ best = max;
+ VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) );
+ free( ranges );
+
+ /* -- set the sample rate -- */
+ propsize = sizeof( best );
+ err = AudioDeviceSetPropertyNowAndWaitForChange(
+ device, 0, isInput,
+ kAudioDevicePropertyNominalSampleRate,
+ propsize, &best, &srate );
+ if( err )
+ return err;
+
+ if( err )
+ return ERR( err );
+ /* -- if the set rate matches, we are done -- */
+ if( srate == best )
+ return paNoError;
+
+ /* -- otherwise, something wierd happened: we didn't set the rate, and we got no errors. Just bail. */
+ return paInternalError;
+}
+
+
+/*
+ Attempts to set the requestedFramesPerBuffer. If it can't set the exact
+ value, it settles for something smaller if available. If nothing smaller
+ is available, it uses the smallest available size.
+ actualFramesPerBuffer will be set to the actual value on successful return.
+ OK to pass NULL to actualFramesPerBuffer.
+ The logic is very simmilar too setBestSampleRate only failure here is
+ not usually catastrophic.
+*/
+PaError setBestFramesPerBuffer( const AudioDeviceID device,
+ const bool isOutput,
+ unsigned long requestedFramesPerBuffer,
+ unsigned long *actualFramesPerBuffer )
+{
+ UInt32 afpb;
+ const bool isInput = !isOutput;
+ UInt32 propsize = sizeof(UInt32);
+ OSErr err;
+ Float64 min = -1; /*the min blocksize*/
+ Float64 best = -1; /*the best blocksize*/
+ int i=0;
+ AudioValueRange *ranges;
+
+ if( actualFramesPerBuffer == NULL )
+ actualFramesPerBuffer = &afpb;
+
+
+ /* -- try and set exact FPB -- */
+ err = AudioDeviceSetProperty( device, NULL, 0, isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ propsize, &requestedFramesPerBuffer);
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ &propsize, actualFramesPerBuffer);
+ if( err )
+ return ERR( err );
+ if( *actualFramesPerBuffer == requestedFramesPerBuffer )
+ return paNoError; /* we are done */
+
+ /* -- fetch available block sizes -- */
+ err = AudioDeviceGetPropertyInfo( device, 0, isInput,
+ kAudioDevicePropertyBufferSizeRange,
+ &propsize, NULL );
+ if( err )
+ return ERR( err );
+ ranges = (AudioValueRange *)calloc( 1, propsize );
+ if( !ranges )
+ return paInsufficientMemory;
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferSizeRange,
+ &propsize, ranges );
+ if( err )
+ {
+ free( ranges );
+ return ERR( err );
+ }
+ VDBUG(("Requested block size of %lu was not available.\n",
+ requestedFramesPerBuffer ));
+ VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange)));
+#ifdef MAC_CORE_VERBOSE_DEBUG
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ VDBUG( ("\t%g-%g\n",
+ (float) ranges[i].mMinimum,
+ (float) ranges[i].mMaximum ) );
+#endif
+ VDBUG(("-----\n"));
+
+ /* --- now pick the best available framesPerBuffer -- */
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ {
+ if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum;
+ if( ranges[i].mMaximum < requestedFramesPerBuffer ) {
+ if( best < 0 )
+ best = ranges[i].mMaximum;
+ else if( ranges[i].mMaximum > best )
+ best = ranges[i].mMaximum;
+ }
+ }
+ if( best == -1 )
+ best = min;
+ VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) );
+ free( ranges );
+
+ /* --- set the buffer size (ignore errors) -- */
+ requestedFramesPerBuffer = (UInt32) best ;
+ propsize = sizeof( UInt32 );
+ err = AudioDeviceSetProperty( device, NULL, 0, isInput,
+ kAudioDevicePropertyBufferSize,
+ propsize, &requestedFramesPerBuffer );
+ /* --- read the property to check that it was set -- */
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferSize,
+ &propsize, actualFramesPerBuffer );
+
+ if( err )
+ return ERR( err );
+
+ return paNoError;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.h b/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.h
new file mode 100644
index 00000000..63ef22a9
--- /dev/null
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_core_utilities.h
@@ -0,0 +1,215 @@
+/*
+ * Helper and utility functions for pa_mac_core.c (Apple AUHAL implementation)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * The following people also deserve acknowledgements:
+ *
+ * Olivier Tristan for feedback and testing
+ * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
+ * interface.
+ *
+ *
+ * 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 hostapi_src
+*/
+
+#ifndef PA_MAC_CORE_UTILITIES_H__
+#define PA_MAC_CORE_UTILITIES_H__
+
+#include <pthread.h>
+#include "portaudio.h"
+#include "pa_util.h"
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+#ifndef MIN
+#define MIN(a, b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a)<(b))?(b):(a))
+#endif
+
+#define ERR(mac_error) PaMacCore_SetError(mac_error, __LINE__, 1 )
+#define WARNING(mac_error) PaMacCore_SetError(mac_error, __LINE__, 0 )
+
+
+/* Help keep track of AUHAL element numbers */
+#define INPUT_ELEMENT (1)
+#define OUTPUT_ELEMENT (0)
+
+/* Normal level of debugging: fine for most apps that don't mind the occational warning being printf'ed */
+/*
+ */
+#define MAC_CORE_DEBUG
+#ifdef MAC_CORE_DEBUG
+# define DBUG(MSG) do { printf("||PaMacCore (AUHAL)|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define DBUG(MSG)
+#endif
+
+/* Verbose Debugging: useful for developement */
+/*
+#define MAC_CORE_VERBOSE_DEBUG
+*/
+#ifdef MAC_CORE_VERBOSE_DEBUG
+# define VDBUG(MSG) do { printf("||PaMacCore (v )|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define VDBUG(MSG)
+#endif
+
+/* Very Verbose Debugging: Traces every call. */
+/*
+#define MAC_CORE_VERY_VERBOSE_DEBUG
+ */
+#ifdef MAC_CORE_VERY_VERBOSE_DEBUG
+# define VVDBUG(MSG) do { printf("||PaMacCore (vv)|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define VVDBUG(MSG)
+#endif
+
+
+
+
+
+#define UNIX_ERR(err) PaMacCore_SetUnixError( err, __LINE__ )
+
+PaError PaMacCore_SetUnixError( int err, int line );
+
+/*
+ * Translates MacOS generated errors into PaErrors
+ */
+PaError PaMacCore_SetError(OSStatus error, int line, int isError);
+
+/*
+ * This function computes an appropriate ring buffer size given
+ * a requested latency (in seconds), sample rate and framesPerBuffer.
+ *
+ * The returned ringBufferSize is computed using the following
+ * constraints:
+ * - it must be at least 4.
+ * - it must be at least 3x framesPerBuffer.
+ * - it must be at least 2x the suggestedLatency.
+ * - it must be a power of 2.
+ * This function attempts to compute the minimum such size.
+ *
+ */
+long computeRingBufferSize( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ long inputFramesPerBuffer,
+ long outputFramesPerBuffer,
+ double sampleRate );
+
+/*
+ * Durring testing of core audio, I found that serious crashes could occur
+ * if properties such as sample rate were changed multiple times in rapid
+ * succession. The function below has some fancy logic to make sure that changes
+ * are acknowledged before another is requested. That seems to help a lot.
+ */
+
+typedef struct {
+ bool once; /* I didn't end up using this. bdr */
+ pthread_mutex_t mutex;
+} MutexAndBool ;
+
+OSStatus propertyProc(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData );
+
+/* sets the value of the given property and waits for the change to
+ be acknowledged, and returns the final value, which is not guaranteed
+ by this function to be the same as the desired value. Obviously, this
+ function can only be used for data whose input and output are the
+ same size and format, and their size and format are known in advance.*/
+PaError AudioDeviceSetPropertyNowAndWaitForChange(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ UInt32 inPropertyDataSize,
+ const void *inPropertyData,
+ void *outPropertyData );
+
+/*
+ * Sets the sample rate the HAL device.
+ * if requireExact: set the sample rate or fail.
+ *
+ * otherwise : set the exact sample rate.
+ * If that fails, check for available sample rates, and choose one
+ * higher than the requested rate. If there isn't a higher one,
+ * just use the highest available.
+ */
+PaError setBestSampleRateForDevice( const AudioDeviceID device,
+ const bool isOutput,
+ const bool requireExact,
+ const Float64 desiredSrate );
+/*
+ Attempts to set the requestedFramesPerBuffer. If it can't set the exact
+ value, it settles for something smaller if available. If nothing smaller
+ is available, it uses the smallest available size.
+ actualFramesPerBuffer will be set to the actual value on successful return.
+ OK to pass NULL to actualFramesPerBuffer.
+ The logic is very simmilar too setBestSampleRate only failure here is
+ not usually catastrophic.
+*/
+PaError setBestFramesPerBuffer( const AudioDeviceID device,
+ const bool isOutput,
+ unsigned long requestedFramesPerBuffer,
+ unsigned long *actualFramesPerBuffer );
+
+#ifdef PA_OLD_CORE_AUDIO
+# define kAudioUnitErr_PropertyNotInUse 10850
+# define kAudioHardwareUnsupportedOperationError 1
+# define kAudioUnitErr_Initialized 10849
+# define kAudioUnitErr_InvalidOfflineRender 10848
+# define kAudioUnitErr_Unauthorized 10847
+#endif
+
+#endif /* PA_MAC_CORE_UTILITIES_H__*/
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_mac_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_mac_hostapis.c
index f8306c30..89622d6b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_mac_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_mac_hostapis.c
@@ -1,6 +1,6 @@
/*
* $Id$
- * Portable Audio I/O Library Windows initialization table
+ * Portable Audio I/O Library Macintosh initialization table
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -16,10 +16,6 @@
* 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.
@@ -29,7 +25,19 @@
* 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 macosx_src
Mac OS host API initialization function table.
*/
diff --git a/pjmedia/src/pjmedia/portaudio/ringbuffer.c b/pjmedia/src/pjmedia/portaudio/ringbuffer.c
index 16031fef..c66ec613 100644
--- a/pjmedia/src/pjmedia/portaudio/ringbuffer.c
+++ b/pjmedia/src/pjmedia/portaudio/ringbuffer.c
@@ -4,9 +4,13 @@
* Ring Buffer utility..
*
* Author: Phil Burk, http://www.softsynth.com
+ * modified for SMP safety on Mac OS X by Bjorn Roche
+ * also, alowed for const where possible
+ * Note that this is safe only for a single-thread reader and a
+ * single-thread writer.
*
* This program uses the PortAudio Portable Audio Library.
- * For more information see: http://www.audiomulch.com/portaudio/
+ * For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
@@ -20,10 +24,6 @@
* 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.
@@ -31,14 +31,69 @@
* 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 hostapi_src
+*/
+
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ringbuffer.h"
#include <string.h>
+/*
+ * We can undefine this, to turn off memory barriers, but that
+ * is only useful if we know we don't need to be MP safe or
+ * we are interested in doing some kind of tests.
+ */
+#define MPSAFE
+
+/****************
+ * First, we'll define some memory barrier primitives based on the system.
+ * right now only OS X and FreeBSD are supported. In addition to providing
+ * memory barriers, these functions should ensure that data cached in registers
+ * is written out to cache where it can be snooped by other CPUs. (ie, the volatile
+ * keyword should not be required)
+ *
+ * the primitives that must be defined are:
+ *
+ * FullMemoryBarrier()
+ * ReadMemoryBarrier()
+ * WriteMemoryBarrier()
+ *
+ ****************/
+
+#if 1
+# define FullMemoryBarrier()
+# define ReadMemoryBarrier()
+# define WriteMemoryBarrier()
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+# include <libkern/OSAtomic.h>
+ /* Here are the memory barrier functions. Mac OS X and FreeBSD only provide
+ full memory barriers, so the three types of barriers are the same.
+ The asm volatile may be redundant with the memory barrier, but
+ until I have proof of that, I'm leaving it. */
+# define FullMemoryBarrier() do{ asm volatile("":::"memory"); OSMemoryBarrier(); }while(false)
+# define ReadMemoryBarrier() do{ asm volatile("":::"memory"); OSMemoryBarrier(); }while(false)
+# define WriteMemoryBarrier() do{ asm volatile("":::"memory"); OSMemoryBarrier(); }while(false)
+#else
+# error Memory Barriers not defined on this system or system unknown
+#endif
+
/***************************************************************************
* Initialize FIFO.
* numBytes must be power of 2, returns -1 if not.
@@ -57,12 +112,16 @@ long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
** Return number of bytes available for reading. */
long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
{
+#ifdef MPSAFE
+ ReadMemoryBarrier();
+#endif
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
}
/***************************************************************************
** Return number of bytes available for writing. */
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
{
+ /* Since we are calling RingBuffer_GetReadAvailable, we don't need an aditional MB */
return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
}
@@ -112,7 +171,13 @@ long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
*/
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
{
+#ifdef MPSAFE
+ /* we need to ensure that previous writes are seen before we update the write index */
+ WriteMemoryBarrier();
return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+#else
+ return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+#endif
}
/***************************************************************************
@@ -152,12 +217,18 @@ long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
*/
long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
{
+#ifdef MPSAFE
+ /* we need to ensure that previous writes are always seen before updating the index. */
+ WriteMemoryBarrier();
+ return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+#else
return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+#endif
}
/***************************************************************************
** Return bytes written. */
-long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+long RingBuffer_Write( RingBuffer *rbuf, const void *data, long numBytes )
{
long size1, size2, numWritten;
void *data1, *data2;
diff --git a/pjmedia/src/pjmedia/portaudio/ringbuffer.h b/pjmedia/src/pjmedia/portaudio/ringbuffer.h
index dd769bee..816520e3 100644
--- a/pjmedia/src/pjmedia/portaudio/ringbuffer.h
+++ b/pjmedia/src/pjmedia/portaudio/ringbuffer.h
@@ -11,9 +11,13 @@ extern "C"
* Ring Buffer utility..
*
* Author: Phil Burk, http://www.softsynth.com
+ * modified for SMP safety on OS X by Bjorn Roche.
+ * also allowed for const where possible.
+ * Note that this is safe only for a single-thread reader
+ * and a single-thread writer.
*
* This program is distributed with the PortAudio Portable Audio Library.
- * For more information see: http://www.audiomulch.com/portaudio/
+ * For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
@@ -27,10 +31,6 @@ extern "C"
* 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.
@@ -38,8 +38,24 @@ extern "C"
* 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 hostapi_src
+*/
+
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
@@ -53,7 +69,7 @@ typedef struct
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;
+ char * buffer;
}
RingBuffer;
/*
@@ -70,7 +86,7 @@ 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 );
+long RingBuffer_Write( RingBuffer *rbuf, const void *data, long numBytes );
/* Return bytes read. */
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );