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