Updated PortAudio to latest version for Mac

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@770 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2006-10-13 17:57:42 +00:00
parent e1a8badcde
commit 39ae2daf5e
15 changed files with 6348 additions and 6459 deletions

7111
aconfigure

File diff suppressed because it is too large Load Diff

View File

@ -243,14 +243,22 @@ AC_ARG_ENABLE(sound,
fi]
)
AC_SUBST(ac_pa_cflags)
if test "$enable_sound" = "no"; then
true;
else
case $target in
*darwin*)
ac_pjmedia_snd=pa_darwinos
AC_MSG_RESULT([Checking sound device backend... coreaudio])
LIBS="$LIBS -framework CoreAudio"
LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox"
if test "`uname -r`" = "6.8"; then
#ac_pa_cflags="-DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS"
#AC_MSG_RESULT([Setting additional PortAudio CFLAGS.. -DPA_OLD_CORE_AUDIO -DMOSX_USE_NON_ATOMIC_FLAG_BITS])
ac_pjmedia_snd=pa_old_darwinos
AC_MSG_RESULT([Checking sound device backend... old coreaudio])
else
ac_pjmedia_snd=pa_darwinos
AC_MSG_RESULT([Checking sound device backend... coreaudio])
fi
;;
*cygwin* | *mingw*)
ac_pjmedia_snd=pa_win32

View File

@ -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,15 +80,33 @@ 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)
#

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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 );
}

View File

@ -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_*/

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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__*/

View File

@ -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.
*/

View File

@ -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;

View File

@ -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 );