1 /////////////////////////////////////////////////////////////////////////
2 // $Id: soundosx.cc,v 1.10 2008/07/26 08:02:27 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // This file (SOUNDOSX.CC) written and donated by Brian Huffman
10 #define Float32 KLUDGE_Float32
11 #define Float64 KLUDGE_Float64
14 #define NO_DEVICE_INCLUDES
21 #if defined(macintosh) && BX_SUPPORT_SB16
23 #define LOG_THIS sb16->
28 #include <QuickTimeMusic.h>
30 #include <CoreAudio/CoreAudio.h>
31 #include <AudioUnit/AudioUnit.h>
32 #include <AudioToolbox/DefaultAudioOutput.h>
33 #include <AudioToolbox/AudioConverter.h>
34 #include <AudioToolbox/AUGraph.h>
35 #include <QuickTime/QuickTimeMusic.h>
39 #ifdef BX_SOUND_OSX_use_converter
40 OSStatus
MyRenderer (void *inRefCon
, AudioUnitRenderActionFlags inActionFlags
,
41 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
, AudioBuffer
*ioData
);
42 OSStatus
MyACInputProc (AudioConverterRef inAudioConverter
, UInt32
* outDataSize
,
43 void** outData
, void* inUserData
);
47 #ifdef BX_SOUND_OSX_use_converter
52 #ifdef BX_SOUND_OSX_use_quicktime
53 SndChannelPtr WaveChannel
;
54 ExtSoundHeader WaveInfo
;
55 ExtSoundHeader WaveHeader
[BX_SOUND_OSX_NBUF
];
58 #ifdef BX_SOUND_OSX_use_converter
59 AudioUnit WaveOutputUnit
= NULL
;
60 AudioConverterRef WaveConverter
= NULL
;
63 bx_sound_osx_c::bx_sound_osx_c(bx_sb16_c
*sb16
)
64 :bx_sound_output_c(sb16
)
72 for (int i
=0; i
<BX_SOUND_OSX_NBUF
; i
++)
76 bx_sound_osx_c::~bx_sound_osx_c()
82 int bx_sound_osx_c::midiready()
84 return BX_SOUND_OUTPUT_OK
;
87 int bx_sound_osx_c::openmidioutput(char *device
)
89 #ifdef BX_SOUND_OSX_use_converter
90 ComponentDescription description
;
91 AUNode synthNode
, outputNode
;
94 NewAUGraph (&MidiGraph
);
97 description
.componentType
= kAudioUnitComponentType
;
98 description
.componentSubType
= kAudioUnitSubType_MusicDevice
;
99 description
.componentManufacturer
= kAudioUnitID_DLSSynth
;
100 description
.componentFlags
= 0;
101 description
.componentFlagsMask
= 0;
102 AUGraphNewNode (MidiGraph
, &description
, 0, NULL
, &synthNode
);
104 // Open the output device
105 description
.componentType
= kAudioUnitComponentType
;
106 description
.componentSubType
= kAudioUnitSubType_Output
;
107 description
.componentManufacturer
= kAudioUnitID_DefaultOutput
;
108 description
.componentFlags
= 0;
109 description
.componentFlagsMask
= 0;
110 AUGraphNewNode (MidiGraph
, &description
, 0, NULL
, &outputNode
);
112 // Connect the devices up
113 AUGraphConnectNodeInput (MidiGraph
, synthNode
, 1, outputNode
, 0);
114 AUGraphUpdate (MidiGraph
, NULL
);
116 // Open and initialize the audio units
117 AUGraphOpen (MidiGraph
);
118 AUGraphInitialize (MidiGraph
);
120 // Turn off the reverb on the synth
121 AUGraphGetNodeInfo (MidiGraph
, synthNode
, NULL
, NULL
, NULL
, &synthUnit
);
122 UInt32 usesReverb
= 0;
123 AudioUnitSetProperty (synthUnit
, kMusicDeviceProperty_UsesInternalReverb
,
124 kAudioUnitScope_Global
, 0, &usesReverb
, sizeof (usesReverb
));
127 AUGraphStart (MidiGraph
);
129 WRITELOG(WAVELOG(4), "openmidioutput(%s)", device
);
131 return BX_SOUND_OUTPUT_OK
;
134 int bx_sound_osx_c::sendmidicommand(int delta
, int command
, int length
, Bit8u data
[])
136 WRITELOG(WAVELOG(5), "sendmidicommand(%i,%02x,%i)", delta
, command
, length
);
137 if (!MidiOpen
) return BX_SOUND_OUTPUT_ERR
;
139 #ifdef BX_SOUND_OSX_use_converter
141 Bit8u arg1
= (length
>=1) ? data
[0] : 0;
142 Bit8u arg2
= (length
>=2) ? data
[1] : 0;
143 MusicDeviceMIDIEvent (synthUnit
, command
, arg1
, arg2
, delta
);
146 MusicDeviceSysEx (synthUnit
, data
, length
);
149 return BX_SOUND_OUTPUT_OK
;
152 int bx_sound_osx_c::closemidioutput()
154 WRITELOG(WAVELOG(4), "closemidioutput()");
156 #ifdef BX_SOUND_OSX_use_converter
157 AUGraphStop (MidiGraph
);
158 AUGraphClose (MidiGraph
);
160 return BX_SOUND_OUTPUT_OK
;
163 #ifdef BX_SOUND_OSX_use_quicktime
167 void WaveCallbackProc (SndChannelPtr chan
, SndCommand
*cmd
)
169 // a new buffer is available, so increment tail pointer
170 int *tail
= (int *) (cmd
->param2
);
175 int bx_sound_osx_c::openwaveoutput(char *device
)
179 WRITELOG(WAVELOG(4), "openwaveoutput(%s)", device
);
181 // open the default output unit
182 #ifdef BX_SOUND_OSX_use_quicktime
183 err
= SndNewChannel (&WaveChannel
, sampledSynth
, 0, NewSndCallBackUPP(WaveCallbackProc
));
184 if (err
!= noErr
) return BX_SOUND_OUTPUT_ERR
;
186 #ifdef BX_SOUND_OSX_use_converter
187 err
= OpenDefaultAudioOutput (&WaveOutputUnit
);
188 if (err
!= noErr
) return BX_SOUND_OUTPUT_ERR
;
189 AudioUnitInitialize (WaveOutputUnit
);
191 // Set up a callback function to generate output to the output unit
192 AudioUnitInputCallback input
;
193 input
.inputProc
= MyRenderer
;
194 input
.inputProcRefCon
= (void *) this;
195 AudioUnitSetProperty (WaveOutputUnit
, kAudioUnitProperty_SetInputCallback
,
196 kAudioUnitScope_Global
, 0, &input
, sizeof(input
));
200 return BX_SOUND_OUTPUT_OK
;
203 int bx_sound_osx_c::startwaveplayback(int frequency
, int bits
, int stereo
, int format
)
205 #ifdef BX_SOUND_OSX_use_converter
206 static int oldfreq
, oldbits
, oldstereo
, oldformat
;
207 AudioStreamBasicDescription srcFormat
, dstFormat
;
208 UInt32 formatSize
= sizeof(AudioStreamBasicDescription
);
211 WRITELOG(WAVELOG(4), "startwaveplayback(%d, %d, %d, %x)", frequency
, bits
, stereo
, format
);
213 #ifdef BX_SOUND_OSX_use_quicktime
214 WaveInfo
.samplePtr
= NULL
;
215 WaveInfo
.numChannels
= stereo
? 2 : 1;
216 WaveInfo
.sampleRate
= frequency
<< 16; // sampleRate is a 16.16 fixed-point value
217 WaveInfo
.loopStart
= 0;
218 WaveInfo
.loopEnd
= 0;
219 WaveInfo
.encode
= extSH
; // WaveInfo has type ExtSoundHeader
220 WaveInfo
.baseFrequency
= 1; // not sure what means. It's only a Uint8.
221 WaveInfo
.numFrames
= 0;
222 //WaveInfo.AIFFSampleRate = frequency; // frequency as float80
223 WaveInfo
.markerChunk
= NULL
;
225 WaveInfo
.instrumentChunks
= NULL
;
226 WaveInfo
.AESRecording
= NULL
;
227 WaveInfo
.sampleSize
= bits
* WaveInfo
.numChannels
;
230 #ifdef BX_SOUND_OSX_use_converter
231 if ((frequency
== oldfreq
) &&
233 (stereo
== oldstereo
) &&
234 (format
== oldformat
))
235 return BX_SOUND_OUTPUT_OK
; // nothing to do
242 // update the source audio format
243 UInt32 bytes
= bits
/ 8;
244 UInt32 channels
= stereo
? 2 : 1;
245 srcFormat
.mSampleRate
= (Float64
) frequency
;
246 srcFormat
.mFormatID
= kAudioFormatLinearPCM
;
247 srcFormat
.mFormatFlags
= kLinearPCMFormatFlagIsPacked
;
248 if (format
& 1) srcFormat
.mFormatFlags
|= kLinearPCMFormatFlagIsSignedInteger
;
249 srcFormat
.mBytesPerPacket
= channels
* bytes
;
250 srcFormat
.mFramesPerPacket
= 1;
251 srcFormat
.mBytesPerFrame
= channels
* bytes
;
252 srcFormat
.mChannelsPerFrame
= channels
;
253 srcFormat
.mBitsPerChannel
= bytes
* 8;
255 if (WavePlaying
) AudioOutputUnitStop (WaveOutputUnit
);
256 if (WaveConverter
) AudioConverterDispose (WaveConverter
);
258 AudioUnitGetProperty (WaveOutputUnit
, kAudioUnitProperty_StreamFormat
,
259 kAudioUnitScope_Output
, 0, &dstFormat
, &formatSize
);
261 AudioConverterNew (&srcFormat
, &dstFormat
, &WaveConverter
);
263 if (srcFormat
.mChannelsPerFrame
== 1 && dstFormat
.mChannelsPerFrame
== 2) {
264 // map single-channel input to both output channels
265 SInt32 map
[2] = {0,0};
266 AudioConverterSetProperty (WaveConverter
, kAudioConverterChannelMap
,
267 sizeof(map
), (void*) map
);
270 if (WavePlaying
) AudioOutputUnitStart (WaveOutputUnit
);
273 return BX_SOUND_OUTPUT_OK
;
276 int bx_sound_osx_c::waveready()
278 // HACK: the -4 is to keep from overwriting buffers that
279 // have been sent, but possibly not yet played. There
280 // should be a better way of doing this.
281 if (WaveOpen
&& (head
- tail
< BX_SOUND_OSX_NBUF
-4)) {
282 return BX_SOUND_OUTPUT_OK
;
285 #ifdef BX_SOUND_OSX_use_converter
286 // If buffer is full, make sure sound is playing
287 if (WaveOutputUnit
&& !WavePlaying
) {
288 AudioOutputUnitStart (WaveOutputUnit
);
292 return BX_SOUND_OUTPUT_ERR
;
296 int bx_sound_osx_c::sendwavepacket(int length
, Bit8u data
[])
298 #ifdef BX_SOUND_OSX_use_quicktime
299 SndCommand mySndCommand
;
302 WRITELOG(WAVELOG(4), "sendwavepacket(%d, %p), head=%u", length
, data
, head
);
305 if ((!WaveOpen
) || (head
- tail
>= BX_SOUND_OSX_NBUF
))
306 return BX_SOUND_OUTPUT_ERR
;
308 // find next available buffer
309 int n
= head
++ % BX_SOUND_OSX_NBUF
;
311 // put data in buffer
312 memcpy(WaveData
[n
], data
, length
);
313 WaveLength
[n
] = length
;
315 #ifdef BX_SOUND_OSX_use_quicktime
316 memcpy(&WaveHeader
[n
], &WaveInfo
, sizeof(WaveInfo
));
317 WaveHeader
[n
].samplePtr
= (char *) (WaveData
[n
]);
318 WaveHeader
[n
].numFrames
= length
* 8 / WaveInfo
.sampleSize
;
320 #ifdef BX_SOUND_OSX_use_converter
321 // make sure that the sound is playing
323 AudioOutputUnitStart (WaveOutputUnit
);
328 #ifdef BX_SOUND_OSX_use_quicktime
329 // queue buffer to play
330 mySndCommand
.cmd
= bufferCmd
;
331 mySndCommand
.param1
= 0;
332 mySndCommand
.param2
= (long)(&WaveHeader
[n
]);
333 SndDoCommand(WaveChannel
, &mySndCommand
, TRUE
);
335 // queue callback for when buffer finishes
336 mySndCommand
.cmd
= callBackCmd
;
337 mySndCommand
.param1
= 0;
338 mySndCommand
.param2
= (long)(&tail
);
339 SndDoCommand(WaveChannel
, &mySndCommand
, TRUE
);
342 return BX_SOUND_OUTPUT_OK
;
345 int bx_sound_osx_c::stopwaveplayback()
347 return BX_SOUND_OUTPUT_OK
;
350 int bx_sound_osx_c::closewaveoutput()
352 #ifdef BX_SOUND_OSX_use_converter
353 if (WavePlaying
) AudioOutputUnitStop (WaveOutputUnit
);
354 if (WaveConverter
) AudioConverterDispose (WaveConverter
);
355 if (WaveOutputUnit
) CloseComponent (WaveOutputUnit
);
358 WaveConverter
= NULL
;
359 WaveOutputUnit
= NULL
;
361 return BX_SOUND_OUTPUT_OK
;
364 #ifdef BX_SOUND_OSX_use_converter
365 OSStatus
MyRenderer (void *inRefCon
, AudioUnitRenderActionFlags inActionFlags
,
366 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
, AudioBuffer
*ioData
)
368 UInt32 size
= ioData
->mDataByteSize
;
369 AudioConverterFillBuffer (WaveConverter
, MyACInputProc
, inRefCon
, &size
, ioData
->mData
);
373 OSStatus
MyACInputProc (AudioConverterRef inAudioConverter
,
374 UInt32
* outDataSize
, void** outData
, void* inUserData
)
376 bx_sound_osx_c
*self
= (bx_sound_osx_c
*) inUserData
;
377 self
->nextbuffer ((int*) outDataSize
, outData
);
381 void bx_sound_osx_c::nextbuffer (int *outDataSize
, void **outData
)
383 WRITELOG(WAVELOG(4), "nextbuffer(), tail=%u", tail
);
384 if (head
- tail
<= 0) {
388 // We are getting behind, so stop the output for now
389 AudioOutputUnitStop (WaveOutputUnit
);
393 int n
= tail
% BX_SOUND_OSX_NBUF
;
394 *outData
= (void *) (WaveData
[n
]);
395 *outDataSize
= WaveLength
[n
];
401 #endif // defined(macintosh)