1/********************************************************************************************************************************** 2* 3* OpenAL cross platform audio library 4* Copyright (c) 2004, Apple Computer, Inc., Copyright (c) 2012, Apple Inc. All rights reserved. 5* 6* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 7* conditions are met: 8* 9* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 11* disclaimer in the documentation and/or other materials provided with the distribution. 12* 3. Neither the name of Apple Inc. ("Apple") nor the names of its contributors may be used to endorse or promote products derived 13* from this software without specific prior written permission. 14* 15* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 17* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 19* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 20* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21* 22**********************************************************************************************************************************/ 23 24#include "oalDevice.h" 25#include "oalContext.h" 26 27// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 29#define LOG_DEVICE_CHANGES 0 30#define PROFILE_IO_USAGE 0 31#define LOG_VERBOSE 0 32 33#if PROFILE_IO_USAGE 34static int debugCounter = -1; 35static int numCyclesToPrint = 1000; 36 37static UInt64 lastHostTime; 38static UInt64 totalHostTime; 39static UInt64 minUsage; 40static UInt64 maxUsage; 41static UInt64 totalUsage; 42 43#define PROFILE_IO_CYCLE 0 44#if PROFILE_IO_CYCLE 45static UInt64 maxHT; 46static UInt64 minHT; 47#endif 48 49#include <CoreAudio/HostTime.h> 50#endif 51 52// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 53#if GET_OVERLOAD_NOTIFICATIONS 54OSStatus PrintTheOverloadMessage( AudioDeviceID inDevice, 55 UInt32 inChannel, 56 Boolean isInput, 57 AudioDevicePropertyID inPropertyID, 58 void* inClientData) 59{ 60 DebugMessage("OVERLOAD OCCURRED"); 61} 62#endif 63 64// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 65// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 66// OALDevices 67// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 69#pragma mark ***** OALDevices ***** 70 71/* 72 If caller wants a specific HAL device (instead of the default output device), a NULL terminated 73 C-String representation of the CFStringRef returned from the HAL APIs for the 74 kAudioDevicePropertyDeviceUID property 75*/ 76OALDevice::OALDevice (const char* inDeviceName, uintptr_t inSelfToken, UInt32 inRenderChannelSetting) 77 : mSelfToken (inSelfToken), 78 mCurrentError(ALC_NO_ERROR), 79 mHALDevice (0), 80 mDistanceScalingRequired(false), 81 mGraphInitialized(false), 82 mAUGraph(0), 83 mOutputNode(0), 84 mOutputUnit(0), 85 mMixerNode(0), 86 mChannelLayoutTag(0), 87 mConnectedContext(NULL), 88 mDeviceSampleRate(kDefaultMixerRate), 89 mRenderChannelCount(0), 90 mRenderChannelSetting(inRenderChannelSetting), 91 mFramesPerSlice(512), 92 mInUseFlag(0) 93{ 94#if LOG_VERBOSE 95 DebugMessageN1("OALDevice::OALDevice() - OALDevice = %ld", (long int) mSelfToken); 96#endif 97 OSStatus result = noErr; 98 UInt32 size = 0; 99 CFStringRef cfString = NULL; 100 char *useThisDevice = (char *) inDeviceName; 101 102 try { 103 // make sure a proper render channel setting was passed to teh constructor 104 if ((inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_MULTICHANNEL) && (inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO)) 105 throw (OSStatus) AL_INVALID_VALUE; 106 107 // until the ALC_ENUMERATION_EXT extension is supported only use the default output device 108 useThisDevice = NULL; 109 110 // first, get the requested HAL device's ID 111 if (useThisDevice) 112 { 113 // turn the inDeviceName into a CFString 114 cfString = CFStringCreateWithCString(NULL, useThisDevice, kCFStringEncodingUTF8); 115 if (cfString) 116 { 117 AudioValueTranslation translation; 118 119 translation.mInputData = &cfString; 120 translation.mInputDataSize = sizeof(cfString); 121 translation.mOutputData = &mHALDevice; 122 translation.mOutputDataSize = sizeof(mHALDevice); 123 124 size = sizeof(AudioValueTranslation); 125 result = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &translation); 126 CFRelease (cfString); 127 } 128 else 129 result = -1; // couldn't get string ref 130 131 THROW_RESULT 132 } 133 else 134 { 135 size = sizeof(AudioDeviceID); 136 result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &mHALDevice); 137 THROW_RESULT 138 } 139 140 InitializeGraph(useThisDevice); 141 142 mRenderChannelCount = GetDesiredRenderChannelCount(); 143 144#if PROFILE_IO_USAGE 145 debugCounter = -1; 146#endif 147 148 } 149 catch (...) { 150 throw; 151 } 152} 153 154// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 155OALDevice::~OALDevice() 156{ 157#if LOG_VERBOSE 158 DebugMessageN1("OALDevice::~OALDevice() - OALDevice = %ld", (long int) mSelfToken); 159#endif 160 TeardownGraph(); 161} 162 163// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 164void OALDevice::SetError(ALenum errorCode) 165{ 166#if LOG_VERBOSE 167 DebugMessageN2("OALDevice::SetError() - OALDevice:errorCode = %ld:%d", (long int) mSelfToken, errorCode); 168#endif 169 if (mCurrentError == ALC_NO_ERROR) 170 return; 171 172 mCurrentError = errorCode; 173} 174 175// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 176ALenum OALDevice::GetError() 177{ 178#if LOG_VERBOSE 179 DebugMessageN1("OALDevice::GetError() - OALDevice = %ld", (long int) mSelfToken); 180#endif 181 ALenum latestError = mCurrentError; 182 mCurrentError = ALC_NO_ERROR; 183 184 return latestError; 185} 186 187// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188void OALDevice::TeardownGraph() 189{ 190#if LOG_VERBOSE || LOG_DEVICE_CHANGES 191 DebugMessageN1("OALDevice::TeardownGraph() - OALDevice = %ld", (long int) mSelfToken); 192#endif 193 194#if GET_OVERLOAD_NOTIFICATIONS 195 AudioDeviceID device = 0; 196 UInt32 size = sizeof(device); 197 198 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device); 199 if (device != 0) 200 { 201 DebugMessage("********** Removing Overload Notification ***********"); 202 AudioDeviceRemovePropertyListener( device, 0, false, kAudioDeviceProcessorOverload, PrintTheOverloadMessage); 203 } 204#endif 205 206 if (mAUGraph) 207 { 208 StopGraph(); 209 DisposeAUGraph (mAUGraph); 210 mAUGraph = 0; 211 } 212} 213 214// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215// also resets the audio channel layout if necessary 216void OALDevice::ResetRenderChannelSettings() 217{ 218#if LOG_VERBOSE || LOG_DEVICE_CHANGES 219 DebugMessageN1("OALDevice::ResetRenderChannelSettings() - OALDevice = %ld", (long int) mSelfToken); 220#endif 221 222 // verify that the channel count has actually changed before doing all this work... 223 UInt32 channelCount = GetDesiredRenderChannelCount(); 224 225 if (mRenderChannelCount == channelCount) 226 return; // only reset the graph if the channel count has changed 227 228 mRenderChannelCount = channelCount; 229 230 Boolean wasRunning = false; 231 AUGraphIsRunning (mAUGraph, &wasRunning); 232 if (wasRunning) 233 StopGraph(); 234 235 // disconnect the mixer (mMixerNode) from the output au if necessary 236 OSStatus result = noErr; 237 if (mMixerNode) 238 { 239 // mixer is currently connected 240 result = AUGraphDisconnectNodeInput (mAUGraph, mOutputNode, 0); 241 THROW_RESULT 242 // update the graph 243 result = AUGraphUpdate (mAUGraph, NULL); 244 THROW_RESULT 245 } 246 247 // set AU properties 248 { 249 CAStreamBasicDescription format; 250 UInt32 propSize = sizeof(format); 251 // get the current input scope format of the output device, so we can just change the channel count 252 result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, &propSize); 253 THROW_RESULT 254 255 format.SetCanonical (mRenderChannelCount, false); // not interleaved 256 result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format)); 257 THROW_RESULT 258 259 // The Output Format of the Contexts that use this device will be set later by ReconfigureContextsOfThisDevice() 260 261 // channel layout may have changed do to a different render channel count 262 AudioChannelLayout layout; 263 layout.mChannelLayoutTag = GetChannelLayoutTag(); 264 layout.mChannelBitmap = 0; 265 layout.mNumberChannelDescriptions = 0; 266 result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof(layout)); 267 THROW_RESULT 268 } 269 270 // tell the contexts using this device to change their mixer output format - so when they are reconnected to the output au - formats will be reset 271 ReconfigureContextsOfThisDevice(mSelfToken); 272 273 // reconnect mixer to output unit if it was previously connected 274 if (mMixerNode) 275 { 276 result = AUGraphConnectNodeInput (mAUGraph, mMixerNode, 0, mOutputNode, 0); 277 THROW_RESULT 278 279 // update the graph 280 result = AUGraphUpdate (mAUGraph, NULL); 281 THROW_RESULT 282 } 283 284 if (wasRunning) 285 AUGraphStart(mAUGraph); // restart the graph if it was already running 286 287 return; 288} 289 290// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 291void OALDevice::GraphFormatPropertyListener ( void *inRefCon, 292 AudioUnit ci, 293 AudioUnitPropertyID inID, 294 AudioUnitScope inScope, 295 AudioUnitElement inElement) 296{ 297#if LOG_VERBOSE || LOG_DEVICE_CHANGES 298 DebugMessageN1("OALDevice::GraphFormatPropertyListener - OALDevice: %ld", ((OALDevice*)inRefCon)->mSelfToken); 299#endif 300 301 try { 302 if (inScope == kAudioUnitScope_Output) 303 { 304 ((OALDevice*)inRefCon)->ResetRenderChannelSettings (); 305 } 306 } 307 catch (...) { 308 } 309} 310 311// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 312void OALDevice::SetRenderChannelSetting (UInt32 inRenderChannelSetting) 313{ 314#if LOG_VERBOSE || LOG_DEVICE_CHANGES 315 DebugMessageN1("OALDevice::SetRenderChannelSetting() - OALDevice = %ld", (long int) mSelfToken); 316#endif 317 318 try { 319 if ((inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_MULTICHANNEL) && (inRenderChannelSetting != ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO)) 320 throw (OSStatus) AL_INVALID_VALUE; 321 322 if (inRenderChannelSetting == mRenderChannelSetting) 323 return; //nothing to do 324 325 mRenderChannelSetting = inRenderChannelSetting; 326 327 if (inRenderChannelSetting == ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO) 328 { 329 // clamping to stereo 330 if (mRenderChannelCount == 2) 331 return; // already rendering to stereo, so there's nothing to do 332 } 333 else 334 { 335 // allowing multi channel now 336 if (mRenderChannelCount > 2) 337 return; // already rendering to mc, so there's nothing to do 338 } 339 340 // work to be done now, it is necessary to change the channel layout and stream format from multi channel to stereo 341 // this requires the graph to be stopped and reconfigured 342 ResetRenderChannelSettings (); 343 } 344 catch (OSStatus result) { 345 DebugMessageN2("OALDevice::SetRenderChannelSetting - OALDevice: %ld:%ld", mSelfToken, (long int) result); 346 } 347 catch (...) { 348 } 349} 350 351// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 352void OALDevice::InitializeGraph (const char* inDeviceName) 353{ 354#if LOG_VERBOSE || LOG_DEVICE_CHANGES 355 DebugMessageN1("OALDevice::InitializeGraph() - OALDevice = %ld", (long int) mSelfToken); 356#endif 357 358 if (mAUGraph) 359 throw static_cast<OSStatus>('init'); 360 361 OSStatus result = noErr; 362 363 // ~~~~~~~~~~~~~~~~~~~~ CREATE GRAPH 364 365 result = NewAUGraph(&mAUGraph); 366 THROW_RESULT 367 368 // ~~~~~~~~~~~~~~~~~~~~ SET UP OUTPUT NODE 369 370 ComponentDescription cd; 371 cd.componentFlags = 0; 372 cd.componentFlagsMask = 0; 373 374 // At this time, only allow the default output device to be used and ignore the inDeviceName parameter 375 cd.componentType = kAudioUnitType_Output; 376 cd.componentSubType = kAudioUnitSubType_DefaultOutput; 377 cd.componentManufacturer = kAudioUnitManufacturer_Apple; 378 result = AUGraphNewNode (mAUGraph, &cd, 0, NULL, &mOutputNode); 379 THROW_RESULT 380 381 // ~~~~~~~~~~~~~~~~~~~~ OPEN GRAPH 382 383 result = AUGraphOpen (mAUGraph); 384 THROW_RESULT 385 386 result = AUGraphGetNodeInfo (mAUGraph, mOutputNode, 0, 0, 0, &mOutputUnit); 387 THROW_RESULT 388 389 result = AudioUnitInitialize (mOutputUnit); 390 THROW_RESULT 391 392 result = AudioUnitAddPropertyListener (mOutputUnit, kAudioUnitProperty_StreamFormat, GraphFormatPropertyListener, this); 393 THROW_RESULT 394 395 // Frame Per Slice 396 // get the device's frame count and set the AUs to match, will be set to 512 if this fails 397 AudioDeviceID device = 0; 398 UInt32 dataSize = sizeof(device); 399 result = AudioUnitGetProperty(mOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &dataSize); 400 if (result == noErr) 401 { 402 dataSize = sizeof(mFramesPerSlice); 403 result = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyBufferFrameSize, &dataSize, &mFramesPerSlice); 404 if (result == noErr) 405 { 406 result = AudioUnitSetProperty( mOutputUnit, kAudioUnitProperty_MaximumFramesPerSlice, 407 kAudioUnitScope_Global, 0, &mFramesPerSlice, sizeof(mFramesPerSlice)); 408 } 409 } 410 411 // get the device's outputRate 412 CAStreamBasicDescription format; 413 UInt32 propSize = sizeof(format); 414 result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize); 415 if (result == noErr) 416 mDeviceSampleRate = format.mSampleRate; 417 418} 419 420// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 421UInt32 OALDevice::GetDesiredRenderChannelCount () 422{ 423#if LOG_VERBOSE 424 DebugMessageN1("OALDevice::GetDesiredRenderChannelCount - OALDevice: %ld", mSelfToken); 425#endif 426 427 UInt32 returnValue = 2; // return stereo by default 428 429 // observe the mRenderChannelSetting flag and clamp to stereo if necessary 430 // This allows the user to request the libary to render to stereo in the case where only 2 speakers 431 // are connected to multichannel hardware 432 if (mRenderChannelSetting == ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO) 433 return (returnValue); 434 435 // get the HAL device id form the output AU 436 AudioDeviceID deviceID; 437 UInt32 propSize = sizeof(deviceID); 438 OSStatus result = AudioUnitGetProperty(mOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 1, &deviceID, &propSize); 439 THROW_RESULT 440 441 // get the channel layout set by the user in AMS 442 result = AudioDeviceGetPropertyInfo(deviceID, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propSize, NULL); 443 444 if (result == noErr) 445 { 446 AudioChannelLayout* layout = (AudioChannelLayout *) calloc(1, propSize); 447 if (layout != NULL) 448 { 449 result = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyPreferredChannelLayout, &propSize, layout); 450 451 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) 452 { 453 // no channel layout tag is returned, so walk through the channel descriptions and count 454 // the channels that are associated with a speaker 455 if (layout->mNumberChannelDescriptions == 2) 456 { 457 returnValue = 2; // there is no channel info for stereo 458 } 459 else 460 { 461 returnValue = 0; 462 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++) 463 { 464 if ((layout->mChannelDescriptions[i].mChannelLabel != kAudioChannelLabel_Unknown) && (layout->mChannelDescriptions[i].mChannelLabel != kAudioChannelLabel_LFEScreen)) 465 returnValue++; 466 } 467 } 468 mChannelLayoutTag = GetLayoutTagForLayout(layout, returnValue); 469 } 470 else 471 { 472 mChannelLayoutTag = layout->mChannelLayoutTag; 473 switch (layout->mChannelLayoutTag) 474 { 475 case kAudioChannelLayoutTag_AudioUnit_5_0: 476 case kAudioChannelLayoutTag_AudioUnit_5_1: 477 returnValue = 5; 478 break; 479 case kAudioChannelLayoutTag_AudioUnit_6_0: 480 case kAudioChannelLayoutTag_AudioUnit_6_1: 481 returnValue = 6; 482 break; 483 case kAudioChannelLayoutTag_AudioUnit_7_0: 484 case kAudioChannelLayoutTag_AudioUnit_7_1: 485 case kAudioChannelLayoutTag_AudioUnit_7_0_Front: 486 returnValue = 7; 487 break; 488 case kAudioChannelLayoutTag_AudioUnit_8: 489 returnValue = 8; 490 break; 491 case kAudioChannelLayoutTag_AudioUnit_4: 492 returnValue = 4; 493 break; 494 default: 495 returnValue = 2; 496 break; 497 } 498 } 499 500 free(layout); 501 } 502 } 503 // pass in num channels on the hw, 504 // how many channels the user has requested, and which 3DMixer is present 505 returnValue = GetDesiredRenderChannelsFor3DMixer(returnValue); 506 507 return (returnValue); 508} 509 510// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 511// called from alcDestroyContext(), when all context's that use this device are gone 512void OALDevice::StopGraph() 513{ 514#if LOG_VERBOSE 515 DebugMessageN1("OALDevice::StopGraph() - OALDevice = %ld", (long int) mSelfToken); 516#endif 517 AUGraphStop (mAUGraph); 518 Boolean flag; 519 do { 520 AUGraphIsRunning (mAUGraph, &flag); 521 } while (flag); 522} 523 524// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 525/* 526 When a Context is connected for the first time, the graph is initialized & started. 527 It will not be explicitly stopped until all the contexts that use this device have been destroyed. 528*/ 529void OALDevice::ConnectContext (OALContext* inContext) 530{ 531#if LOG_VERBOSE || LOG_DEVICE_CHANGES 532 DebugMessageN1("OALDevice::ConnectContext() - OALDevice = %ld", (long int) mSelfToken); 533#endif 534 535 OSStatus result = noErr; 536 OALContext* oldContext = mConnectedContext; // save in case it needs to be restored 537 538 if (inContext == mConnectedContext) 539 return; // already connected 540 541 try { 542 // we only have to disconnect when the 3DMixer node has changed 543 if (mConnectedContext && (inContext->GetMixerNode() != mMixerNode)){ 544 result = AUGraphDisconnectNodeInput(mAUGraph, mOutputNode, 0); 545 THROW_RESULT 546 547 mMixerNode = 0; 548 mConnectedContext = 0; 549 } 550 551 // set AU properties 552 { 553 CAStreamBasicDescription format; 554 UInt32 propSize = sizeof(format); 555 result = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize); 556 THROW_RESULT 557 558 format.SetCanonical (mRenderChannelCount, false); // not interleaved 559 format.mSampleRate = inContext->GetMixerRate(); 560 561 result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format)); 562 THROW_RESULT 563 564 result = AudioUnitSetProperty (inContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format)); 565 THROW_RESULT 566 567 // used to be a configure graph AUs (outout AU and mixerAU to be connected) 568 AudioChannelLayout layout; 569 layout.mChannelLayoutTag = GetChannelLayoutTag(); 570 layout.mChannelBitmap = 0; 571 layout.mNumberChannelDescriptions = 0; 572 result = AudioUnitSetProperty (mOutputUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof(layout)); 573 THROW_RESULT 574 } 575 576 // connect new mixer to output unit 577 result = AUGraphConnectNodeInput (mAUGraph, inContext->GetMixerNode(), 0, mOutputNode, 0); 578 THROW_RESULT 579 580 mMixerNode = inContext->GetMixerNode(); 581 mConnectedContext = inContext; 582 583 // initialize the graph on the 1st connection of a mixer to the graph 584 if (!mGraphInitialized) { 585 AUGraphInitialize(mAUGraph); 586 mGraphInitialized = true; 587 } 588 else 589 { 590 // if graph in uninitialized or not running , where should this go 591 result = AUGraphUpdate (mAUGraph, NULL); 592 THROW_RESULT 593 } 594 595 // the graph may not be running yet 596 if (!IsGraphStillRunning ()) 597 AUGraphStart (mAUGraph); 598 } 599 catch (OSStatus result) { 600 mConnectedContext = oldContext; 601 throw result; 602 } 603 catch (...) { 604 mConnectedContext = oldContext; 605 throw -1; 606 } 607 return; 608} 609 610// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 611void OALDevice::DisconnectContext(OALContext* inContext) 612{ 613#if LOG_VERBOSE || LOG_DEVICE_CHANGES 614 DebugMessageN1("OALDevice::DisconnectContext() - OALDevice = %ld", (long int) mSelfToken); 615#endif 616 if (inContext == mConnectedContext) 617 mConnectedContext = NULL; 618 619 AUGraphDisconnectNodeInput(mAUGraph, mOutputNode, 0); 620 621 // AUGraphUpdate will block here, until the node is removed 622 AUGraphUpdate(mAUGraph, NULL); 623} 624 625// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 626void OALDevice::RemoveContext(OALContext* inContext) 627{ 628#if LOG_VERBOSE || LOG_DEVICE_CHANGES 629 DebugMessageN1("OALDevice::RemoveContext() - OALDevice = %ld", (long int) mSelfToken); 630#endif 631 if(inContext == mConnectedContext) 632 mConnectedContext = NULL; 633 634 // now remove the remove the mixer node for the context 635 AUGraphRemoveNode(mAUGraph, inContext->GetMixerNode()); 636 // AUGraphUpdate will block here, until the node is removed 637 AUGraphUpdate(mAUGraph, NULL); 638} 639 640// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 641AudioChannelLayoutTag OALDevice::GetLayoutTagForLayout(AudioChannelLayout *inLayout, UInt32 inNumChannels) 642{ 643#if LOG_VERBOSE 644 DebugMessageN1("OALDevice::GetLayoutTagForLayout() - OALDevice = %ld", (long int) mSelfToken); 645#endif 646 if (inNumChannels == 5) 647 return kAudioChannelLayoutTag_AudioUnit_5_0; 648 649 // Quad not supported prior to 3d mixer ver. 2.0 650 else if ((inNumChannels == 4) && (Get3DMixerVersion() >= k3DMixerVersion_2_0)) 651 return kAudioChannelLayoutTag_AudioUnit_4; 652 653 // now check for new multichannel formats in the 3d mixer v. 2.3 and higher 654 else if (inNumChannels == 6 && (Get3DMixerVersion() >= k3DMixerVersion_2_3)) 655 return kAudioChannelLayoutTag_AudioUnit_6_0; 656 657 else if (inNumChannels == 7 && (Get3DMixerVersion() >= k3DMixerVersion_2_3)) 658 { 659 //return kAudioChannelLayoutTag_AudioUnit_7_0; 660 for (UInt32 i=0; i< inLayout->mNumberChannelDescriptions; i++) 661 { 662 if((inLayout->mChannelDescriptions[i].mChannelLabel == kAudioChannelLabel_RearSurroundLeft) || 663 (inLayout->mChannelDescriptions[i].mChannelLabel == kAudioChannelLabel_RearSurroundRight)) 664 { 665 //if we have rear channels, we need kAudioChannelLayoutTag_AudioUnit_7_0 666 return kAudioChannelLayoutTag_AudioUnit_7_0; 667 } 668 } 669 //if we didn't find rear channels, default 7.0 front 670 return kAudioChannelLayoutTag_AudioUnit_7_0_Front; 671 } 672 673 else if (inNumChannels == 8 && (Get3DMixerVersion() >= k3DMixerVersion_2_3)) 674 return kAudioChannelLayoutTag_AudioUnit_8; 675 676 return kAudioChannelLayoutTag_Stereo; // default case 677} 678 679// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 680UInt32 OALDevice::GetChannelLayoutTag() 681{ 682#if LOG_VERBOSE 683 DebugMessageN1("OALDevice::GetChannelLayoutTag() - OALDevice = %ld", (long int) mSelfToken); 684#endif 685 return mChannelLayoutTag; 686} 687