2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "SDL_config.h"
24 #if defined(__APPLE__) && defined(__MACH__)
25 # include <Carbon/Carbon.h>
26 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
29 # include <Sound.h> /* SoundManager interface */
31 # include <DriverServices.h>
34 #if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
35 #if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
36 #define NewSndCallBackUPP NewSndCallBackProc
38 #if !defined(NewSndCallBackUPP)
39 #define NewSndCallBackUPP NewSndCallBackProc
43 #include "SDL_audio.h"
44 #include "../SDL_audio_c.h"
45 #include "../SDL_sysaudio.h"
46 #include "SDL_romaudio.h"
48 /* Audio driver functions */
50 static void Mac_CloseAudio(_THIS);
51 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec);
52 static void Mac_LockAudio(_THIS);
53 static void Mac_UnlockAudio(_THIS);
55 /* Audio driver bootstrap functions */
58 static int Audio_Available(void)
63 static void Audio_DeleteDevice(SDL_AudioDevice *device)
65 SDL_free(device->hidden);
69 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
71 SDL_AudioDevice *this;
73 /* Initialize all variables that we clean on shutdown */
74 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
76 SDL_memset(this, 0, (sizeof *this));
77 this->hidden = (struct SDL_PrivateAudioData *)
78 SDL_malloc((sizeof *this->hidden));
80 if ( (this == NULL) || (this->hidden == NULL) ) {
87 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
89 /* Set the function pointers */
90 this->OpenAudio = Mac_OpenAudio;
91 this->CloseAudio = Mac_CloseAudio;
92 this->LockAudio = Mac_LockAudio;
93 this->UnlockAudio = Mac_UnlockAudio;
94 this->free = Audio_DeleteDevice;
96 #ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
97 this->LockAudio = NULL;
98 this->UnlockAudio = NULL;
103 AudioBootStrap SNDMGR_bootstrap = {
104 "sndmgr", "MacOS SoundManager 3.0",
105 Audio_Available, Audio_CreateDevice
108 #if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
109 /* This works correctly on Mac OS X */
111 #pragma options align=power
113 static volatile SInt32 audio_is_locked = 0;
114 static volatile SInt32 need_to_mix = 0;
116 static UInt8 *buffer[2];
117 static volatile UInt32 running = 0;
118 static CmpSoundHeader header;
119 static volatile Uint32 fill_me = 0;
121 static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer)
123 if ( ! audio->paused ) {
125 SDL_mutexP(audio->mixer_lock);
127 if ( audio->convert.needed ) {
128 audio->spec.callback(audio->spec.userdata,
129 (Uint8 *)audio->convert.buf,audio->convert.len);
130 SDL_ConvertAudio(&audio->convert);
131 if ( audio->convert.len_cvt != audio->spec.size ) {
132 /* Uh oh... probably crashes here */;
134 SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
136 audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
139 SDL_mutexV(audio->mixer_lock);
143 DecrementAtomic((SInt32 *) &need_to_mix);
146 static void Mac_LockAudio(_THIS)
148 IncrementAtomic((SInt32 *) &audio_is_locked);
151 static void Mac_UnlockAudio(_THIS)
155 oldval = DecrementAtomic((SInt32 *) &audio_is_locked);
156 if ( oldval != 1 ) /* != 1 means audio is still locked. */
159 /* Did we miss the chance to mix in an interrupt? Do it now. */
160 if ( BitAndAtomic (0xFFFFFFFF, (UInt32 *) &need_to_mix) ) {
162 * Note that this could be a problem if you missed an interrupt
163 * while the audio was locked, and get preempted by a second
164 * interrupt here, but that means you locked for way too long anyhow.
166 mix_buffer (this, buffer[fill_me]);
170 static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
173 SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
175 IncrementAtomic((SInt32 *) &need_to_mix);
177 fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */
178 play_me = ! fill_me; /* filled buffer to play _now_ */
180 if ( ! audio->enabled ) {
184 /* queue previously mixed buffer for playback. */
185 header.samplePtr = (Ptr)buffer[play_me];
188 cmd.param2 = (long)&header;
189 SndDoCommand (chan, &cmd, 0);
191 memset (buffer[fill_me], 0, audio->spec.size);
194 * if audio device isn't locked, mix the next buffer to be queued in
195 * the memory block that just finished playing.
197 if ( ! BitAndAtomic(0xFFFFFFFF, (UInt32 *) &audio_is_locked) ) {
198 mix_buffer (audio, buffer[fill_me]);
201 /* set this callback to run again when current buffer drains. */
203 cmd.cmd = callBackCmd;
205 cmd.param2 = play_me;
207 SndDoCommand (chan, &cmd, 0);
211 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
213 SndCallBackUPP callback;
218 /* Very few conversions are required, but... */
219 switch (spec->format) {
221 spec->format = AUDIO_U8;
224 spec->format = AUDIO_S16LSB;
227 spec->format = AUDIO_S16MSB;
230 SDL_CalculateAudioSpec(spec);
232 /* initialize bufferCmd header */
233 memset (&header, 0, sizeof(header));
234 callback = (SndCallBackUPP) NewSndCallBackUPP (callBackProc);
235 sample_bits = spec->size / spec->samples / spec->channels * 8;
239 "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
240 spec->format, spec->channels, sample_bits, spec->freq);
241 #endif /* DEBUG_AUDIO */
243 header.numChannels = spec->channels;
244 header.sampleSize = sample_bits;
245 header.sampleRate = spec->freq << 16;
246 header.numFrames = spec->samples;
247 header.encode = cmpSH;
249 /* Note that we install the 16bitLittleEndian Converter if needed. */
250 if ( spec->format == 0x8010 ) {
251 header.compressionID = fixedCompression;
252 header.format = k16BitLittleEndianFormat;
255 /* allocate 2 buffers */
256 for (i=0; i<2; i++) {
257 buffer[i] = (UInt8*)malloc (sizeof(UInt8) * spec->size);
258 if (buffer[i] == NULL) {
262 memset (buffer[i], 0, spec->size);
265 /* Create the sound manager channel */
266 channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
267 if ( channel == NULL ) {
271 if ( spec->channels >= 2 ) {
272 initOptions = initStereo;
274 initOptions = initMono;
276 channel->userInfo = (long)this;
277 channel->qLength = 128;
278 if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr ) {
279 SDL_SetError("Unable to create audio channel");
288 cmd.cmd = callBackCmd;
291 SndDoCommand (channel, &cmd, 0);
297 static void Mac_CloseAudio(_THIS) {
304 SndDisposeChannel (channel, true);
308 for ( i=0; i<2; ++i ) {
316 #else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */
318 static void Mac_LockAudio(_THIS)
323 static void Mac_UnlockAudio(_THIS)
329 /* This function is called by Sound Manager when it has exhausted one of
330 the buffers, so we'll zero it to silence and fill it with audio if
334 void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf)
336 SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0];
338 /* If audio is quitting, don't do anything */
339 if ( ! audio->enabled ) {
342 memset (newbuf->dbSoundData, 0, audio->spec.size);
343 newbuf->dbNumFrames = audio->spec.samples;
344 if ( ! audio->paused ) {
345 if ( audio->convert.needed ) {
346 audio->spec.callback(audio->spec.userdata,
347 (Uint8 *)audio->convert.buf,audio->convert.len);
348 SDL_ConvertAudio(&audio->convert);
350 if ( audio->convert.len_cvt != audio->spec.size ) {
351 /* Uh oh... probably crashes here */;
354 SDL_memcpy(newbuf->dbSoundData, audio->convert.buf,
355 audio->convert.len_cvt);
357 audio->spec.callback(audio->spec.userdata,
358 (Uint8 *)newbuf->dbSoundData, audio->spec.size);
361 newbuf->dbFlags |= dbBufferReady;
364 static int DoubleBufferAudio_Available(void)
367 NumVersion sndversion;
371 sndversion = SndSoundManagerVersion();
372 if ( sndversion.majorRev >= 3 ) {
373 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
374 if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) {
379 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
380 if ( (response & (1 << gestaltHasASC)) ) {
388 static void Mac_CloseAudio(_THIS)
392 if ( channel != NULL ) {
393 /* Clean up the audio channel */
394 SndDisposeChannel(channel, true);
397 for ( i=0; i<2; ++i ) {
398 if ( audio_buf[i] ) {
399 SDL_free(audio_buf[i]);
405 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec)
407 SndDoubleBufferHeader2 audio_dbh;
411 SndDoubleBackUPP doubleBackProc;
413 /* Check to make sure double-buffered audio is available */
414 if ( ! DoubleBufferAudio_Available() ) {
415 SDL_SetError("Sound manager doesn't support double-buffering");
419 /* Very few conversions are required, but... */
420 switch (spec->format) {
422 spec->format = AUDIO_U8;
425 spec->format = AUDIO_S16LSB;
428 spec->format = AUDIO_S16MSB;
431 SDL_CalculateAudioSpec(spec);
433 /* initialize the double-back header */
434 SDL_memset(&audio_dbh, 0, sizeof(audio_dbh));
435 doubleBackProc = NewSndDoubleBackProc (sndDoubleBackProc);
436 sample_bits = spec->size / spec->samples / spec->channels * 8;
438 audio_dbh.dbhNumChannels = spec->channels;
439 audio_dbh.dbhSampleSize = sample_bits;
440 audio_dbh.dbhCompressionID = 0;
441 audio_dbh.dbhPacketSize = 0;
442 audio_dbh.dbhSampleRate = spec->freq << 16;
443 audio_dbh.dbhDoubleBack = doubleBackProc;
444 audio_dbh.dbhFormat = 0;
446 /* Note that we install the 16bitLittleEndian Converter if needed. */
447 if ( spec->format == 0x8010 ) {
448 audio_dbh.dbhCompressionID = fixedCompression;
449 audio_dbh.dbhFormat = k16BitLittleEndianFormat;
452 /* allocate the 2 double-back buffers */
453 for ( i=0; i<2; ++i ) {
454 audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer)+spec->size);
455 if ( audio_buf[i] == NULL ) {
459 audio_buf[i]->dbNumFrames = spec->samples;
460 audio_buf[i]->dbFlags = dbBufferReady;
461 audio_buf[i]->dbUserInfo[0] = (long)this;
462 audio_dbh.dbhBufferPtr[i] = audio_buf[i];
465 /* Create the sound manager channel */
466 channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
467 if ( channel == NULL ) {
471 if ( spec->channels >= 2 ) {
472 initOptions = initStereo;
474 initOptions = initMono;
476 channel->userInfo = 0;
477 channel->qLength = 128;
478 if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) {
479 SDL_SetError("Unable to create audio channel");
486 if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh)
488 SDL_SetError("Unable to play double buffered audio");
495 #endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */