#include "oalDevice.h"
#include "oalContext.h"
#define LOG_DEVICE_CHANGES 0
#define PROFILE_IO_USAGE 0
#define LOG_VERBOSE 0
#if PROFILE_IO_USAGE
static int debugCounter = -1;
static int numCyclesToPrint = 1000;
static UInt64 lastHostTime;
static UInt64 totalHostTime;
static UInt64 minUsage;
static UInt64 maxUsage;
static UInt64 totalUsage;
#define PROFILE_IO_CYCLE 0
#if PROFILE_IO_CYCLE
static UInt64 maxHT;
static UInt64 minHT;
#endif
#include <CoreAudio/HostTime.h>
#endif
#if GET_OVERLOAD_NOTIFICATIONS
OSStatus PrintTheOverloadMessage( AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData)
{
DebugMessage("OVERLOAD OCCURRED");
}
#endif
#pragma mark ***** OALDevices *****
OALDevice::OALDevice (const char* inDeviceName, uintptr_t inSelfToken, UInt32 inRenderChannelSetting)
: mSelfToken (inSelfToken),
mCurrentError(ALC_NO_ERROR),
mHALDevice (0),
mDistanceScalingRequired(false),
mGraphInitialized(false),
mAUGraph(0),
mOutputNode(0),
mOutputUnit(0),
mMixerNode(0),
mChannelLayoutTag(0),
mConnectedContext(NULL),
mDeviceSampleRate(kDefaultMixerRate),
mRenderChannelCount(0),
mRenderChannelSetting(inRenderChannelSetting),
mFramesPerSlice(512),
mInUseFlag(0)
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::OALDevice() - OALDevice = %ld", (long int) mSelfToken);
#endif
OSStatus result = noErr;
UInt32 size = 0;
CFStringRef cfString = NULL;
char *useThisDevice = (char *) inDeviceName;
try {
if ((inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_MULTICHANNEL) && (inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO))
throw (OSStatus) AL_INVALID_VALUE;
useThisDevice = NULL;
if (useThisDevice)
{
cfString = CFStringCreateWithCString(NULL, useThisDevice, kCFStringEncodingUTF8);
if (cfString)
{
AudioValueTranslation translation;
translation.mInputData = &cfString;
translation.mInputDataSize = sizeof(cfString);
translation.mOutputData = &mHALDevice;
translation.mOutputDataSize = sizeof(mHALDevice);
size = sizeof(AudioValueTranslation);
result = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &translation);
CFRelease (cfString);
}
else
result = -1;
THROW_RESULT
}
else
{
size = sizeof(AudioDeviceID);
result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &mHALDevice);
THROW_RESULT
}
InitializeGraph(useThisDevice);
mRenderChannelCount = GetDesiredRenderChannelCount();
#if PROFILE_IO_USAGE
debugCounter = -1;
#endif
}
catch (...) {
throw;
}
}
OALDevice::~OALDevice()
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::~OALDevice() - OALDevice = %ld", (long int) mSelfToken);
#endif
TeardownGraph();
}
void OALDevice::SetError(ALenum errorCode)
{
#if LOG_VERBOSE
DebugMessageN2("OALDevice::SetError() - OALDevice:errorCode = %ld:%d", (long int) mSelfToken, errorCode);
#endif
if (mCurrentError == ALC_NO_ERROR)
return;
mCurrentError = errorCode;
}
ALenum OALDevice::GetError()
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::GetError() - OALDevice = %ld", (long int) mSelfToken);
#endif
ALenum latestError = mCurrentError;
mCurrentError = ALC_NO_ERROR;
return latestError;
}
void OALDevice::TeardownGraph()
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::TeardownGraph() - OALDevice = %ld", (long int) mSelfToken);
#endif
#if GET_OVERLOAD_NOTIFICATIONS
AudioDeviceID device = 0;
UInt32 size = sizeof(device);
AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
if (device != 0)
{
DebugMessage("********** Removing Overload Notification ***********");
AudioDeviceRemovePropertyListener( device, 0, false, kAudioDeviceProcessorOverload, PrintTheOverloadMessage);
}
#endif
if (mAUGraph)
{
StopGraph();
DisposeAUGraph (mAUGraph);
mAUGraph = 0;
}
}
void OALDevice::ResetRenderChannelSettings()
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::ResetRenderChannelSettings() - OALDevice = %ld", (long int) mSelfToken);
#endif
UInt32 channelCount = GetDesiredRenderChannelCount();
if (mRenderChannelCount == channelCount)
return;
mRenderChannelCount = channelCount;
Boolean wasRunning = false;
AUGraphIsRunning (mAUGraph, &wasRunning);
if (wasRunning)
StopGraph();
OSStatus result = noErr;
if (mMixerNode)
{
result = AUGraphDisconnectNodeInput (mAUGraph, mOutputNode, 0);
THROW_RESULT
result = AUGraphUpdate (mAUGraph, NULL);
THROW_RESULT
}
{
CAStreamBasicDescription format;
UInt32 propSize = sizeof(format);
result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, &propSize);
THROW_RESULT
format.SetCanonical (mRenderChannelCount, false); result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format));
THROW_RESULT
AudioChannelLayout layout;
layout.mChannelLayoutTag = GetChannelLayoutTag();
layout.mChannelBitmap = 0;
layout.mNumberChannelDescriptions = 0;
result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof(layout));
THROW_RESULT
}
ReconfigureContextsOfThisDevice(mSelfToken);
if (mMixerNode)
{
result = AUGraphConnectNodeInput (mAUGraph, mMixerNode, 0, mOutputNode, 0);
THROW_RESULT
result = AUGraphUpdate (mAUGraph, NULL);
THROW_RESULT
}
if (wasRunning)
AUGraphStart(mAUGraph);
return;
}
void OALDevice::GraphFormatPropertyListener ( void *inRefCon,
AudioUnit ci,
AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::GraphFormatPropertyListener - OALDevice: %ld", ((OALDevice*)inRefCon)->mSelfToken);
#endif
try {
if (inScope == kAudioUnitScope_Output)
{
((OALDevice*)inRefCon)->ResetRenderChannelSettings ();
}
}
catch (...) {
}
}
void OALDevice::SetRenderChannelSetting (UInt32 inRenderChannelSetting)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::SetRenderChannelSetting() - OALDevice = %ld", (long int) mSelfToken);
#endif
try {
if ((inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_MULTICHANNEL) && (inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO))
throw (OSStatus) AL_INVALID_VALUE;
if (inRenderChannelSetting == mRenderChannelSetting)
return;
mRenderChannelSetting = inRenderChannelSetting;
if (inRenderChannelSetting == ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO)
{
if (mRenderChannelCount == 2)
return; }
else
{
if (mRenderChannelCount > 2)
return; }
ResetRenderChannelSettings ();
}
catch (OSStatus result) {
DebugMessageN2("OALDevice::SetRenderChannelSetting - OALDevice: %ld:%ld", mSelfToken, (long int) result);
}
catch (...) {
}
}
void OALDevice::InitializeGraph (const char* inDeviceName)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::InitializeGraph() - OALDevice = %ld", (long int) mSelfToken);
#endif
if (mAUGraph)
throw static_cast<OSStatus>('init');
OSStatus result = noErr;
result = NewAUGraph(&mAUGraph);
THROW_RESULT
ComponentDescription cd;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_DefaultOutput;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
result = AUGraphNewNode (mAUGraph, &cd, 0, NULL, &mOutputNode);
THROW_RESULT
result = AUGraphOpen (mAUGraph);
THROW_RESULT
result = AUGraphGetNodeInfo (mAUGraph, mOutputNode, 0, 0, 0, &mOutputUnit);
THROW_RESULT
result = AudioUnitInitialize (mOutputUnit);
THROW_RESULT
result = AudioUnitAddPropertyListener (mOutputUnit, kAudioUnitProperty_StreamFormat, GraphFormatPropertyListener, this);
THROW_RESULT
AudioDeviceID device = 0;
UInt32 dataSize = sizeof(device);
result = AudioUnitGetProperty(mOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &dataSize);
if (result == noErr)
{
dataSize = sizeof(mFramesPerSlice);
result = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyBufferFrameSize, &dataSize, &mFramesPerSlice);
if (result == noErr)
{
result = AudioUnitSetProperty( mOutputUnit, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 0, &mFramesPerSlice, sizeof(mFramesPerSlice));
}
}
CAStreamBasicDescription format;
UInt32 propSize = sizeof(format);
result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize);
if (result == noErr)
mDeviceSampleRate = format.mSampleRate;
}
UInt32 OALDevice::GetDesiredRenderChannelCount ()
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::GetDesiredRenderChannelCount - OALDevice: %ld", mSelfToken);
#endif
UInt32 returnValue = 2;
if (mRenderChannelSetting == ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO)
return (returnValue);
AudioDeviceID deviceID;
UInt32 propSize = sizeof(deviceID);
OSStatus result = AudioUnitGetProperty(mOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 1, &deviceID, &propSize);
THROW_RESULT
result = AudioDeviceGetPropertyInfo(deviceID, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propSize, NULL);
if (result == noErr)
{
AudioChannelLayout* layout = (AudioChannelLayout *) calloc(1, propSize);
if (layout != NULL)
{
result = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propSize, layout);
if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions)
{
if (layout->mNumberChannelDescriptions == 2)
{
returnValue = 2; }
else
{
returnValue = 0;
for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++)
{
if ((layout->mChannelDescriptions[i].mChannelLabel != kAudioChannelLabel_Unknown) && (layout->mChannelDescriptions[i].mChannelLabel != kAudioChannelLabel_LFEScreen))
returnValue++;
}
}
mChannelLayoutTag = GetLayoutTagForLayout(layout, returnValue);
}
else
{
mChannelLayoutTag = layout->mChannelLayoutTag;
switch (layout->mChannelLayoutTag)
{
case kAudioChannelLayoutTag_AudioUnit_5_0:
case kAudioChannelLayoutTag_AudioUnit_5_1:
returnValue = 5;
break;
case kAudioChannelLayoutTag_AudioUnit_6_0:
case kAudioChannelLayoutTag_AudioUnit_6_1:
returnValue = 6;
break;
case kAudioChannelLayoutTag_AudioUnit_7_0:
case kAudioChannelLayoutTag_AudioUnit_7_1:
case kAudioChannelLayoutTag_AudioUnit_7_0_Front:
returnValue = 7;
break;
case kAudioChannelLayoutTag_AudioUnit_8:
returnValue = 8;
break;
case kAudioChannelLayoutTag_AudioUnit_4:
returnValue = 4;
break;
default:
returnValue = 2;
break;
}
}
free(layout);
}
}
returnValue = GetDesiredRenderChannelsFor3DMixer(returnValue);
return (returnValue);
}
void OALDevice::StopGraph()
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::StopGraph() - OALDevice = %ld", (long int) mSelfToken);
#endif
AUGraphStop (mAUGraph);
Boolean flag;
do {
AUGraphIsRunning (mAUGraph, &flag);
} while (flag);
}
void OALDevice::ConnectContext (OALContext* inContext)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::ConnectContext() - OALDevice = %ld", (long int) mSelfToken);
#endif
OSStatus result = noErr;
OALContext* oldContext = mConnectedContext;
if (inContext == mConnectedContext)
return;
try {
if (mConnectedContext && (inContext->GetMixerNode() != mMixerNode)){
result = AUGraphDisconnectNodeInput(mAUGraph, mOutputNode, 0);
THROW_RESULT
mMixerNode = 0;
mConnectedContext = 0;
}
{
CAStreamBasicDescription format;
UInt32 propSize = sizeof(format);
result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize);
THROW_RESULT
format.SetCanonical (mRenderChannelCount, false); format.mSampleRate = inContext->GetMixerRate();
result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format));
THROW_RESULT
result = AudioUnitSetProperty (inContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format));
THROW_RESULT
AudioChannelLayout layout;
layout.mChannelLayoutTag = GetChannelLayoutTag();
layout.mChannelBitmap = 0;
layout.mNumberChannelDescriptions = 0;
result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof(layout));
THROW_RESULT
}
result = AUGraphConnectNodeInput (mAUGraph, inContext->GetMixerNode(), 0, mOutputNode, 0);
THROW_RESULT
mMixerNode = inContext->GetMixerNode();
mConnectedContext = inContext;
if (!mGraphInitialized) {
AUGraphInitialize(mAUGraph);
mGraphInitialized = true;
}
else
{
result = AUGraphUpdate (mAUGraph, NULL);
THROW_RESULT
}
if (!IsGraphStillRunning ())
AUGraphStart (mAUGraph);
}
catch (OSStatus result) {
mConnectedContext = oldContext;
throw result;
}
catch (...) {
mConnectedContext = oldContext;
throw -1;
}
return;
}
void OALDevice::DisconnectContext(OALContext* inContext)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::DisconnectContext() - OALDevice = %ld", (long int) mSelfToken);
#endif
if (inContext == mConnectedContext)
mConnectedContext = NULL;
AUGraphDisconnectNodeInput(mAUGraph, mOutputNode, 0);
AUGraphUpdate(mAUGraph, NULL);
}
void OALDevice::RemoveContext(OALContext* inContext)
{
#if LOG_VERBOSE || LOG_DEVICE_CHANGES
DebugMessageN1("OALDevice::RemoveContext() - OALDevice = %ld", (long int) mSelfToken);
#endif
if(inContext == mConnectedContext)
mConnectedContext = NULL;
AUGraphRemoveNode(mAUGraph, inContext->GetMixerNode());
AUGraphUpdate(mAUGraph, NULL);
}
AudioChannelLayoutTag OALDevice::GetLayoutTagForLayout(AudioChannelLayout *inLayout, UInt32 inNumChannels)
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::GetLayoutTagForLayout() - OALDevice = %ld", (long int) mSelfToken);
#endif
if (inNumChannels == 5)
return kAudioChannelLayoutTag_AudioUnit_5_0;
else if ((inNumChannels == 4) && (Get3DMixerVersion() >= k3DMixerVersion_2_0))
return kAudioChannelLayoutTag_AudioUnit_4;
else if (inNumChannels == 6 && (Get3DMixerVersion() >= k3DMixerVersion_2_3))
return kAudioChannelLayoutTag_AudioUnit_6_0;
else if (inNumChannels == 7 && (Get3DMixerVersion() >= k3DMixerVersion_2_3))
{
for (UInt32 i=0; i< inLayout->mNumberChannelDescriptions; i++)
{
if((inLayout->mChannelDescriptions[i].mChannelLabel == kAudioChannelLabel_RearSurroundLeft) ||
(inLayout->mChannelDescriptions[i].mChannelLabel == kAudioChannelLabel_RearSurroundRight))
{
return kAudioChannelLayoutTag_AudioUnit_7_0;
}
}
return kAudioChannelLayoutTag_AudioUnit_7_0_Front;
}
else if (inNumChannels == 8 && (Get3DMixerVersion() >= k3DMixerVersion_2_3))
return kAudioChannelLayoutTag_AudioUnit_8;
return kAudioChannelLayoutTag_Stereo; }
UInt32 OALDevice::GetChannelLayoutTag()
{
#if LOG_VERBOSE
DebugMessageN1("OALDevice::GetChannelLayoutTag() - OALDevice = %ld", (long int) mSelfToken);
#endif
return mChannelLayoutTag;
}