📄 pa_mac_core.c
字号:
/*
* 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 pa_mac_core
@ingroup hostapi_src
@author Bjorn Roche
@brief AUHAL implementation of PortAudio
*/
/* FIXME: not all error conditions call PaUtil_SetLastHostErrorInfo()
* PaMacCore_SetError() will do this.
*/
#include "pa_mac_core_internal.h"
#include <string.h> /* strlen(), memcmp() etc. */
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
#include "pa_mac_core_blocking.h"
/* prototypes for functions declared in this file */
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
static PaError AbortStream( PaStream *stream );
static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static void setStreamStartTime( PaStream *stream );
static OSStatus AudioIOProc( void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData );
static double GetStreamCpuLoad( PaStream* stream );
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId,
int isInput);
static PaError OpenAndSetupOneAudioUnit(
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
const unsigned long requestedFramesPerBuffer,
unsigned long *actualInputFramesPerBuffer,
unsigned long *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
AudioDeviceID *audioDevice,
const double sampleRate,
void *refCon );
/* for setting errors. */
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
/*currently, this is only used in initialization, but it might be modified
to be used when the list of devices changes.*/
static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
{
UInt32 size;
UInt32 propsize;
VVDBUG(("gatherDeviceInfo()\n"));
/* -- free any previous allocations -- */
if( auhalHostApi->devIds )
PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
auhalHostApi->devIds = NULL;
/* -- figure out how many devices there are -- */
AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
&propsize,
NULL );
auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
/* -- copy the device IDs -- */
auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
auhalHostApi->allocations,
propsize );
if( !auhalHostApi->devIds )
return paInsufficientMemory;
AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
&propsize,
auhalHostApi->devIds );
#ifdef MAC_CORE_VERBOSE_DEBUG
{
int i;
for( i=0; i<auhalHostApi->devCount; ++i )
printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
}
#endif
size = sizeof(AudioDeviceID);
auhalHostApi->defaultIn = kAudioDeviceUnknown;
auhalHostApi->defaultOut = kAudioDeviceUnknown;
/* determine the default device. */
/* I am not sure how these calls to AudioHardwareGetProperty()
could fail, but in case they do, we use the first available
device as the default. */
if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
&size,
&auhalHostApi->defaultIn) ) {
int i;
auhalHostApi->defaultIn = kAudioDeviceUnknown;
VDBUG(("Failed to get default input device from OS."));
VDBUG((" I will substitute the first available input Device."));
for( i=0; i<auhalHostApi->devCount; ++i ) {
PaDeviceInfo devInfo;
if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
auhalHostApi->devIds[i], TRUE ) )
if( devInfo.maxInputChannels ) {
auhalHostApi->defaultIn = auhalHostApi->devIds[i];
break;
}
}
}
if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
&size,
&auhalHostApi->defaultOut) ) {
int i;
auhalHostApi->defaultIn = kAudioDeviceUnknown;
VDBUG(("Failed to get default output device from OS."));
VDBUG((" I will substitute the first available output Device."));
for( i=0; i<auhalHostApi->devCount; ++i ) {
PaDeviceInfo devInfo;
if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
auhalHostApi->devIds[i], FALSE ) )
if( devInfo.maxOutputChannels ) {
auhalHostApi->defaultOut = auhalHostApi->devIds[i];
break;
}
}
}
VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) );
VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
return paNoError;
}
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId,
int isInput)
{
UInt32 propSize;
PaError err = paNoError;
UInt32 i;
int numChannels = 0;
AudioBufferList *buflist = NULL;
UInt32 frameLatency;
VVDBUG(("GetChannelInfo()\n"));
/* Get the number of channels from the stream configuration.
Fail if we can't get this. */
err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
if (err)
return err;
buflist = PaUtil_AllocateMemory(propSize);
if( !buflist )
return paInsufficientMemory;
err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
if (err)
goto error;
for (i = 0; i < buflist->mNumberBuffers; ++i)
numChannels += buflist->mBuffers[i].mNumberChannels;
if (isInput)
deviceInfo->maxInputChannels = numChannels;
else
deviceInfo->maxOutputChannels = numChannels;
if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */
{
/* Get the latency. Don't fail if we can't get this. */
/* default to something reasonable */
deviceInfo->defaultLowInputLatency = .01;
deviceInfo->defaultHighInputLatency = .10;
deviceInfo->defaultLowOutputLatency = .01;
deviceInfo->defaultHighOutputLatency = .10;
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
if (!err)
{
/** FEEDBACK:
* This code was arrived at by trial and error, and some extentive, but not exhaustive
* testing. Sebastien Beaulieu <seb@plogue.com> has suggested using
* kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead.
* At the time this code was written, many users were reporting dropouts with audio
* programs that probably used this formula. This was probably
* around 10.4.4, and the problem is probably fixed now. So perhaps
* his formula should be reviewed and used.
* */
double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
if (isInput)
{
deviceInfo->defaultLowInputLatency = 3 * secondLatency;
deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency;
}
else
{
deviceInfo->defaultLowOutputLatency = 3 * secondLatency;
deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency;
}
}
}
PaUtil_FreeMemory( buflist );
return paNoError;
error:
PaUtil_FreeMemory( buflist );
return err;
}
static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId,
PaHostApiIndex hostApiIndex )
{
Float64 sampleRate;
char *name;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -