/************************************************************************
IMPORTANT NOTE : this file contains two clearly delimited sections :
the ARCHITECTURE section (in two parts) and the USER section. Each section
is governed by its own copyright and license. Please check individually
each section for license and copyright information.
*************************************************************************/
#ifndef __coreaudio_dsp__
#define __coreaudio_dsp__
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale
---------------------------------------------------------------------
This Architecture section is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; If not, see .
EXCEPTION : As a special exception, you may create a larger work
that contains this FAUST architecture section and distribute
that work under terms of your choice, so long as this FAUST
architecture section is not modified.
************************************************************************
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "audio.h"
#include "dsp.h"
using namespace std;
/******************************************************************************
*******************************************************************************
COREAUDIO INTERFACE
*******************************************************************************
*******************************************************************************/
//----------------------------------------------------------------------------
// number of physical input and output channels of the CA device
//----------------------------------------------------------------------------
int gDevNumInChans;
int gDevNumOutChans;
//----------------------------------------------------------------------------
// tables of noninterleaved input and output channels for FAUST
//----------------------------------------------------------------------------
float* gInChannel[256];
float* gOutChannel[256];
#define OPEN_ERR -1
#define NO_ERR 0
#define WAIT_COUNTER 60
typedef UInt8 CAAudioHardwareDeviceSectionID;
#define kAudioDeviceSectionInput ((CAAudioHardwareDeviceSectionID)0x01)
#define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00)
#define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00)
#define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF)
dsp * gDsp;
class TCoreAudioRenderer
{
private:
AudioBufferList* fInputData;
AudioDeviceID fDeviceID;
AudioUnit fAUHAL;
AudioObjectID fPluginID; // Used for aggregate device
bool fState;
OSStatus GetDefaultDevice(int inChan, int outChan, int samplerate, AudioDeviceID* id);
OSStatus CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, int samplerate, AudioDeviceID* outAggregateDevice);
OSStatus CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, int samplerate, AudioDeviceID* outAggregateDevice);
OSStatus DestroyAggregateDevice();
OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name);
int SetupSampleRateAux(AudioDeviceID inDevice, int samplerate);
static OSStatus Render(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
static OSStatus SRNotificationCallback(AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData);
public:
TCoreAudioRenderer()
:fInputData(0),fDeviceID(0),fAUHAL(0),fPluginID(0),fState(false)
{}
virtual ~TCoreAudioRenderer()
{}
long OpenDefault(long inChan, long outChan, long bufferSize, long sampleRate);
long Close();
long Start();
long Stop();
};
typedef TCoreAudioRenderer * TCoreAudioRendererPtr;
static void PrintStreamDesc(AudioStreamBasicDescription *inDesc)
{
cout << "- - - - - - - - - - - - - - - - - - - -" << endl;
cout << " Sample Rate: " << inDesc->mSampleRate << endl;
cout << " Format ID:%.*s\n" << sizeof(inDesc->mFormatID) << (char*)&inDesc->mFormatID << endl;
cout << " Format Flags " << inDesc->mFormatFlags << endl;
cout << " Bytes per Packet: " << inDesc->mBytesPerPacket << endl;
cout << " Frames per Packet: " << inDesc->mFramesPerPacket << endl;
cout << " Bytes per Frame: " << inDesc->mBytesPerFrame << endl;
cout << " Channels per Frame: "<< inDesc->mChannelsPerFrame << endl;
cout << " Bits per Channel: " << inDesc->mBitsPerChannel << endl;
cout << "- - - - - - - - - - - - - - - - - - - -" << endl;
}
static void printError(OSStatus err)
{
switch (err) {
case kAudioHardwareNoError:
printf("error code : kAudioHardwareNoError\n");
break;
case kAudioConverterErr_FormatNotSupported:
printf("error code : kAudioConverterErr_FormatNotSupported\n");
break;
case kAudioConverterErr_OperationNotSupported:
printf("error code : kAudioConverterErr_OperationNotSupported\n");
break;
case kAudioConverterErr_PropertyNotSupported:
printf("error code : kAudioConverterErr_PropertyNotSupported\n");
break;
case kAudioConverterErr_InvalidInputSize:
printf("error code : kAudioConverterErr_InvalidInputSize\n");
break;
case kAudioConverterErr_InvalidOutputSize:
printf("error code : kAudioConverterErr_InvalidOutputSize\n");
break;
case kAudioConverterErr_UnspecifiedError:
printf("error code : kAudioConverterErr_UnspecifiedError\n");
break;
case kAudioConverterErr_BadPropertySizeError:
printf("error code : kAudioConverterErr_BadPropertySizeError\n");
break;
case kAudioConverterErr_RequiresPacketDescriptionsError:
printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n");
break;
case kAudioConverterErr_InputSampleRateOutOfRange:
printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n");
break;
case kAudioConverterErr_OutputSampleRateOutOfRange:
printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n");
break;
case kAudioHardwareNotRunningError:
printf("error code : kAudioHardwareNotRunningError\n");
break;
case kAudioHardwareUnknownPropertyError:
printf("error code : kAudioHardwareUnknownPropertyError\n");
break;
case kAudioHardwareIllegalOperationError:
printf("error code : kAudioHardwareIllegalOperationError\n");
break;
case kAudioHardwareBadDeviceError:
printf("error code : kAudioHardwareBadDeviceError\n");
break;
case kAudioHardwareBadStreamError:
printf("error code : kAudioHardwareBadStreamError\n");
break;
case kAudioDeviceUnsupportedFormatError:
printf("error code : kAudioDeviceUnsupportedFormatError\n");
break;
case kAudioDevicePermissionsError:
printf("error code : kAudioDevicePermissionsError\n");
break;
default:
printf("error code : unknown\n");
break;
}
}
OSStatus TCoreAudioRenderer::Render(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
TCoreAudioRendererPtr renderer = (TCoreAudioRendererPtr)inRefCon;
AudioUnitRender(renderer->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, renderer->fInputData);
for (int i = 0; i < gDevNumInChans; i++) {
gInChannel[i] = (float*)renderer->fInputData->mBuffers[i].mData;
}
for (int i = 0; i < gDevNumOutChans; i++) {
gOutChannel[i] = (float*)ioData->mBuffers[i].mData;
}
gDsp->compute((int)inNumberFrames, gInChannel, gOutChannel);
return 0;
}
static CFStringRef GetDeviceName(AudioDeviceID id)
{
UInt32 size = sizeof(CFStringRef);
CFStringRef UIname;
OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
return (err == noErr) ? UIname : NULL;
}
OSStatus TCoreAudioRenderer::GetDeviceNameFromID(AudioDeviceID id, char* name)
{
UInt32 size = 256;
return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name);
}
OSStatus TCoreAudioRenderer::GetDefaultDevice(int inChan, int outChan, int samplerate, AudioDeviceID* id)
{
UInt32 theSize = sizeof(UInt32);
AudioDeviceID inDefault;
AudioDeviceID outDefault;
OSStatus res;
if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
&theSize, &inDefault)) != noErr)
return res;
if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
&theSize, &outDefault)) != noErr)
return res;
// Duplex mode
if (inChan > 0 && outChan > 0) {
// Get the device only if default input and output are the same
if (inDefault == outDefault) {
*id = inDefault;
return noErr;
} else {
printf("GetDefaultDevice : input = %ld and output = %ld are not the same, create aggregate device...\n", inDefault, outDefault);
if (CreateAggregateDevice(inDefault, outDefault, samplerate, id) != noErr)
return kAudioHardwareBadDeviceError;
}
} else if (inChan > 0) {
*id = inDefault;
return noErr;
} else if (outChan > 0) {
*id = outDefault;
return noErr;
} else {
return kAudioHardwareBadDeviceError;
}
return noErr;
}
OSStatus TCoreAudioRenderer::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, int samplerate, AudioDeviceID* outAggregateDevice)
{
OSStatus err = noErr;
AudioObjectID sub_device[32];
UInt32 outSize = sizeof(sub_device);
err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
vector captureDeviceIDArray;
if (err != noErr) {
printf("Input device does not have subdevices\n");
captureDeviceIDArray.push_back(captureDeviceID);
} else {
int num_devices = outSize / sizeof(AudioObjectID);
printf("Input device has %d subdevices\n", num_devices);
for (int i = 0; i < num_devices; i++) {
captureDeviceIDArray.push_back(sub_device[i]);
}
}
err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
vector playbackDeviceIDArray;
if (err != noErr) {
printf("Output device does not have subdevices\n");
playbackDeviceIDArray.push_back(playbackDeviceID);
} else {
int num_devices = outSize / sizeof(AudioObjectID);
printf("Output device has %d subdevices\n", num_devices);
for (int i = 0; i < num_devices; i++) {
playbackDeviceIDArray.push_back(sub_device[i]);
}
}
return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice);
}
OSStatus TCoreAudioRenderer::SRNotificationCallback(AudioDeviceID inDevice,
UInt32 /*inChannel*/,
Boolean /*isInput*/,
AudioDevicePropertyID inPropertyID,
void* inClientData)
{
TCoreAudioRenderer* driver = (TCoreAudioRenderer*)inClientData;
switch (inPropertyID) {
case kAudioDevicePropertyNominalSampleRate: {
printf("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate\n");
driver->fState = true;
// Check new sample rate
Float64 sampleRate;
UInt32 outSize = sizeof(Float64);
OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
if (err != noErr) {
printf("Cannot get current sample rate\n");
printError(err);
} else {
printf("SRNotificationCallback : checked sample rate = %f\n", sampleRate);
}
break;
}
}
return noErr;
}
int TCoreAudioRenderer::SetupSampleRateAux(AudioDeviceID inDevice, int samplerate)
{
OSStatus err = noErr;
UInt32 outSize;
Float64 sampleRate;
// Get sample rate
outSize = sizeof(Float64);
err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
if (err != noErr) {
printf("Cannot get current sample rate\n");
printError(err);
return -1;
} else {
printf("Current sample rate = %f\n", sampleRate);
}
// If needed, set new sample rate
if (samplerate != (int)sampleRate) {
sampleRate = (Float64)samplerate;
// To get SR change notification
err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this);
if (err != noErr) {
printf("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate\n");
printError(err);
return -1;
}
err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate);
if (err != noErr) {
printf("Cannot set sample rate = %d\n", samplerate);
printError(err);
return -1;
}
// Waiting for SR change notification
int count = 0;
while (!fState && count++ < WAIT_COUNTER) {
usleep(100000);
printf("Wait count = %d\n", count);
}
// Check new sample rate
outSize = sizeof(Float64);
err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
if (err != noErr) {
printf("Cannot get current sample rate\n");
printError(err);
} else {
printf("Checked sample rate = %f\n", sampleRate);
}
// Remove SR change notification
AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback);
}
return 0;
}
OSStatus TCoreAudioRenderer::CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, int samplerate, AudioDeviceID* outAggregateDevice)
{
OSStatus osErr = noErr;
UInt32 outSize;
Boolean outWritable;
bool fClockDriftCompensate = true;
// Prepare sub-devices for clock drift compensation
// Workaround for bug in the HAL : until 10.6.2
AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
UInt32 theQualifierDataSize = sizeof(AudioObjectID);
AudioClassID inClass = kAudioSubDeviceClassID;
void* theQualifierData = &inClass;
UInt32 subDevicesNum = 0;
//---------------------------------------------------------------------------
// Setup SR of both devices otherwise creating AD may fail...
//---------------------------------------------------------------------------
UInt32 keptclockdomain = 0;
UInt32 clockdomain = 0;
outSize = sizeof(UInt32);
bool need_clock_drift_compensation = false;
for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) {
printf("TCoreAudioRenderer::CreateAggregateDevice : cannot set SR of input device\n");
} else {
// Check clock domain
osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
if (osErr != 0) {
printf("TCoreAudioRenderer::CreateAggregateDevice : kAudioDevicePropertyClockDomain error\n");
printError(osErr);
} else {
keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
printf("TCoreAudioRenderer::CreateAggregateDevice : input clockdomain = %d\n", clockdomain);
if (clockdomain != 0 && clockdomain != keptclockdomain) {
printf("TCoreAudioRenderer::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...\n");
need_clock_drift_compensation = true;
}
}
}
}
for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) {
printf("TCoreAudioRenderer::CreateAggregateDevice : cannot set SR of output device\n");
} else {
// Check clock domain
osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
if (osErr != 0) {
printf("TCoreAudioRenderer::CreateAggregateDevice : kAudioDevicePropertyClockDomain error\n");
printError(osErr);
} else {
keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
printf("TCoreAudioRenderer::CreateAggregateDevice : output clockdomain = %d", clockdomain);
if (clockdomain != 0 && clockdomain != keptclockdomain) {
printf("TCoreAudioRenderer::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...\n");
need_clock_drift_compensation = true;
}
}
}
}
// If no valid clock domain was found, then assume we have to compensate...
if (keptclockdomain == 0) {
need_clock_drift_compensation = true;
}
//---------------------------------------------------------------------------
// Start to create a new aggregate by getting the base audio hardware plugin
//---------------------------------------------------------------------------
char device_name[256];
for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
GetDeviceNameFromID(captureDeviceID[i], device_name);
printf("Separated input = '%s' \n", device_name);
}
for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
GetDeviceNameFromID(playbackDeviceID[i], device_name);
printf("Separated output = '%s' \n", device_name);
}
osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error\n");
printError(osErr);
return osErr;
}
AudioValueTranslation pluginAVT;
CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
pluginAVT.mInputData = &inBundleRef;
pluginAVT.mInputDataSize = sizeof(inBundleRef);
pluginAVT.mOutputData = &fPluginID;
pluginAVT.mOutputDataSize = sizeof(fPluginID);
osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error\n");
printError(osErr);
return osErr;
}
//-------------------------------------------------
// Create a CFDictionary for our aggregate device
//-------------------------------------------------
CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex");
CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex");
// add the name of the device to the dictionary
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);
// add our choice of UID for the aggregate device to the dictionary
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
// add a "private aggregate key" to the dictionary
int value = 1;
CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
SInt32 system;
Gestalt(gestaltSystemVersion, &system);
printf("TCoreAudioRenderer::CreateAggregateDevice : system version = %x limit = %x\n", system, 0x00001054);
// Starting with 10.5.4 systems, the AD can be internal... (better)
if (system < 0x00001054) {
printf("TCoreAudioRenderer::CreateAggregateDevice : public aggregate device....\n");
} else {
printf("TCoreAudioRenderer::CreateAggregateDevice : private aggregate device....\n");
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
}
// Prepare sub-devices for clock drift compensation
CFMutableArrayRef subDevicesArrayClock = NULL;
/*
if (fClockDriftCompensate) {
if (need_clock_drift_compensation) {
jack_info("Clock drift compensation activated...");
subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
CFStringRef UID = GetDeviceName(captureDeviceID[i]);
if (UID) {
CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
//CFRelease(UID);
CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
}
}
for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
if (UID) {
CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
//CFRelease(UID);
CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
}
}
// add sub-device clock array for the aggregate device to the dictionary
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
} else {
jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
}
}
*/
//-------------------------------------------------
// Create a CFMutableArray for our sub-device list
//-------------------------------------------------
// we need to append the UID for each device to a CFMutableArray, so create one here
CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
vector captureDeviceUID;
for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
CFStringRef ref = GetDeviceName(captureDeviceID[i]);
if (ref == NULL)
return -1;
captureDeviceUID.push_back(ref);
// input sub-devices in this example, so append the sub-device's UID to the CFArray
CFArrayAppendValue(subDevicesArray, ref);
}
vector playbackDeviceUID;
for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
CFStringRef ref = GetDeviceName(playbackDeviceID[i]);
if (ref == NULL)
return -1;
playbackDeviceUID.push_back(ref);
// output sub-devices in this example, so append the sub-device's UID to the CFArray
CFArrayAppendValue(subDevicesArray, ref);
}
//-----------------------------------------------------------------------
// Feed the dictionary to the plugin, to create a blank aggregate device
//-----------------------------------------------------------------------
AudioObjectPropertyAddress pluginAOPA;
pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
UInt32 outDataSize;
osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectGetPropertyDataSize error\n");
printError(osErr);
goto error;
}
osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectGetPropertyData error\n");
printError(osErr);
goto error;
}
// pause for a bit to make sure that everything completed correctly
// this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
//-------------------------
// Set the sub-device list
//-------------------------
pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
outDataSize = sizeof(CFMutableArrayRef);
osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error\n");
printError(osErr);
goto error;
}
// pause again to give the changes time to take effect
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
//-----------------------
// Set the master device
//-----------------------
// set the master device manually (this is the device which will act as the master clock for the aggregate device)
// pass in the UID of the device you want to use
pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
outDataSize = sizeof(CFStringRef);
osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master...
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectSetPropertyData for master device error\n");
printError(osErr);
goto error;
}
// pause again to give the changes time to take effect
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
// Prepare sub-devices for clock drift compensation
// Workaround for bug in the HAL : until 10.6.2
if (fClockDriftCompensate) {
if (need_clock_drift_compensation) {
printf("Clock drift compensation activated...\n");
// Get the property data size
osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error\n");
printError(osErr);
}
// Calculate the number of object IDs
subDevicesNum = outSize / sizeof(AudioObjectID);
printf("TCoreAudioRenderer::CreateAggregateDevice clock drift compensation, number of sub-devices = %d\n", subDevicesNum);
AudioObjectID subDevices[subDevicesNum];
outSize = sizeof(subDevices);
osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error\n");
printError(osErr);
}
// Set kAudioSubDevicePropertyDriftCompensation property...
for (UInt32 index = 0; index < subDevicesNum; ++index) {
UInt32 theDriftCompensationValue = 1;
osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue);
if (osErr != noErr) {
printf("TCoreAudioRenderer::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error\n");
printError(osErr);
}
}
} else {
printf("Clock drift compensation was asked but is not needed (devices use the same clock domain)\n");
}
}
// pause again to give the changes time to take effect
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
//----------
// Clean up
//----------
// release the private AD key
CFRelease(AggregateDeviceNumberRef);
// release the CF objects we have created - we don't need them any more
CFRelease(aggDeviceDict);
CFRelease(subDevicesArray);
if (subDevicesArrayClock)
CFRelease(subDevicesArrayClock);
// release the device UID
for (UInt32 i = 0; i < captureDeviceUID.size(); i++) {
CFRelease(captureDeviceUID[i]);
}
for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) {
CFRelease(playbackDeviceUID[i]);
}
printf("New aggregate device %d\n", *outAggregateDevice);
return noErr;
error:
DestroyAggregateDevice();
return -1;
}
OSStatus TCoreAudioRenderer::DestroyAggregateDevice()
{
OSStatus osErr = noErr;
AudioObjectPropertyAddress pluginAOPA;
pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice;
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
UInt32 outDataSize;
if (fPluginID > 0) {
osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
if (osErr != noErr) {
printf("TCoreAudioRenderer::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error\n");
printError(osErr);
return osErr;
}
osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID);
if (osErr != noErr) {
printf("TCoreAudioRenderer::DestroyAggregateDevice : AudioObjectGetPropertyData error\n");
printError(osErr);
return osErr;
}
}
return noErr;
}
long TCoreAudioRenderer::OpenDefault(long inChan, long outChan, long bufferSize, long samplerate)
{
OSStatus err = noErr;
ComponentResult err1;
UInt32 outSize;
UInt32 enableIO;
Boolean isWritable;
AudioStreamBasicDescription srcFormat, dstFormat, sampleRate;
long in_nChannels, out_nChannels;
printf("OpenDefault inChan = %ld outChan = %ld bufferSize = %ld samplerate = %ld\n", inChan, outChan, bufferSize, samplerate);
SInt32 major;
SInt32 minor;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
// Starting with 10.6 systems, the HAL notification thread is created internally
if (major == 10 && minor >= 6) {
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
if (osErr != noErr) {
printf("TCoreAudioRenderer::Open kAudioHardwarePropertyRunLoop error\n");
printError(osErr);
}
}
if (GetDefaultDevice(inChan, outChan, samplerate,&fDeviceID) != noErr) {
printf("Cannot open default device\n");
return OPEN_ERR;
}
// Setting buffer size
outSize = sizeof(UInt32);
err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &bufferSize);
if (err != noErr) {
printf("Cannot set buffer size %ld\n", bufferSize);
printError(err);
return OPEN_ERR;
}
// Setting sample rate
outSize = sizeof(AudioStreamBasicDescription);
err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &outSize, &sampleRate);
if (err != noErr) {
printf("Cannot get current sample rate\n");
printError(err);
return OPEN_ERR;
}
if (samplerate != long(sampleRate.mSampleRate)) {
sampleRate.mSampleRate = (Float64)(samplerate);
err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyStreamFormat, outSize, &sampleRate);
if (err != noErr) {
printf("Cannot set sample rate = %ld\n", samplerate);
printError(err);
return OPEN_ERR;
}
}
// AUHAL
ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
Component HALOutput = FindNextComponent(NULL, &cd);
err1 = OpenAComponent(HALOutput, &fAUHAL);
if (err1 != noErr) {
printf("Error calling OpenAComponent\n");
printError(err1);
goto error;
}
err1 = AudioUnitInitialize(fAUHAL);
if (err1 != noErr) {
printf("Cannot initialize AUHAL unit\n");
printError(err1);
goto error;
}
enableIO = 1;
err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n");
printError(err1);
goto error;
}
enableIO = 1;
err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n");
printError(err1);
goto error;
}
err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice\n");
printError(err1);
goto error;
}
err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&bufferSize, sizeof(UInt32));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n");
printError(err1);
goto error;
}
err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&bufferSize, sizeof(UInt32));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n");
printError(err1);
goto error;
}
err1 = AudioUnitGetPropertyInfo(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 1, &outSize, &isWritable);
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap-INFO 1\n");
printError(err1);
}
in_nChannels = (err1 == noErr) ? outSize / sizeof(SInt32) : 0;
printf("in_nChannels = %ld\n", in_nChannels);
err1 = AudioUnitGetPropertyInfo(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, &outSize, &isWritable);
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap-INFO 0\n");
printError(err1);
}
out_nChannels = (err1 == noErr) ? outSize / sizeof(SInt32) : 0;
printf("out_nChannels = %ld\n", out_nChannels);
/*
Just ignore this case : seems to work without any further change...
if (outChan > out_nChannels) {
printf("This device hasn't required output channels\n");
goto error;
}
if (inChan > in_nChannels) {
printf("This device hasn't required input channels\n");
goto error;
}
*/
if (outChan < out_nChannels) {
SInt32 chanArr[out_nChannels];
for (int i = 0; i < out_nChannels; i++) {
chanArr[i] = -1;
}
for (int i = 0; i < outChan; i++) {
chanArr[i] = i;
}
err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels);
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0\n");
printError(err1);
}
}
if (inChan < in_nChannels) {
SInt32 chanArr[in_nChannels];
for (int i = 0; i < in_nChannels; i++) {
chanArr[i] = -1;
}
for (int i = 0; i < inChan; i++) {
chanArr[i] = i;
}
AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels);
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1\n");
printError(err1);
}
}
if (inChan > 0) {
outSize = sizeof(AudioStreamBasicDescription);
err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &outSize);
if (err1 != noErr) {
printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
printError(err1);
}
PrintStreamDesc(&srcFormat);
srcFormat.mSampleRate = samplerate;
srcFormat.mFormatID = kAudioFormatLinearPCM;
srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
srcFormat.mBytesPerPacket = sizeof(float);
srcFormat.mFramesPerPacket = 1;
srcFormat.mBytesPerFrame = sizeof(float);
srcFormat.mChannelsPerFrame = inChan;
srcFormat.mBitsPerChannel = 32;
PrintStreamDesc(&srcFormat);
err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
printError(err1);
}
}
if (outChan > 0) {
outSize = sizeof(AudioStreamBasicDescription);
err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &outSize);
if (err1 != noErr) {
printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
printError(err1);
}
PrintStreamDesc(&dstFormat);
dstFormat.mSampleRate = samplerate;
dstFormat.mFormatID = kAudioFormatLinearPCM;
dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
dstFormat.mBytesPerPacket = sizeof(float);
dstFormat.mFramesPerPacket = 1;
dstFormat.mBytesPerFrame = sizeof(float);
dstFormat.mChannelsPerFrame = outChan;
dstFormat.mBitsPerChannel = 32;
PrintStreamDesc(&dstFormat);
err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
printError(err1);
}
}
if (inChan > 0 && outChan == 0) {
AURenderCallbackStruct output;
output.inputProc = Render;
output.inputProcRefCon = this;
err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n");
printError(err1);
goto error;
}
} else {
AURenderCallbackStruct output;
output.inputProc = Render;
output.inputProcRefCon = this;
err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output));
if (err1 != noErr) {
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n");
printError(err1);
goto error;
}
}
fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inChan * sizeof(AudioBuffer));
if (fInputData == 0) {
printf("Cannot allocate memory for input buffers\n");
goto error;
}
fInputData->mNumberBuffers = inChan;
// Prepare buffers
for (int i = 0; i < inChan; i++) {
fInputData->mBuffers[i].mNumberChannels = 1;
fInputData->mBuffers[i].mData = malloc(bufferSize * sizeof(float));
fInputData->mBuffers[i].mDataByteSize = bufferSize * sizeof(float);
}
return NO_ERR;
error:
AudioUnitUninitialize(fAUHAL);
CloseComponent(fAUHAL);
return OPEN_ERR;
}
long TCoreAudioRenderer::Close()
{
for (int i = 0; i < gDevNumInChans; i++) {
free(fInputData->mBuffers[i].mData);
}
free(fInputData);
AudioUnitUninitialize(fAUHAL);
CloseComponent(fAUHAL);
DestroyAggregateDevice();
return NO_ERR;
}
long TCoreAudioRenderer::Start()
{
OSStatus err = AudioOutputUnitStart(fAUHAL);
if (err != noErr) {
printf("Error while opening device : device open error \n");
return OPEN_ERR;
} else {
return NO_ERR;
}
}
long TCoreAudioRenderer::Stop()
{
OSStatus err = AudioOutputUnitStop(fAUHAL);
if (err != noErr) {
printf("Error while closing device : device close error \n");
return OPEN_ERR;
} else {
return NO_ERR;
}
}
/******************************************************************************
*******************************************************************************
CORE AUDIO INTERFACE
*******************************************************************************
*******************************************************************************/
class coreaudio : public audio {
TCoreAudioRenderer audio_device;
long fSampleRate, fFramesPerBuf;
public:
coreaudio(long srate, long fpb) : fSampleRate(srate), fFramesPerBuf(fpb) {}
virtual ~coreaudio() {}
virtual bool init(const char* /*name*/, dsp* DSP) {
gDsp = DSP;
DSP->init (fSampleRate);
gDevNumInChans = DSP->getNumInputs();
gDevNumOutChans = DSP->getNumOutputs();
if (audio_device.OpenDefault(gDevNumInChans, gDevNumOutChans, fFramesPerBuf, fSampleRate) < 0) {
printf("Cannot open CoreAudio device\n");
return false;
}
return true;
}
virtual bool start() {
if (audio_device.Start() < 0) {
printf("Cannot start CoreAudio device\n");
return false;
}
return true;
}
virtual void stop() {
audio_device.Stop();
audio_device.Close();
}
};
#endif
/********************END ARCHITECTURE SECTION (part 2/2)****************/