1/***********************************************************************************
2  Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4  (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com),
5                             Jerremy Koot (jkoot@snes9x.com)
6
7  (c) Copyright 2002 - 2004  Matthew Kendora
8
9  (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10
11  (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12
13  (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14
15  (c) Copyright 2002 - 2006  funkyass (funkyass@spam.shaw.ca),
16                             Kris Bleakley (codeviolation@hotmail.com)
17
18  (c) Copyright 2002 - 2010  Brad Jorsch (anomie@users.sourceforge.net),
19                             Nach (n-a-c-h@users.sourceforge.net),
20
21  (c) Copyright 2002 - 2011  zones (kasumitokoduck@yahoo.com)
22
23  (c) Copyright 2006 - 2007  nitsuja
24
25  (c) Copyright 2009 - 2016  BearOso,
26                             OV2
27
28
29  BS-X C emulator code
30  (c) Copyright 2005 - 2006  Dreamer Nom,
31                             zones
32
33  C4 x86 assembler and some C emulation code
34  (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
35                             Nach,
36                             zsKnight (zsknight@zsnes.com)
37
38  C4 C++ code
39  (c) Copyright 2003 - 2006  Brad Jorsch,
40                             Nach
41
42  DSP-1 emulator code
43  (c) Copyright 1998 - 2006  _Demo_,
44                             Andreas Naive (andreasnaive@gmail.com),
45                             Gary Henderson,
46                             Ivar (ivar@snes9x.com),
47                             John Weidman,
48                             Kris Bleakley,
49                             Matthew Kendora,
50                             Nach,
51                             neviksti (neviksti@hotmail.com)
52
53  DSP-2 emulator code
54  (c) Copyright 2003         John Weidman,
55                             Kris Bleakley,
56                             Lord Nightmare (lord_nightmare@users.sourceforge.net),
57                             Matthew Kendora,
58                             neviksti
59
60  DSP-3 emulator code
61  (c) Copyright 2003 - 2006  John Weidman,
62                             Kris Bleakley,
63                             Lancer,
64                             z80 gaiden
65
66  DSP-4 emulator code
67  (c) Copyright 2004 - 2006  Dreamer Nom,
68                             John Weidman,
69                             Kris Bleakley,
70                             Nach,
71                             z80 gaiden
72
73  OBC1 emulator code
74  (c) Copyright 2001 - 2004  zsKnight,
75                             pagefault (pagefault@zsnes.com),
76                             Kris Bleakley
77                             Ported from x86 assembler to C by sanmaiwashi
78
79  SPC7110 and RTC C++ emulator code used in 1.39-1.51
80  (c) Copyright 2002         Matthew Kendora with research by
81                             zsKnight,
82                             John Weidman,
83                             Dark Force
84
85  SPC7110 and RTC C++ emulator code used in 1.52+
86  (c) Copyright 2009         byuu,
87                             neviksti
88
89  S-DD1 C emulator code
90  (c) Copyright 2003         Brad Jorsch with research by
91                             Andreas Naive,
92                             John Weidman
93
94  S-RTC C emulator code
95  (c) Copyright 2001 - 2006  byuu,
96                             John Weidman
97
98  ST010 C++ emulator code
99  (c) Copyright 2003         Feather,
100                             John Weidman,
101                             Kris Bleakley,
102                             Matthew Kendora
103
104  Super FX x86 assembler emulator code
105  (c) Copyright 1998 - 2003  _Demo_,
106                             pagefault,
107                             zsKnight
108
109  Super FX C emulator code
110  (c) Copyright 1997 - 1999  Ivar,
111                             Gary Henderson,
112                             John Weidman
113
114  Sound emulator code used in 1.5-1.51
115  (c) Copyright 1998 - 2003  Brad Martin
116  (c) Copyright 1998 - 2006  Charles Bilyue'
117
118  Sound emulator code used in 1.52+
119  (c) Copyright 2004 - 2007  Shay Green (gblargg@gmail.com)
120
121  S-SMP emulator code used in 1.54+
122  (c) Copyright 2016         byuu
123
124  SH assembler code partly based on x86 assembler code
125  (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
126
127  2xSaI filter
128  (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
129
130  HQ2x, HQ3x, HQ4x filters
131  (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
132
133  NTSC filter
134  (c) Copyright 2006 - 2007  Shay Green
135
136  GTK+ GUI code
137  (c) Copyright 2004 - 2016  BearOso
138
139  Win32 GUI code
140  (c) Copyright 2003 - 2006  blip,
141                             funkyass,
142                             Matthew Kendora,
143                             Nach,
144                             nitsuja
145  (c) Copyright 2009 - 2016  OV2
146
147  Mac OS GUI code
148  (c) Copyright 1998 - 2001  John Stiles
149  (c) Copyright 2001 - 2011  zones
150
151
152  Specific ports contains the works of other authors. See headers in
153  individual files.
154
155
156  Snes9x homepage: http://www.snes9x.com/
157
158  Permission to use, copy, modify and/or distribute Snes9x in both binary
159  and source form, for non-commercial purposes, is hereby granted without
160  fee, providing that this license information and copyright notice appear
161  with all copies and any derived work.
162
163  This software is provided 'as-is', without any express or implied
164  warranty. In no event shall the authors be held liable for any damages
165  arising from the use of this software or it's derivatives.
166
167  Snes9x is freeware for PERSONAL USE only. Commercial users should
168  seek permission of the copyright holders first. Commercial use includes,
169  but is not limited to, charging money for Snes9x or software derived from
170  Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171  using Snes9x as a promotion for your commercial product.
172
173  The copyright holders request that bug fixes and improvements to the code
174  should be forwarded to them so everyone can benefit from the modifications
175  in future versions.
176
177  Super NES and Super Nintendo Entertainment System are trademarks of
178  Nintendo Co., Limited and its subsidiary companies.
179 ***********************************************************************************/
180
181/***********************************************************************************
182  SNES9X for Mac OS (c) Copyright John Stiles
183
184  Snes9x for Mac OS X
185
186  (c) Copyright 2001 - 2011  zones
187  (c) Copyright 2002 - 2005  107
188  (c) Copyright 2002         PB1400c
189  (c) Copyright 2004         Alexander and Sander
190  (c) Copyright 2004 - 2005  Steven Seeger
191  (c) Copyright 2005         Ryan Vogt
192 ***********************************************************************************/
193
194
195#include "snes9x.h"
196#include "apu.h"
197
198#include <Cocoa/Cocoa.h>
199#include <CoreAudio/CoreAudio.h>
200#include <AudioToolbox/AudioToolbox.h>
201#include <AudioUnit/AudioUnitCarbonView.h>
202#include <pthread.h>
203
204#include "mac-prefix.h"
205#include "mac-dialog.h"
206#include "mac-musicbox.h"
207#include "mac-os.h"
208#include "mac-snes9x.h"
209#include "mac-audio.h"
210
211#define	kAUReverb	(1 << 0)
212#define	kAUGraphEQ	(1 << 1)
213
214int	cureffect = kAUReverb;
215
216static AUGraph				agraph;
217static AUNode				outNode, cnvNode, revNode, eqlNode;
218static AudioUnit			outAU, cnvAU, revAU, eqlAU;
219static AudioUnitCarbonView	carbonView         = NULL;
220static EventHandlerUPP		carbonViewEventUPP = NULL;
221static EventHandlerRef		carbonViewEventRef = NULL;
222static WindowRef			effectWRef;
223static HISize				effectWSize;
224static pthread_mutex_t		mutex;
225static UInt32				outStoredFrames, cnvStoredFrames, revStoredFrames, eqlStoredFrames, devStoredFrames;
226
227static void ConnectAudioUnits (void);
228static void DisconnectAudioUnits (void);
229static void SaveEffectPresets (void);
230static void LoadEffectPresets (void);
231static void SetAudioUnitSoundFormat (void);
232static void SetAudioUnitVolume (void);
233static void StoreBufferFrameSize (void);
234static void ChangeBufferFrameSize (void);
235static void ReplaceAudioUnitCarbonView (void);
236static void ResizeSoundEffectsDialog (HIViewRef);
237static void MacFinalizeSamplesCallBack (void *);
238static OSStatus MacAURenderCallBack (void *, AudioUnitRenderActionFlags *, const AudioTimeStamp *, UInt32, UInt32, AudioBufferList *);
239static pascal OSStatus SoundEffectsEventHandler (EventHandlerCallRef, EventRef, void *);
240static pascal OSStatus SoundEffectsCarbonViewEventHandler (EventHandlerCallRef, EventRef, void *);
241
242
243void InitMacSound (void)
244{
245	OSStatus	err;
246
247	err = NewAUGraph(&agraph);
248
249#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
250	AudioComponentDescription	outdesc, cnvdesc, revdesc, eqldesc;
251#else
252	ComponentDescription		outdesc, cnvdesc, revdesc, eqldesc;
253#endif
254
255	outdesc.componentType         = kAudioUnitType_Output;
256	outdesc.componentSubType      = kAudioUnitSubType_DefaultOutput;
257	outdesc.componentManufacturer = 0;
258	outdesc.componentFlags        = 0;
259	outdesc.componentFlagsMask    = 0;
260
261	cnvdesc.componentType         = kAudioUnitType_FormatConverter;
262	cnvdesc.componentSubType      = kAudioUnitSubType_AUConverter;
263	cnvdesc.componentManufacturer = kAudioUnitManufacturer_Apple;
264	cnvdesc.componentFlags        = 0;
265	cnvdesc.componentFlagsMask    = 0;
266
267	revdesc.componentType         = kAudioUnitType_Effect;
268	revdesc.componentSubType      = kAudioUnitSubType_MatrixReverb;
269	revdesc.componentManufacturer = kAudioUnitManufacturer_Apple;
270	revdesc.componentFlags        = 0;
271	revdesc.componentFlagsMask    = 0;
272
273	eqldesc.componentType         = kAudioUnitType_Effect;
274	eqldesc.componentSubType      = kAudioUnitSubType_GraphicEQ;
275	eqldesc.componentManufacturer = kAudioUnitManufacturer_Apple;
276	eqldesc.componentFlags        = 0;
277	eqldesc.componentFlagsMask    = 0;
278
279#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
280	err = AUGraphAddNode(agraph, &outdesc, &outNode);
281	err = AUGraphAddNode(agraph, &cnvdesc, &cnvNode);
282	err = AUGraphAddNode(agraph, &revdesc, &revNode);
283	err = AUGraphAddNode(agraph, &eqldesc, &eqlNode);
284#else
285	err = AUGraphNewNode(agraph, &outdesc, 0, NULL, &outNode);
286	err = AUGraphNewNode(agraph, &cnvdesc, 0, NULL, &cnvNode);
287	err = AUGraphNewNode(agraph, &revdesc, 0, NULL, &revNode);
288	err = AUGraphNewNode(agraph, &eqldesc, 0, NULL, &eqlNode);
289#endif
290
291	err = AUGraphOpen(agraph);
292
293#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
294	err = AUGraphNodeInfo(agraph, outNode, NULL, &outAU);
295	err = AUGraphNodeInfo(agraph, cnvNode, NULL, &cnvAU);
296	err = AUGraphNodeInfo(agraph, revNode, NULL, &revAU);
297	err = AUGraphNodeInfo(agraph, eqlNode, NULL, &eqlAU);
298#else
299	err = AUGraphGetNodeInfo(agraph, outNode, NULL, NULL, NULL, &outAU);
300	err = AUGraphGetNodeInfo(agraph, cnvNode, NULL, NULL, NULL, &cnvAU);
301	err = AUGraphGetNodeInfo(agraph, revNode, NULL, NULL, NULL, &revAU);
302	err = AUGraphGetNodeInfo(agraph, eqlNode, NULL, NULL, NULL, &eqlAU);
303#endif
304
305	SetAudioUnitSoundFormat();
306	SetAudioUnitVolume();
307	StoreBufferFrameSize();
308	ChangeBufferFrameSize();
309
310	err = AUGraphInitialize(agraph);
311
312	ConnectAudioUnits();
313	LoadEffectPresets();
314
315	pthread_mutex_init(&mutex, NULL);
316	S9xSetSamplesAvailableCallback(MacFinalizeSamplesCallBack, NULL);
317}
318
319void DeinitMacSound (void)
320{
321	OSStatus	err;
322
323	pthread_mutex_destroy(&mutex);
324	SaveEffectPresets();
325	DisconnectAudioUnits();
326	err = AUGraphUninitialize(agraph);
327	err = AUGraphClose(agraph);
328	err = DisposeAUGraph(agraph);
329}
330
331static void SetAudioUnitSoundFormat (void)
332{
333	OSStatus					err;
334	AudioStreamBasicDescription	format;
335
336#ifdef __BIG_ENDIAN__
337	UInt32	endian = kLinearPCMFormatFlagIsBigEndian;
338#else
339	UInt32	endian = 0;
340#endif
341
342	memset(&format, 0, sizeof(format));
343
344	format.mSampleRate	     = (Float64) Settings.SoundPlaybackRate;
345	format.mFormatID	     = kAudioFormatLinearPCM;
346	format.mFormatFlags	     = endian | (Settings.SixteenBitSound ? kLinearPCMFormatFlagIsSignedInteger : 0);
347	format.mBytesPerPacket   = 2 * (Settings.SixteenBitSound ? 2 : 1);
348	format.mFramesPerPacket  = 1;
349	format.mBytesPerFrame    = 2 * (Settings.SixteenBitSound ? 2 : 1);
350	format.mChannelsPerFrame = 2;
351	format.mBitsPerChannel   = Settings.SixteenBitSound ? 16 : 8;
352
353	err = AudioUnitSetProperty(aueffect ? cnvAU : outAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format));
354}
355
356static void SetAudioUnitVolume (void)
357{
358	OSStatus	err;
359
360	err = AudioUnitSetParameter(outAU, kAudioUnitParameterUnit_LinearGain, kAudioUnitScope_Output, 0, (float) macSoundVolume / 100.0f, 0);
361}
362
363static void StoreBufferFrameSize (void)
364{
365	OSStatus					err;
366	UInt32						size;
367	AudioDeviceID				device;
368#ifndef MAC_PANTHER_SUPPORT
369	AudioObjectPropertyAddress	address;
370
371	address.mSelector = kAudioDevicePropertyBufferFrameSize;
372	address.mScope    = kAudioObjectPropertyScopeGlobal;
373	address.mElement  = kAudioObjectPropertyElementMaster;
374#endif
375
376	size = sizeof(AudioDeviceID);
377	err = AudioUnitGetProperty(outAU, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &size);
378
379	size = sizeof(UInt32);
380#ifndef MAC_PANTHER_SUPPORT
381	err = AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &devStoredFrames);
382#else
383	err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &devStoredFrames);
384#endif
385
386	size = sizeof(UInt32);
387	err = AudioUnitGetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &outStoredFrames, &size);
388	size = sizeof(UInt32);
389	err = AudioUnitGetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &eqlStoredFrames, &size);
390	size = sizeof(UInt32);
391	err = AudioUnitGetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &revStoredFrames, &size);
392	size = sizeof(UInt32);
393	err = AudioUnitGetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &cnvStoredFrames, &size);
394}
395
396static void ChangeBufferFrameSize (void)
397{
398	OSStatus					err;
399	UInt32						numframes, size;
400	AudioDeviceID				device;
401#ifndef MAC_PANTHER_SUPPORT
402	AudioObjectPropertyAddress	address;
403
404	address.mSelector = kAudioDevicePropertyBufferFrameSize;
405	address.mScope    = kAudioObjectPropertyScopeGlobal;
406	address.mElement  = kAudioObjectPropertyElementMaster;
407#else
408	AudioTimeStamp				ts;
409
410	ts.mFlags = 0;
411#endif
412
413	size = sizeof(AudioDeviceID);
414	err = AudioUnitGetProperty(outAU, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device, &size);
415
416	size = sizeof(UInt32);
417
418	if (macSoundInterval_ms == 0)
419	{
420	#ifndef MAC_PANTHER_SUPPORT
421		err = AudioObjectSetPropertyData(device, &address, 0, NULL, size, &devStoredFrames);
422	#else
423		err = AudioDeviceSetProperty(device, &ts, 0, false, kAudioDevicePropertyBufferFrameSize, size, &devStoredFrames);
424	#endif
425
426		err = AudioUnitSetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &outStoredFrames, size);
427		err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &eqlStoredFrames, size);
428		err = AudioUnitSetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &revStoredFrames, size);
429		err = AudioUnitSetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &cnvStoredFrames, size);
430
431		printf("Interval: system, Frames: %d/%d/%d/%d/%d\n", (int) devStoredFrames, (int) outStoredFrames, (int) eqlStoredFrames, (int) revStoredFrames, (int) cnvStoredFrames);
432	}
433	else
434	{
435		numframes = macSoundInterval_ms * Settings.SoundPlaybackRate / 1000;
436
437	#ifndef MAC_PANTHER_SUPPORT
438		err = AudioObjectSetPropertyData(device, &address, 0, NULL, size, &numframes);
439	#else
440		err = AudioDeviceSetProperty(device, &ts, 0, false, kAudioDevicePropertyBufferFrameSize, size, &numframes);
441	#endif
442
443		err = AudioUnitSetProperty(outAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size);
444		err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size);
445		err = AudioUnitSetProperty(revAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size);
446		err = AudioUnitSetProperty(cnvAU, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numframes, size);
447
448		printf("Interval: %dms, Frames: %d\n", (int) macSoundInterval_ms, (int) numframes);
449	}
450}
451
452static void ConnectAudioUnits (void)
453{
454	OSStatus				err;
455	AURenderCallbackStruct	callback;
456
457	callback.inputProc       = MacAURenderCallBack;
458	callback.inputProcRefCon = NULL;
459
460	if (systemVersion >= 0x1050)
461		err = AUGraphSetNodeInputCallback(agraph, aueffect ? cnvNode : outNode, 0, &callback);
462#ifdef MAC_TIGER_PANTHER_SUPPORT
463	else
464		err = AudioUnitSetProperty(aueffect ? cnvAU : outAU, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback));
465#endif
466
467	if ((aueffect & kAUReverb) && (aueffect & kAUGraphEQ))
468	{
469		err = AUGraphConnectNodeInput(agraph, cnvNode, 0, revNode, 0);
470		err = AUGraphConnectNodeInput(agraph, revNode, 0, eqlNode, 0);
471		err = AUGraphConnectNodeInput(agraph, eqlNode, 0, outNode, 0);
472	}
473	else
474	if (aueffect & kAUReverb)
475	{
476		err = AUGraphConnectNodeInput(agraph, cnvNode, 0, revNode, 0);
477		err = AUGraphConnectNodeInput(agraph, revNode, 0, outNode, 0);
478	}
479	else
480	if (aueffect & kAUGraphEQ)
481	{
482		err = AUGraphConnectNodeInput(agraph, cnvNode, 0, eqlNode, 0);
483		err = AUGraphConnectNodeInput(agraph, eqlNode, 0, outNode, 0);
484	}
485}
486
487static void DisconnectAudioUnits (void)
488{
489	OSStatus	err;
490
491	err = AUGraphClearConnections(agraph);
492}
493
494static OSStatus MacAURenderCallBack (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData)
495{
496	if (Settings.Mute)
497	{
498		memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
499		*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
500	}
501	else
502	if (Settings.Stereo)
503	{
504		unsigned int	samples;
505
506		samples = ioData->mBuffers[0].mDataByteSize;
507		if (Settings.SixteenBitSound)
508			samples >>= 1;
509
510		pthread_mutex_lock(&mutex);
511		S9xMixSamples((uint8 *) ioData->mBuffers[0].mData, samples);
512		pthread_mutex_unlock(&mutex);
513	}
514	else	// Manually map L to R
515	{
516		unsigned int	monosmp;
517
518		monosmp = ioData->mBuffers[0].mDataByteSize >> 1;
519		if (Settings.SixteenBitSound)
520			monosmp >>= 1;
521
522		pthread_mutex_lock(&mutex);
523		S9xMixSamples((uint8 *) ioData->mBuffers[0].mData, monosmp);
524		pthread_mutex_unlock(&mutex);
525
526		if (Settings.SixteenBitSound)
527		{
528			for (int i = monosmp - 1; i >= 0; i--)
529				((int16 *) ioData->mBuffers[0].mData)[i * 2 + 1] = ((int16 *) ioData->mBuffers[0].mData)[i * 2] = ((int16 *) ioData->mBuffers[0].mData)[i];
530		}
531		else
532		{
533			for (int i = monosmp - 1; i >= 0; i--)
534				((int8  *) ioData->mBuffers[0].mData)[i * 2 + 1] = ((int8  *) ioData->mBuffers[0].mData)[i * 2] = ((int8  *) ioData->mBuffers[0].mData)[i];
535		}
536	}
537
538	return (noErr);
539}
540
541static void MacFinalizeSamplesCallBack (void *userData)
542{
543	pthread_mutex_lock(&mutex);
544	S9xFinalizeSamples();
545	pthread_mutex_unlock(&mutex);
546}
547
548static void SaveEffectPresets (void)
549{
550	OSStatus			err;
551	AUPreset			preset;
552	CFPropertyListRef	classData;
553	UInt32				size;
554
555	preset.presetNumber = -1;	// User Preset
556	preset.presetName   = CFSTR("SNES9X Preset");
557
558	err = AudioUnitSetProperty(revAU, kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0, &preset, sizeof(preset));
559	if (err == noErr)
560	{
561		size = sizeof(classData);
562		err = AudioUnitGetProperty(revAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size);
563		if (err == noErr)
564		{
565			CFPreferencesSetAppValue(CFSTR("Effect_Preset_Reverb"),  classData, kCFPreferencesCurrentApplication);
566			CFRelease(classData);
567		}
568	}
569
570	err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0, &preset, sizeof(preset));
571	if (err == noErr)
572	{
573		size = sizeof(classData);
574		err = AudioUnitGetProperty(eqlAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size);
575		if (err == noErr)
576		{
577			CFPreferencesSetAppValue(CFSTR("Effect_Preset_GraphEQ"), classData, kCFPreferencesCurrentApplication);
578			CFRelease(classData);
579		}
580	}
581
582	CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
583}
584
585static void LoadEffectPresets (void)
586{
587	OSStatus			err;
588	CFPropertyListRef	classData;
589
590	classData = CFPreferencesCopyAppValue(CFSTR("Effect_Preset_Reverb"),  kCFPreferencesCurrentApplication);
591	if (classData)
592	{
593		err = AudioUnitSetProperty(revAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, sizeof(classData));
594		CFRelease(classData);
595	}
596
597	classData = CFPreferencesCopyAppValue(CFSTR("Effect_Preset_GraphEQ"), kCFPreferencesCurrentApplication);
598	if (classData)
599	{
600		err = AudioUnitSetProperty(eqlAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, sizeof(classData));
601		CFRelease(classData);
602	}
603}
604
605void MacStartSound (void)
606{
607	OSStatus	err;
608	Boolean		r = false;
609
610	if (macQTRecord)
611		return;
612
613	err = AUGraphIsRunning(agraph, &r);
614	if (err == noErr && r == false)
615	{
616		err = AUGraphStart(agraph);
617		printf("AUGraph started.\n");
618	}
619}
620
621void MacStopSound (void)
622{
623	OSStatus	err;
624	Boolean		r = false;
625
626	if (macQTRecord)
627		return;
628
629	err = AUGraphIsRunning(agraph, &r);
630	if (err == noErr && r == true)
631	{
632		err = AUGraphStop(agraph);
633		printf("AUGraph stopped.\n");
634	}
635}
636
637bool8 S9xOpenSoundDevice (void)
638{
639	OSStatus	err;
640
641	err = AUGraphUninitialize(agraph);
642
643	SetAudioUnitSoundFormat();
644	SetAudioUnitVolume();
645	ChangeBufferFrameSize();
646
647	err = AUGraphInitialize(agraph);
648
649	return (true);
650}
651
652void PlayAlertSound (void)
653{
654	if (systemVersion >= 0x1050)
655		AudioServicesPlayAlertSound(kUserPreferredAlert);
656#ifdef MAC_TIGER_PANTHER_SUPPORT
657	else
658		SysBeep(10);
659#endif
660}
661
662static void ReplaceAudioUnitCarbonView (void)
663{
664	OSStatus				err;
665	AudioUnit				editau;
666	Component				cmp;
667	ComponentDescription	desc;
668	HIViewRef				pane, contentview, ctl;
669	HIViewID				cid;
670	Float32Point			location, size;
671	Rect					rct;
672	UInt32					psize;
673
674	if (carbonView)
675	{
676		err = RemoveEventHandler(carbonViewEventRef);
677		DisposeEventHandlerUPP(carbonViewEventUPP);
678		carbonViewEventRef = NULL;
679		carbonViewEventUPP = NULL;
680
681		CloseComponent(carbonView);
682		carbonView = NULL;
683	}
684
685	switch (cureffect)
686	{
687		case kAUGraphEQ:
688			editau = eqlAU;
689			break;
690
691		case kAUReverb:
692		default:
693			editau = revAU;
694			break;
695	}
696
697	desc.componentType         = kAudioUnitCarbonViewComponentType;
698	desc.componentSubType      = kAUCarbonViewSubType_Generic;
699	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
700	desc.componentFlags        = 0;
701	desc.componentFlagsMask    = 0;
702
703	err = AudioUnitGetPropertyInfo(editau, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &psize, NULL);
704	if (err == noErr)
705	{
706		ComponentDescription	*editors;
707		int						nEditors;
708
709		nEditors = psize / sizeof(ComponentDescription);
710
711		editors = new ComponentDescription[nEditors];
712
713		err = AudioUnitGetProperty(editau, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, editors, &psize);
714		if (err == noErr)
715			desc = editors[0];
716
717		delete [] editors;
718	}
719
720	HIViewFindByID(HIViewGetRoot(effectWRef), kHIViewWindowContentID, &contentview);
721
722	cmp = FindNextComponent(NULL, &desc);
723	if (cmp)
724	{
725		err = OpenAComponent(cmp, &carbonView);
726		if (err == noErr)
727		{
728			EventTypeSpec	event[] = { { kEventClassControl, kEventControlBoundsChanged } };
729
730			GetWindowBounds(effectWRef, kWindowContentRgn, &rct);
731			location.x = 20;
732			location.y = 96;
733			size.x     = rct.right  - rct.left;
734			size.y     = rct.bottom - rct.top;
735
736			err = AudioUnitCarbonViewCreate(carbonView, editau, effectWRef, contentview, &location, &size, &pane);
737
738			carbonViewEventUPP = NewEventHandlerUPP(SoundEffectsCarbonViewEventHandler);
739			err = InstallControlEventHandler(pane, carbonViewEventUPP, GetEventTypeCount(event), event, (void *) effectWRef, &carbonViewEventRef);
740
741			ResizeSoundEffectsDialog(pane);
742		}
743		else
744			carbonView = NULL;
745	}
746	else
747		carbonView = NULL;
748
749	cid.id = 0;
750	cid.signature = 'Enab';
751	HIViewFindByID(contentview, cid, &ctl);
752	SetControl32BitValue(ctl, (aueffect & cureffect) ? 1 : 0);
753}
754
755static void ResizeSoundEffectsDialog (HIViewRef view)
756{
757	OSStatus	err;
758	HIViewRef	ctl, root;
759	HIViewID	cid;
760	HIRect		bounds;
761	Rect		rv;
762	int			w, h;
763
764	root = HIViewGetRoot(effectWRef);
765
766	cid.id = 0;
767	cid.signature = 'Enab';
768	HIViewFindByID(root, cid, &ctl);
769
770	err = HIViewSetVisible(ctl,  false);
771	err = HIViewSetVisible(view, false);
772
773	HIViewGetBounds(view, &bounds);
774	w = ((int) bounds.size.width + 30 > (int) effectWSize.width) ? ((int) bounds.size.width + 30) : (int) effectWSize.width;
775	h = (int) bounds.size.height + 122;
776#ifdef MAC_PANTHER_SUPPORT
777	if (systemVersion < 0x1040)
778		h += 16;
779#endif
780	GetWindowBounds(effectWRef, kWindowStructureRgn, &rv);
781	rv.right  = rv.left + w;
782	rv.bottom = rv.top  + h;
783	err = TransitionWindow(effectWRef, kWindowSlideTransitionEffect, kWindowResizeTransitionAction, &rv);
784
785	err = HIViewSetVisible(ctl,  true);
786	err = HIViewSetVisible(view, true);
787
788#ifdef MAC_PANTHER_SUPPORT
789	if (systemVersion < 0x1040)
790	{
791		HIRect	frame;
792		Rect	rct;
793
794		GetWindowBounds(effectWRef, kWindowContentRgn, &rv);
795
796		cid.signature = 'SfUI';
797		HIViewFindByID(root, cid, &ctl);
798		HIViewGetFrame(ctl, &frame);
799		frame.size.width = (float) (rv.right - rv.left);
800		HIViewSetFrame(ctl, &frame);
801
802		cid.signature = 'LINE';
803		HIViewFindByID(root, cid, &ctl);
804		HIViewGetFrame(ctl, &frame);
805		frame.size.width = (float) (rv.right - rv.left - 24);
806		HIViewSetFrame(ctl, &frame);
807
808		rct.top    = 0;
809		rct.left   = 0;
810		rct.bottom = rv.bottom - rv.top;
811		rct.right  = rv.right  - rv.left;
812		err = InvalWindowRect(effectWRef, &rct);
813	}
814#endif
815}
816
817void ConfigureSoundEffects (void)
818{
819	OSStatus	err;
820	IBNibRef	nibRef;
821
822	err = CreateNibReference(kMacS9XCFString, &nibRef);
823	if (err == noErr)
824	{
825		WindowRef	uiparts;
826
827		err = CreateWindowFromNib(nibRef, CFSTR("SoundEffect"), &uiparts);
828		if (err == noErr)
829		{
830			EventHandlerUPP		eventUPP;
831			EventHandlerRef		eventHandler;
832			EventTypeSpec		event[] = { { kEventClassWindow,  kEventWindowClose         },
833											{ kEventClassCommand, kEventCommandProcess      },
834											{ kEventClassCommand, kEventCommandUpdateStatus } };
835			HIViewRef			ctl, userpane, contentview;
836			HIViewID			cid;
837			CFStringRef			str;
838			Rect				rct;
839			WindowAttributes	metal = 0;
840
841			cid.id = 0;
842			cid.signature = 'SfUI';
843			HIViewFindByID(HIViewGetRoot(uiparts), cid, &userpane);
844			GetWindowBounds(uiparts, kWindowContentRgn, &rct);
845
846			if (systemVersion >= 0x1040)	// AUs support compositing
847			{
848				HIRect	frame;
849
850				str = CFCopyLocalizedString(CFSTR("CreateMetalDlg"), "NO");
851				if (str)
852				{
853					if (CFStringCompare(str, CFSTR("YES"), 0) == kCFCompareEqualTo)
854						metal = kWindowMetalAttribute;
855
856					CFRelease(str);
857				}
858
859				frame = CGRectMake(0.0f, 0.0f, (float) (rct.right - rct.left), (float) (rct.bottom - rct.top));
860				err = CreateNewWindow(kDocumentWindowClass, kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute | kWindowCompositingAttribute | metal, &rct, &effectWRef);
861				err = HIViewFindByID(HIViewGetRoot(effectWRef), kHIViewWindowContentID, &contentview);
862				err = HIViewAddSubview(contentview, userpane);
863				err = HIViewSetFrame(userpane, &frame);
864			}
865		#ifdef MAC_PANTHER_SUPPORT
866			else
867			{
868				err = CreateNewWindow(kDocumentWindowClass, kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute, &rct, &effectWRef);
869				err = CreateRootControl(effectWRef, &contentview);
870				err = EmbedControl(userpane, contentview);
871				MoveControl(userpane, 0, 0);
872			}
873		#endif
874
875			CFRelease(uiparts);
876
877			if (!metal)
878				err = SetThemeWindowBackground(effectWRef, kThemeBrushDialogBackgroundActive, false);
879
880			str = CFCopyLocalizedString(CFSTR("SoundEffectDlg"), "SoundEffect");
881			if (str)
882			{
883				err = SetWindowTitleWithCFString(effectWRef, str);
884				CFRelease(str);
885			}
886
887			if (systemVersion >= 0x1040)	// AUs support compositing
888			{
889				HILayoutInfo	layoutinfo;
890				HIViewRef		separator;
891
892				cid.signature = 'LINE';
893				err = HIViewFindByID(userpane, cid, &separator);
894
895				layoutinfo.version = kHILayoutInfoVersionZero;
896				err = HIViewGetLayoutInfo(userpane, &layoutinfo);
897
898				layoutinfo.binding.top.toView    = contentview;
899				layoutinfo.binding.top.kind      = kHILayoutBindNone;
900				layoutinfo.binding.bottom.toView = contentview;
901				layoutinfo.binding.bottom.kind   = kHILayoutBindNone;
902				layoutinfo.binding.left.toView   = contentview;
903				layoutinfo.binding.left.kind     = kHILayoutBindLeft;
904				layoutinfo.binding.right.toView  = contentview;
905				layoutinfo.binding.right.kind    = kHILayoutBindRight;
906				err = HIViewSetLayoutInfo(userpane, &layoutinfo);
907
908				layoutinfo.version = kHILayoutInfoVersionZero;
909				err = HIViewGetLayoutInfo(separator, &layoutinfo);
910
911				layoutinfo.binding.top.toView    = userpane;
912				layoutinfo.binding.top.kind      = kHILayoutBindNone;
913				layoutinfo.binding.bottom.toView = userpane;
914				layoutinfo.binding.bottom.kind   = kHILayoutBindNone;
915				layoutinfo.binding.left.toView   = userpane;
916				layoutinfo.binding.left.kind     = kHILayoutBindLeft;
917				layoutinfo.binding.right.toView  = userpane;
918				layoutinfo.binding.right.kind    = kHILayoutBindRight;
919				err = HIViewSetLayoutInfo(separator, &layoutinfo);
920			}
921
922			eventUPP = NewEventHandlerUPP(SoundEffectsEventHandler);
923			err = InstallWindowEventHandler(effectWRef, eventUPP, GetEventTypeCount(event), event, (void *) effectWRef, &eventHandler);
924
925			GetWindowBounds(effectWRef, kWindowContentRgn, &rct);
926			effectWSize.width  = (float) (rct.right  - rct.left);
927			effectWSize.height = (float) (rct.bottom - rct.top );
928
929			carbonView = NULL;
930			ReplaceAudioUnitCarbonView();
931
932			cid.signature = 'Epop';
933			HIViewFindByID(userpane, cid, &ctl);
934			switch (cureffect)
935			{
936				case kAUReverb:
937					SetControl32BitValue(ctl, 1);
938					break;
939
940				case kAUGraphEQ:
941					SetControl32BitValue(ctl, 2);
942					break;
943			}
944
945			MoveWindowPosition(effectWRef, kWindowSoundEffect, false);
946			ShowWindow(effectWRef);
947			err = RunAppModalLoopForWindow(effectWRef);
948			HideWindow(effectWRef);
949			SaveWindowPosition(effectWRef, kWindowSoundEffect);
950
951			if (carbonView)
952			{
953				err = RemoveEventHandler(carbonViewEventRef);
954				DisposeEventHandlerUPP(carbonViewEventUPP);
955				carbonViewEventRef = NULL;
956				carbonViewEventUPP = NULL;
957
958				CloseComponent(carbonView);
959				carbonView = NULL;
960			}
961
962			err = RemoveEventHandler(eventHandler);
963			DisposeEventHandlerUPP(eventUPP);
964
965			CFRelease(effectWRef);
966		}
967
968		DisposeNibReference(nibRef);
969	}
970}
971
972static pascal OSStatus SoundEffectsCarbonViewEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
973{
974	OSStatus	err, result = eventNotHandledErr;
975	HIViewRef	ctl;
976	HIRect		bounds;
977
978	switch (GetEventClass(inEvent))
979	{
980		case kEventClassControl:
981			switch (GetEventKind(inEvent))
982			{
983				case kEventControlBoundsChanged:
984					err = GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &ctl);
985					if (err == noErr)
986					{
987						err = GetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &bounds);
988						if (err == noErr)
989						{
990							if ((bounds.size.width > 0) && (bounds.size.height > 0))
991								ResizeSoundEffectsDialog(ctl);
992						}
993					}
994
995					result = noErr;
996					break;
997			}
998
999			break;
1000	}
1001
1002	return (result);
1003}
1004
1005static pascal OSStatus SoundEffectsEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
1006{
1007	OSStatus	err, result = eventNotHandledErr;
1008	WindowRef	tWindowRef = (WindowRef) inUserData;
1009
1010	switch (GetEventClass(inEvent))
1011	{
1012		case kEventClassWindow:
1013			switch (GetEventKind(inEvent))
1014			{
1015				case kEventWindowClose:
1016					QuitAppModalLoopForWindow(tWindowRef);
1017					result = noErr;
1018					break;
1019			}
1020
1021			break;
1022
1023		case kEventClassCommand:
1024			switch (GetEventKind(inEvent))
1025			{
1026				HICommand	tHICommand;
1027
1028				case kEventCommandUpdateStatus:
1029					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
1030					if (err == noErr && tHICommand.commandID == 'clos')
1031					{
1032						UpdateMenuCommandStatus(true);
1033						result = noErr;
1034					}
1035
1036					break;
1037
1038				case kEventCommandProcess:
1039					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
1040					if (err == noErr)
1041					{
1042						switch (tHICommand.commandID)
1043						{
1044							case 'Enab':
1045							{
1046								Boolean	r = false;
1047
1048								mboxPause = true;
1049
1050								err = AUGraphIsRunning(agraph, &r);
1051								if (err == noErr && r)
1052									err = AUGraphStop(agraph);
1053
1054								DisconnectAudioUnits();
1055								err = AUGraphUninitialize(agraph);
1056
1057								aueffect ^= cureffect;
1058
1059								SetAudioUnitSoundFormat();
1060								ChangeBufferFrameSize();
1061
1062								err = AUGraphInitialize(agraph);
1063								ConnectAudioUnits();
1064
1065								if (r)
1066									err = AUGraphStart(agraph);
1067
1068								mboxPause = false;
1069
1070								result = noErr;
1071								break;
1072							}
1073
1074							case 'Revb':
1075								cureffect = kAUReverb;
1076								ReplaceAudioUnitCarbonView();
1077								break;
1078
1079							case 'GrEQ':
1080								cureffect = kAUGraphEQ;
1081								ReplaceAudioUnitCarbonView();
1082								break;
1083						}
1084					}
1085
1086					break;
1087			}
1088
1089			break;
1090	}
1091
1092	return (result);
1093}
1094