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:
parent
e1a8badcde
commit
39ae2daf5e
7111
aconfigure
7111
aconfigure
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
#
|
||||
|
|
|
@ -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
|
@ -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 */
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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_*/
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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__*/
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
Loading…
Reference in New Issue