1/*****************************************************************************\ 2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. 3 This file is licensed under the Snes9x License. 4 For further information, consult the LICENSE file in the root directory. 5\*****************************************************************************/ 6 7/*********************************************************************************** 8 SNES9X for Mac OS (c) Copyright John Stiles 9 10 Snes9x for Mac OS X 11 12 (c) Copyright 2001 - 2011 zones 13 (c) Copyright 2002 - 2005 107 14 (c) Copyright 2002 PB1400c 15 (c) Copyright 2004 Alexander and Sander 16 (c) Copyright 2004 - 2005 Steven Seeger 17 (c) Copyright 2005 Ryan Vogt 18 ***********************************************************************************/ 19 20 21#include "snes9x.h" 22#include "apu.h" 23 24#include <Cocoa/Cocoa.h> 25#include <CoreAudio/CoreAudio.h> 26#include <AudioToolbox/AudioToolbox.h> 27#include <AudioUnit/AudioUnitCarbonView.h> 28#include <pthread.h> 29#include <semaphore.h> 30 31#include "mac-prefix.h" 32#include "mac-dialog.h" 33#include "mac-musicbox.h" 34#include "mac-os.h" 35#include "mac-snes9x.h" 36#include "mac-audio.h" 37 38#define kAUReverb (1 << 0) 39#define kAUGraphEQ (1 << 1) 40 41int cureffect = kAUReverb; 42 43static AUGraph agraph; 44static AUNode outNode, cnvNode, revNode, eqlNode; 45static AudioUnit outAU, cnvAU, revAU, eqlAU; 46static AudioUnitCarbonView carbonView = NULL; 47static EventHandlerUPP carbonViewEventUPP = NULL; 48static EventHandlerRef carbonViewEventRef = NULL; 49static WindowRef effectWRef; 50static HISize effectWSize; 51static pthread_mutex_t mutex; 52static UInt32 outStoredFrames, cnvStoredFrames, revStoredFrames, eqlStoredFrames, devStoredFrames; 53static int16_t *audioBuffer; 54static uint32_t audioBufferSampleCapacity; 55static uint32_t audioBufferSampleCount; 56static sem_t *soundSyncSemaphore; 57 58static void ConnectAudioUnits (void); 59static void DisconnectAudioUnits (void); 60static void SaveEffectPresets (void); 61static void LoadEffectPresets (void); 62static void SetAudioUnitSoundFormat (void); 63static void SetAudioUnitVolume (void); 64static void StoreBufferFrameSize (void); 65static void ChangeBufferFrameSize (void); 66static void ReplaceAudioUnitCarbonView (void); 67static void ResizeSoundEffectsDialog (HIViewRef); 68static void MacSamplesAvailableCallBack (void *); 69static OSStatus MacAURenderCallBack (void *, AudioUnitRenderActionFlags *, const AudioTimeStamp *, UInt32, UInt32, AudioBufferList *); 70static pascal OSStatus SoundEffectsEventHandler (EventHandlerCallRef, EventRef, void *); 71static pascal OSStatus SoundEffectsCarbonViewEventHandler (EventHandlerCallRef, EventRef, void *); 72 73 74void InitMacSound (void) 75{ 76 OSStatus err; 77 78 err = NewAUGraph(&agraph); 79 80#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT 81 AudioComponentDescription outdesc, cnvdesc, revdesc, eqldesc; 82#else 83 ComponentDescription outdesc, cnvdesc, revdesc, eqldesc; 84#endif 85 86 outdesc.componentType = kAudioUnitType_Output; 87 outdesc.componentSubType = kAudioUnitSubType_DefaultOutput; 88 outdesc.componentManufacturer = 0; 89 outdesc.componentFlags = 0; 90 outdesc.componentFlagsMask = 0; 91 92 cnvdesc.componentType = kAudioUnitType_FormatConverter; 93 cnvdesc.componentSubType = kAudioUnitSubType_AUConverter; 94 cnvdesc.componentManufacturer = kAudioUnitManufacturer_Apple; 95 cnvdesc.componentFlags = 0; 96 cnvdesc.componentFlagsMask = 0; 97 98 revdesc.componentType = kAudioUnitType_Effect; 99 revdesc.componentSubType = kAudioUnitSubType_MatrixReverb; 100 revdesc.componentManufacturer = kAudioUnitManufacturer_Apple; 101 revdesc.componentFlags = 0; 102 revdesc.componentFlagsMask = 0; 103 104 eqldesc.componentType = kAudioUnitType_Effect; 105 eqldesc.componentSubType = kAudioUnitSubType_GraphicEQ; 106 eqldesc.componentManufacturer = kAudioUnitManufacturer_Apple; 107 eqldesc.componentFlags = 0; 108 eqldesc.componentFlagsMask = 0; 109 110#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT 111 err = AUGraphAddNode(agraph, &outdesc, &outNode); 112 err = AUGraphAddNode(agraph, &cnvdesc, &cnvNode); 113 err = AUGraphAddNode(agraph, &revdesc, &revNode); 114 err = AUGraphAddNode(agraph, &eqldesc, &eqlNode); 115#else 116 err = AUGraphNewNode(agraph, &outdesc, 0, NULL, &outNode); 117 err = AUGraphNewNode(agraph, &cnvdesc, 0, NULL, &cnvNode); 118 err = AUGraphNewNode(agraph, &revdesc, 0, NULL, &revNode); 119 err = AUGraphNewNode(agraph, &eqldesc, 0, NULL, &eqlNode); 120#endif 121 122 err = AUGraphOpen(agraph); 123 124#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT 125 err = AUGraphNodeInfo(agraph, outNode, NULL, &outAU); 126 err = AUGraphNodeInfo(agraph, cnvNode, NULL, &cnvAU); 127 err = AUGraphNodeInfo(agraph, revNode, NULL, &revAU); 128 err = AUGraphNodeInfo(agraph, eqlNode, NULL, &eqlAU); 129#else 130 err = AUGraphGetNodeInfo(agraph, outNode, NULL, NULL, NULL, &outAU); 131 err = AUGraphGetNodeInfo(agraph, cnvNode, NULL, NULL, NULL, &cnvAU); 132 err = AUGraphGetNodeInfo(agraph, revNode, NULL, NULL, NULL, &revAU); 133 err = AUGraphGetNodeInfo(agraph, eqlNode, NULL, NULL, NULL, &eqlAU); 134#endif 135 136 SetAudioUnitSoundFormat(); 137 SetAudioUnitVolume(); 138 StoreBufferFrameSize(); 139 ChangeBufferFrameSize(); 140 141 err = AUGraphInitialize(agraph); 142 143 ConnectAudioUnits(); 144 LoadEffectPresets(); 145 146 pthread_mutex_init(&mutex, NULL); 147 soundSyncSemaphore = sem_open("/s9x_mac_soundsync", O_CREAT, 0644, 1); 148 S9xSetSamplesAvailableCallback(MacSamplesAvailableCallBack, NULL); 149} 150 151void DeinitMacSound (void) 152{ 153 OSStatus err; 154 155 pthread_mutex_destroy(&mutex); 156 sem_close(soundSyncSemaphore); 157 sem_unlink("/s9x_mac_soundsync"); 158 SaveEffectPresets(); 159 DisconnectAudioUnits(); 160 err = AUGraphUninitialize(agraph); 161 err = AUGraphClose(agraph); 162 err = DisposeAUGraph(agraph); 163} 164 165static void SetAudioUnitSoundFormat (void) 166{ 167 OSStatus err; 168 AudioStreamBasicDescription format; 169 170#ifdef __BIG_ENDIAN__ 171 UInt32 endian = kLinearPCMFormatFlagIsBigEndian; 172#else 173 UInt32 endian = 0; 174#endif 175 176 memset(&format, 0, sizeof(format)); 177 178 format.mSampleRate = (Float64) Settings.SoundPlaybackRate; 179 format.mFormatID = kAudioFormatLinearPCM; 180 format.mFormatFlags = endian | (Settings.SixteenBitSound ? kLinearPCMFormatFlagIsSignedInteger : 0); 181 format.mBytesPerPacket = 2 * (Settings.SixteenBitSound ? 2 : 1); 182 format.mFramesPerPacket = 1; 183 format.mBytesPerFrame = 2 * (Settings.SixteenBitSound ? 2 : 1); 184 format.mChannelsPerFrame = 2; 185 format.mBitsPerChannel = Settings.SixteenBitSound ? 16 : 8; 186 187 err = AudioUnitSetProperty(aueffect ? cnvAU : outAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format)); 188} 189 190static void SetAudioUnitVolume (void) 191{ 192 OSStatus err; 193 194 err = AudioUnitSetParameter(outAU, kAudioUnitParameterUnit_LinearGain, kAudioUnitScope_Output, 0, (float) macSoundVolume / 100.0f, 0); 195} 196 197static void StoreBufferFrameSize (void) 198{ 199 OSStatus err; 200 UInt32 size; 201 AudioDeviceID device; 202#ifndef MAC_PANTHER_SUPPORT 203 AudioObjectPropertyAddress address; 204 205 address.mSelector = kAudioDevicePropertyBufferFrameSize; 206 address.mScope = kAudioObjectPropertyScopeGlobal; 207 address.mElement = kAudioObjectPropertyElementMaster; 208#endif 209 210 size = sizeof(AudioDeviceID); 211 err = AudioUnitGetProperty(outAU, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &size); 212 213 size = sizeof(UInt32); 214#ifndef MAC_PANTHER_SUPPORT 215 err = AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &devStoredFrames); 216#else 217 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &devStoredFrames); 218#endif 219 220 size = sizeof(UInt32); 221 err = AudioUnitGetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &outStoredFrames, &size); 222 size = sizeof(UInt32); 223 err = AudioUnitGetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &eqlStoredFrames, &size); 224 size = sizeof(UInt32); 225 err = AudioUnitGetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &revStoredFrames, &size); 226 size = sizeof(UInt32); 227 err = AudioUnitGetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &cnvStoredFrames, &size); 228} 229 230static void ChangeBufferFrameSize (void) 231{ 232 OSStatus err; 233 UInt32 numframes, size; 234 AudioDeviceID device; 235#ifndef MAC_PANTHER_SUPPORT 236 AudioObjectPropertyAddress address; 237 238 address.mSelector = kAudioDevicePropertyBufferFrameSize; 239 address.mScope = kAudioObjectPropertyScopeGlobal; 240 address.mElement = kAudioObjectPropertyElementMaster; 241#else 242 AudioTimeStamp ts; 243 244 ts.mFlags = 0; 245#endif 246 247 size = sizeof(AudioDeviceID); 248 err = AudioUnitGetProperty(outAU, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &size); 249 250 size = sizeof(UInt32); 251 252 if (macSoundInterval_ms == 0) 253 { 254 #ifndef MAC_PANTHER_SUPPORT 255 err = AudioObjectSetPropertyData(device, &address, 0, NULL, size, &devStoredFrames); 256 #else 257 err = AudioDeviceSetProperty(device, &ts, 0, false, kAudioDevicePropertyBufferFrameSize, size, &devStoredFrames); 258 #endif 259 260 err = AudioUnitSetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &outStoredFrames, size); 261 err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &eqlStoredFrames, size); 262 err = AudioUnitSetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &revStoredFrames, size); 263 err = AudioUnitSetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &cnvStoredFrames, size); 264 265 printf("Interval: system, Frames: %d/%d/%d/%d/%d\n", (int) devStoredFrames, (int) outStoredFrames, (int) eqlStoredFrames, (int) revStoredFrames, (int) cnvStoredFrames); 266 } 267 else 268 { 269 numframes = macSoundInterval_ms * Settings.SoundPlaybackRate / 1000; 270 271 #ifndef MAC_PANTHER_SUPPORT 272 err = AudioObjectSetPropertyData(device, &address, 0, NULL, size, &numframes); 273 #else 274 err = AudioDeviceSetProperty(device, &ts, 0, false, kAudioDevicePropertyBufferFrameSize, size, &numframes); 275 #endif 276 277 err = AudioUnitSetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size); 278 err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size); 279 err = AudioUnitSetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size); 280 err = AudioUnitSetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size); 281 282 printf("Interval: %dms, Frames: %d\n", (int) macSoundInterval_ms, (int) numframes); 283 } 284} 285 286static void ConnectAudioUnits (void) 287{ 288 OSStatus err; 289 AURenderCallbackStruct callback; 290 291 callback.inputProc = MacAURenderCallBack; 292 callback.inputProcRefCon = NULL; 293 294 if (systemVersion >= 0x1050) 295 err = AUGraphSetNodeInputCallback(agraph, aueffect ? cnvNode : outNode, 0, &callback); 296#ifdef MAC_TIGER_PANTHER_SUPPORT 297 else 298 err = AudioUnitSetProperty(aueffect ? cnvAU : outAU, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); 299#endif 300 301 if ((aueffect & kAUReverb) && (aueffect & kAUGraphEQ)) 302 { 303 err = AUGraphConnectNodeInput(agraph, cnvNode, 0, revNode, 0); 304 err = AUGraphConnectNodeInput(agraph, revNode, 0, eqlNode, 0); 305 err = AUGraphConnectNodeInput(agraph, eqlNode, 0, outNode, 0); 306 } 307 else 308 if (aueffect & kAUReverb) 309 { 310 err = AUGraphConnectNodeInput(agraph, cnvNode, 0, revNode, 0); 311 err = AUGraphConnectNodeInput(agraph, revNode, 0, outNode, 0); 312 } 313 else 314 if (aueffect & kAUGraphEQ) 315 { 316 err = AUGraphConnectNodeInput(agraph, cnvNode, 0, eqlNode, 0); 317 err = AUGraphConnectNodeInput(agraph, eqlNode, 0, outNode, 0); 318 } 319} 320 321static void DisconnectAudioUnits (void) 322{ 323 OSStatus err; 324 325 err = AUGraphClearConnections(agraph); 326} 327 328static OSStatus MacAURenderCallBack (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData) 329{ 330 if (Settings.Mute) 331 { 332 memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); 333 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence; 334 } 335 else 336 { 337 static bool recoverBufferUnderrun = false; 338 unsigned int samples = ioData->mBuffers[0].mDataByteSize >> 1; 339 340 pthread_mutex_lock(&mutex); 341 if (samples > audioBufferSampleCount || (recoverBufferUnderrun && audioBufferSampleCount<<1 < audioBufferSampleCapacity)) 342 { 343 /* buffer underrun - emit silence at least 50% of buffer is filled */ 344 bzero(ioData->mBuffers[0].mData, samples*2); 345 recoverBufferUnderrun = true; 346 } 347 else 348 { 349 recoverBufferUnderrun = false; 350 memcpy(ioData->mBuffers[0].mData, audioBuffer, samples*2); 351 memmove(audioBuffer, audioBuffer+samples, (audioBufferSampleCount-samples)*2); 352 audioBufferSampleCount -= samples; 353 sem_post(soundSyncSemaphore); 354 } 355 pthread_mutex_unlock(&mutex); 356 } 357 358 return (noErr); 359} 360 361static void MacSamplesAvailableCallBack (void *userData) 362{ 363 uint32_t availableSamples = S9xGetSampleCount(); 364 if (Settings.DynamicRateControl) 365 { 366 S9xUpdateDynamicRate((audioBufferSampleCapacity-audioBufferSampleCount)*2, audioBufferSampleCapacity*2); 367 } 368 369tryLock: 370 pthread_mutex_lock(&mutex); 371 if (audioBufferSampleCapacity - audioBufferSampleCount < availableSamples) 372 { 373 /* buffer overrun */ 374 if (Settings.DynamicRateControl && !Settings.SoundSync) 375 { 376 /* for dynamic rate control, clear S9x internal buffer and do nothing */ 377 pthread_mutex_unlock(&mutex); 378 S9xClearSamples(); 379 return; 380 } 381 if (Settings.SoundSync && !Settings.TurboMode) 382 { 383 /* when SoundSync is enabled, wait buffer for being drained by render callback */ 384 pthread_mutex_unlock(&mutex); 385 sem_wait(soundSyncSemaphore); 386 goto tryLock; 387 } 388 /* dispose samples to allocate 50% of the buffer capacity */ 389 uint32_t samplesToBeDisposed = availableSamples + audioBufferSampleCount - audioBufferSampleCapacity/2; 390 if(samplesToBeDisposed >= audioBufferSampleCount) 391 { 392 audioBufferSampleCount = 0; 393 } 394 else 395 { 396 memmove(audioBuffer, audioBuffer+samplesToBeDisposed, (audioBufferSampleCount-samplesToBeDisposed)*2); 397 audioBufferSampleCount = audioBufferSampleCount - samplesToBeDisposed; 398 } 399 } 400 S9xMixSamples((uint8 *)(audioBuffer+audioBufferSampleCount), availableSamples); 401 audioBufferSampleCount += availableSamples; 402 pthread_mutex_unlock(&mutex); 403} 404 405static void SaveEffectPresets (void) 406{ 407 OSStatus err; 408 AUPreset preset; 409 CFPropertyListRef classData; 410 UInt32 size; 411 412 preset.presetNumber = -1; // User Preset 413 preset.presetName = CFSTR("SNES9X Preset"); 414 415 err = AudioUnitSetProperty(revAU, kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0, &preset, sizeof(preset)); 416 if (err == noErr) 417 { 418 size = sizeof(classData); 419 err = AudioUnitGetProperty(revAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size); 420 if (err == noErr) 421 { 422 CFPreferencesSetAppValue(CFSTR("Effect_Preset_Reverb"), classData, kCFPreferencesCurrentApplication); 423 CFRelease(classData); 424 } 425 } 426 427 err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0, &preset, sizeof(preset)); 428 if (err == noErr) 429 { 430 size = sizeof(classData); 431 err = AudioUnitGetProperty(eqlAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size); 432 if (err == noErr) 433 { 434 CFPreferencesSetAppValue(CFSTR("Effect_Preset_GraphEQ"), classData, kCFPreferencesCurrentApplication); 435 CFRelease(classData); 436 } 437 } 438 439 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 440} 441 442static void LoadEffectPresets (void) 443{ 444 OSStatus err; 445 CFPropertyListRef classData; 446 447 classData = CFPreferencesCopyAppValue(CFSTR("Effect_Preset_Reverb"), kCFPreferencesCurrentApplication); 448 if (classData) 449 { 450 err = AudioUnitSetProperty(revAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, sizeof(classData)); 451 CFRelease(classData); 452 } 453 454 classData = CFPreferencesCopyAppValue(CFSTR("Effect_Preset_GraphEQ"), kCFPreferencesCurrentApplication); 455 if (classData) 456 { 457 err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, sizeof(classData)); 458 CFRelease(classData); 459 } 460} 461 462void MacStartSound (void) 463{ 464 OSStatus err; 465 Boolean r = false; 466 467 if (macQTRecord) 468 return; 469 470 err = AUGraphIsRunning(agraph, &r); 471 if (err == noErr && r == false) 472 { 473 err = AUGraphStart(agraph); 474 printf("AUGraph started.\n"); 475 } 476} 477 478void MacStopSound (void) 479{ 480 OSStatus err; 481 Boolean r = false; 482 483 if (macQTRecord) 484 return; 485 486 err = AUGraphIsRunning(agraph, &r); 487 if (err == noErr && r == true) 488 { 489 err = AUGraphStop(agraph); 490 printf("AUGraph stopped.\n"); 491 } 492} 493 494bool8 S9xOpenSoundDevice (void) 495{ 496 OSStatus err; 497 498 err = AUGraphUninitialize(agraph); 499 500 SetAudioUnitSoundFormat(); 501 SetAudioUnitVolume(); 502 ChangeBufferFrameSize(); 503 504 err = AUGraphInitialize(agraph); 505 506 if (audioBuffer) free(audioBuffer); 507 audioBufferSampleCapacity = 2 * macSoundBuffer_ms * Settings.SoundPlaybackRate / 1000; 508 audioBuffer = (int16_t *)calloc(audioBufferSampleCapacity,sizeof(int16_t)); 509 audioBufferSampleCount = 0; 510 return (true); 511} 512 513void PlayAlertSound (void) 514{ 515 if (systemVersion >= 0x1050) 516 AudioServicesPlayAlertSound(kUserPreferredAlert); 517#ifdef MAC_TIGER_PANTHER_SUPPORT 518 else 519 SysBeep(10); 520#endif 521} 522 523static void ReplaceAudioUnitCarbonView (void) 524{ 525 OSStatus err; 526 AudioUnit editau; 527 Component cmp; 528 ComponentDescription desc; 529 HIViewRef pane, contentview, ctl; 530 HIViewID cid; 531 Float32Point location, size; 532 Rect rct; 533 UInt32 psize; 534 535 if (carbonView) 536 { 537 err = RemoveEventHandler(carbonViewEventRef); 538 DisposeEventHandlerUPP(carbonViewEventUPP); 539 carbonViewEventRef = NULL; 540 carbonViewEventUPP = NULL; 541 542 CloseComponent(carbonView); 543 carbonView = NULL; 544 } 545 546 switch (cureffect) 547 { 548 case kAUGraphEQ: 549 editau = eqlAU; 550 break; 551 552 case kAUReverb: 553 default: 554 editau = revAU; 555 break; 556 } 557 558 desc.componentType = kAudioUnitCarbonViewComponentType; 559 desc.componentSubType = kAUCarbonViewSubType_Generic; 560 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 561 desc.componentFlags = 0; 562 desc.componentFlagsMask = 0; 563 564 err = AudioUnitGetPropertyInfo(editau, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &psize, NULL); 565 if (err == noErr) 566 { 567 ComponentDescription *editors; 568 int nEditors; 569 570 nEditors = psize / sizeof(ComponentDescription); 571 572 editors = new ComponentDescription[nEditors]; 573 574 err = AudioUnitGetProperty(editau, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, editors, &psize); 575 if (err == noErr) 576 desc = editors[0]; 577 578 delete [] editors; 579 } 580 581 HIViewFindByID(HIViewGetRoot(effectWRef), kHIViewWindowContentID, &contentview); 582 583 cmp = FindNextComponent(NULL, &desc); 584 if (cmp) 585 { 586 err = OpenAComponent(cmp, &carbonView); 587 if (err == noErr) 588 { 589 EventTypeSpec event[] = { { kEventClassControl, kEventControlBoundsChanged } }; 590 591 GetWindowBounds(effectWRef, kWindowContentRgn, &rct); 592 location.x = 20; 593 location.y = 96; 594 size.x = rct.right - rct.left; 595 size.y = rct.bottom - rct.top; 596 597 err = AudioUnitCarbonViewCreate(carbonView, editau, effectWRef, contentview, &location, &size, &pane); 598 599 carbonViewEventUPP = NewEventHandlerUPP(SoundEffectsCarbonViewEventHandler); 600 err = InstallControlEventHandler(pane, carbonViewEventUPP, GetEventTypeCount(event), event, (void *) effectWRef, &carbonViewEventRef); 601 602 ResizeSoundEffectsDialog(pane); 603 } 604 else 605 carbonView = NULL; 606 } 607 else 608 carbonView = NULL; 609 610 cid.id = 0; 611 cid.signature = 'Enab'; 612 HIViewFindByID(contentview, cid, &ctl); 613 SetControl32BitValue(ctl, (aueffect & cureffect) ? 1 : 0); 614} 615 616static void ResizeSoundEffectsDialog (HIViewRef view) 617{ 618 OSStatus err; 619 HIViewRef ctl, root; 620 HIViewID cid; 621 HIRect bounds; 622 Rect rv; 623 int w, h; 624 625 root = HIViewGetRoot(effectWRef); 626 627 cid.id = 0; 628 cid.signature = 'Enab'; 629 HIViewFindByID(root, cid, &ctl); 630 631 err = HIViewSetVisible(ctl, false); 632 err = HIViewSetVisible(view, false); 633 634 HIViewGetBounds(view, &bounds); 635 w = ((int) bounds.size.width + 30 > (int) effectWSize.width) ? ((int) bounds.size.width + 30) : (int) effectWSize.width; 636 h = (int) bounds.size.height + 122; 637#ifdef MAC_PANTHER_SUPPORT 638 if (systemVersion < 0x1040) 639 h += 16; 640#endif 641 GetWindowBounds(effectWRef, kWindowStructureRgn, &rv); 642 rv.right = rv.left + w; 643 rv.bottom = rv.top + h; 644 err = TransitionWindow(effectWRef, kWindowSlideTransitionEffect, kWindowResizeTransitionAction, &rv); 645 646 err = HIViewSetVisible(ctl, true); 647 err = HIViewSetVisible(view, true); 648 649#ifdef MAC_PANTHER_SUPPORT 650 if (systemVersion < 0x1040) 651 { 652 HIRect frame; 653 Rect rct; 654 655 GetWindowBounds(effectWRef, kWindowContentRgn, &rv); 656 657 cid.signature = 'SfUI'; 658 HIViewFindByID(root, cid, &ctl); 659 HIViewGetFrame(ctl, &frame); 660 frame.size.width = (float) (rv.right - rv.left); 661 HIViewSetFrame(ctl, &frame); 662 663 cid.signature = 'LINE'; 664 HIViewFindByID(root, cid, &ctl); 665 HIViewGetFrame(ctl, &frame); 666 frame.size.width = (float) (rv.right - rv.left - 24); 667 HIViewSetFrame(ctl, &frame); 668 669 rct.top = 0; 670 rct.left = 0; 671 rct.bottom = rv.bottom - rv.top; 672 rct.right = rv.right - rv.left; 673 err = InvalWindowRect(effectWRef, &rct); 674 } 675#endif 676} 677 678void ConfigureSoundEffects (void) 679{ 680 OSStatus err; 681 IBNibRef nibRef; 682 683 err = CreateNibReference(kMacS9XCFString, &nibRef); 684 if (err == noErr) 685 { 686 WindowRef uiparts; 687 688 err = CreateWindowFromNib(nibRef, CFSTR("SoundEffect"), &uiparts); 689 if (err == noErr) 690 { 691 EventHandlerUPP eventUPP; 692 EventHandlerRef eventHandler; 693 EventTypeSpec event[] = { { kEventClassWindow, kEventWindowClose }, 694 { kEventClassCommand, kEventCommandProcess }, 695 { kEventClassCommand, kEventCommandUpdateStatus } }; 696 HIViewRef ctl, userpane, contentview; 697 HIViewID cid; 698 CFStringRef str; 699 Rect rct; 700 WindowAttributes metal = 0; 701 702 cid.id = 0; 703 cid.signature = 'SfUI'; 704 HIViewFindByID(HIViewGetRoot(uiparts), cid, &userpane); 705 GetWindowBounds(uiparts, kWindowContentRgn, &rct); 706 707 if (systemVersion >= 0x1040) // AUs support compositing 708 { 709 HIRect frame; 710 711 str = CFCopyLocalizedString(CFSTR("CreateMetalDlg"), "NO"); 712 if (str) 713 { 714 if (CFStringCompare(str, CFSTR("YES"), 0) == kCFCompareEqualTo) 715 metal = kWindowMetalAttribute; 716 717 CFRelease(str); 718 } 719 720 frame = CGRectMake(0.0f, 0.0f, (float) (rct.right - rct.left), (float) (rct.bottom - rct.top)); 721 err = CreateNewWindow(kDocumentWindowClass, kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute | kWindowCompositingAttribute | metal, &rct, &effectWRef); 722 err = HIViewFindByID(HIViewGetRoot(effectWRef), kHIViewWindowContentID, &contentview); 723 err = HIViewAddSubview(contentview, userpane); 724 err = HIViewSetFrame(userpane, &frame); 725 } 726 #ifdef MAC_PANTHER_SUPPORT 727 else 728 { 729 err = CreateNewWindow(kDocumentWindowClass, kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute, &rct, &effectWRef); 730 err = CreateRootControl(effectWRef, &contentview); 731 err = EmbedControl(userpane, contentview); 732 MoveControl(userpane, 0, 0); 733 } 734 #endif 735 736 CFRelease(uiparts); 737 738 if (!metal) 739 err = SetThemeWindowBackground(effectWRef, kThemeBrushDialogBackgroundActive, false); 740 741 str = CFCopyLocalizedString(CFSTR("SoundEffectDlg"), "SoundEffect"); 742 if (str) 743 { 744 err = SetWindowTitleWithCFString(effectWRef, str); 745 CFRelease(str); 746 } 747 748 if (systemVersion >= 0x1040) // AUs support compositing 749 { 750 HILayoutInfo layoutinfo; 751 HIViewRef separator; 752 753 cid.signature = 'LINE'; 754 err = HIViewFindByID(userpane, cid, &separator); 755 756 layoutinfo.version = kHILayoutInfoVersionZero; 757 err = HIViewGetLayoutInfo(userpane, &layoutinfo); 758 759 layoutinfo.binding.top.toView = contentview; 760 layoutinfo.binding.top.kind = kHILayoutBindNone; 761 layoutinfo.binding.bottom.toView = contentview; 762 layoutinfo.binding.bottom.kind = kHILayoutBindNone; 763 layoutinfo.binding.left.toView = contentview; 764 layoutinfo.binding.left.kind = kHILayoutBindLeft; 765 layoutinfo.binding.right.toView = contentview; 766 layoutinfo.binding.right.kind = kHILayoutBindRight; 767 err = HIViewSetLayoutInfo(userpane, &layoutinfo); 768 769 layoutinfo.version = kHILayoutInfoVersionZero; 770 err = HIViewGetLayoutInfo(separator, &layoutinfo); 771 772 layoutinfo.binding.top.toView = userpane; 773 layoutinfo.binding.top.kind = kHILayoutBindNone; 774 layoutinfo.binding.bottom.toView = userpane; 775 layoutinfo.binding.bottom.kind = kHILayoutBindNone; 776 layoutinfo.binding.left.toView = userpane; 777 layoutinfo.binding.left.kind = kHILayoutBindLeft; 778 layoutinfo.binding.right.toView = userpane; 779 layoutinfo.binding.right.kind = kHILayoutBindRight; 780 err = HIViewSetLayoutInfo(separator, &layoutinfo); 781 } 782 783 eventUPP = NewEventHandlerUPP(SoundEffectsEventHandler); 784 err = InstallWindowEventHandler(effectWRef, eventUPP, GetEventTypeCount(event), event, (void *) effectWRef, &eventHandler); 785 786 GetWindowBounds(effectWRef, kWindowContentRgn, &rct); 787 effectWSize.width = (float) (rct.right - rct.left); 788 effectWSize.height = (float) (rct.bottom - rct.top ); 789 790 carbonView = NULL; 791 ReplaceAudioUnitCarbonView(); 792 793 cid.signature = 'Epop'; 794 HIViewFindByID(userpane, cid, &ctl); 795 switch (cureffect) 796 { 797 case kAUReverb: 798 SetControl32BitValue(ctl, 1); 799 break; 800 801 case kAUGraphEQ: 802 SetControl32BitValue(ctl, 2); 803 break; 804 } 805 806 MoveWindowPosition(effectWRef, kWindowSoundEffect, false); 807 ShowWindow(effectWRef); 808 err = RunAppModalLoopForWindow(effectWRef); 809 HideWindow(effectWRef); 810 SaveWindowPosition(effectWRef, kWindowSoundEffect); 811 812 if (carbonView) 813 { 814 err = RemoveEventHandler(carbonViewEventRef); 815 DisposeEventHandlerUPP(carbonViewEventUPP); 816 carbonViewEventRef = NULL; 817 carbonViewEventUPP = NULL; 818 819 CloseComponent(carbonView); 820 carbonView = NULL; 821 } 822 823 err = RemoveEventHandler(eventHandler); 824 DisposeEventHandlerUPP(eventUPP); 825 826 CFRelease(effectWRef); 827 } 828 829 DisposeNibReference(nibRef); 830 } 831} 832 833static pascal OSStatus SoundEffectsCarbonViewEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) 834{ 835 OSStatus err, result = eventNotHandledErr; 836 HIViewRef ctl; 837 HIRect bounds; 838 839 switch (GetEventClass(inEvent)) 840 { 841 case kEventClassControl: 842 switch (GetEventKind(inEvent)) 843 { 844 case kEventControlBoundsChanged: 845 err = GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &ctl); 846 if (err == noErr) 847 { 848 err = GetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &bounds); 849 if (err == noErr) 850 { 851 if ((bounds.size.width > 0) && (bounds.size.height > 0)) 852 ResizeSoundEffectsDialog(ctl); 853 } 854 } 855 856 result = noErr; 857 break; 858 } 859 860 break; 861 } 862 863 return (result); 864} 865 866static pascal OSStatus SoundEffectsEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) 867{ 868 OSStatus err, result = eventNotHandledErr; 869 WindowRef tWindowRef = (WindowRef) inUserData; 870 871 switch (GetEventClass(inEvent)) 872 { 873 case kEventClassWindow: 874 switch (GetEventKind(inEvent)) 875 { 876 case kEventWindowClose: 877 QuitAppModalLoopForWindow(tWindowRef); 878 result = noErr; 879 break; 880 } 881 882 break; 883 884 case kEventClassCommand: 885 switch (GetEventKind(inEvent)) 886 { 887 HICommand tHICommand; 888 889 case kEventCommandUpdateStatus: 890 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); 891 if (err == noErr && tHICommand.commandID == 'clos') 892 { 893 UpdateMenuCommandStatus(true); 894 result = noErr; 895 } 896 897 break; 898 899 case kEventCommandProcess: 900 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); 901 if (err == noErr) 902 { 903 switch (tHICommand.commandID) 904 { 905 case 'Enab': 906 { 907 Boolean r = false; 908 909 mboxPause = true; 910 911 err = AUGraphIsRunning(agraph, &r); 912 if (err == noErr && r) 913 err = AUGraphStop(agraph); 914 915 DisconnectAudioUnits(); 916 err = AUGraphUninitialize(agraph); 917 918 aueffect ^= cureffect; 919 920 SetAudioUnitSoundFormat(); 921 ChangeBufferFrameSize(); 922 923 err = AUGraphInitialize(agraph); 924 ConnectAudioUnits(); 925 926 if (r) 927 err = AUGraphStart(agraph); 928 929 mboxPause = false; 930 931 result = noErr; 932 break; 933 } 934 935 case 'Revb': 936 cureffect = kAUReverb; 937 ReplaceAudioUnitCarbonView(); 938 break; 939 940 case 'GrEQ': 941 cureffect = kAUGraphEQ; 942 ReplaceAudioUnitCarbonView(); 943 break; 944 } 945 } 946 947 break; 948 } 949 950 break; 951 } 952 953 return (result); 954} 955