SDL-1.2.14
[sdl_omap.git] / src / audio / macrom / SDL_romaudio.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
4
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.
9
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.
14
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
18
19 Sam Lantinga
20 slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#if defined(__APPLE__) && defined(__MACH__)
25# include <Carbon/Carbon.h>
26#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
27# include <Carbon.h>
28#else
29# include <Sound.h> /* SoundManager interface */
30# include <Gestalt.h>
31# include <DriverServices.h>
32#endif
33
34#if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
35#if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
36#define NewSndCallBackUPP NewSndCallBackProc
37#endif
38#if !defined(NewSndCallBackUPP)
39#define NewSndCallBackUPP NewSndCallBackProc
40#endif
41#endif
42
43#include "SDL_audio.h"
44#include "../SDL_audio_c.h"
45#include "../SDL_sysaudio.h"
46#include "SDL_romaudio.h"
47
48/* Audio driver functions */
49
50static void Mac_CloseAudio(_THIS);
51static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec);
52static void Mac_LockAudio(_THIS);
53static void Mac_UnlockAudio(_THIS);
54
55/* Audio driver bootstrap functions */
56
57
58static int Audio_Available(void)
59{
60 return(1);
61}
62
63static void Audio_DeleteDevice(SDL_AudioDevice *device)
64{
65 SDL_free(device->hidden);
66 SDL_free(device);
67}
68
69static SDL_AudioDevice *Audio_CreateDevice(int devindex)
70{
71 SDL_AudioDevice *this;
72
73 /* Initialize all variables that we clean on shutdown */
74 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
75 if ( this ) {
76 SDL_memset(this, 0, (sizeof *this));
77 this->hidden = (struct SDL_PrivateAudioData *)
78 SDL_malloc((sizeof *this->hidden));
79 }
80 if ( (this == NULL) || (this->hidden == NULL) ) {
81 SDL_OutOfMemory();
82 if ( this ) {
83 SDL_free(this);
84 }
85 return(0);
86 }
87 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
88
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;
95
96#ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
97 this->LockAudio = NULL;
98 this->UnlockAudio = NULL;
99#endif
100 return this;
101}
102
103AudioBootStrap SNDMGR_bootstrap = {
104 "sndmgr", "MacOS SoundManager 3.0",
105 Audio_Available, Audio_CreateDevice
106};
107
108#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
109/* This works correctly on Mac OS X */
110
111#pragma options align=power
112
113static volatile SInt32 audio_is_locked = 0;
114static volatile SInt32 need_to_mix = 0;
115
116static UInt8 *buffer[2];
117static volatile UInt32 running = 0;
118static CmpSoundHeader header;
119static volatile Uint32 fill_me = 0;
120
121static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer)
122{
123 if ( ! audio->paused ) {
124#ifdef __MACOSX__
125 SDL_mutexP(audio->mixer_lock);
126#endif
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 */;
133 }
134 SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
135 } else {
136 audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
137 }
138#ifdef __MACOSX__
139 SDL_mutexV(audio->mixer_lock);
140#endif
141 }
142
143 DecrementAtomic((SInt32 *) &need_to_mix);
144}
145
146static void Mac_LockAudio(_THIS)
147{
148 IncrementAtomic((SInt32 *) &audio_is_locked);
149}
150
151static void Mac_UnlockAudio(_THIS)
152{
153 SInt32 oldval;
154
155 oldval = DecrementAtomic((SInt32 *) &audio_is_locked);
156 if ( oldval != 1 ) /* != 1 means audio is still locked. */
157 return;
158
159 /* Did we miss the chance to mix in an interrupt? Do it now. */
160 if ( BitAndAtomic (0xFFFFFFFF, (UInt32 *) &need_to_mix) ) {
161 /*
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.
165 */
166 mix_buffer (this, buffer[fill_me]);
167 }
168}
169
170static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
171 UInt32 play_me;
172 SndCommand cmd;
173 SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
174
175 IncrementAtomic((SInt32 *) &need_to_mix);
176
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_ */
179
180 if ( ! audio->enabled ) {
181 return;
182 }
183
184 /* queue previously mixed buffer for playback. */
185 header.samplePtr = (Ptr)buffer[play_me];
186 cmd.cmd = bufferCmd;
187 cmd.param1 = 0;
188 cmd.param2 = (long)&header;
189 SndDoCommand (chan, &cmd, 0);
190
191 memset (buffer[fill_me], 0, audio->spec.size);
192
193 /*
194 * if audio device isn't locked, mix the next buffer to be queued in
195 * the memory block that just finished playing.
196 */
197 if ( ! BitAndAtomic(0xFFFFFFFF, (UInt32 *) &audio_is_locked) ) {
198 mix_buffer (audio, buffer[fill_me]);
199 }
200
201 /* set this callback to run again when current buffer drains. */
202 if ( running ) {
203 cmd.cmd = callBackCmd;
204 cmd.param1 = 0;
205 cmd.param2 = play_me;
206
207 SndDoCommand (chan, &cmd, 0);
208 }
209}
210
211static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
212
213 SndCallBackUPP callback;
214 int sample_bits;
215 int i;
216 long initOptions;
217
218 /* Very few conversions are required, but... */
219 switch (spec->format) {
220 case AUDIO_S8:
221 spec->format = AUDIO_U8;
222 break;
223 case AUDIO_U16LSB:
224 spec->format = AUDIO_S16LSB;
225 break;
226 case AUDIO_U16MSB:
227 spec->format = AUDIO_S16MSB;
228 break;
229 }
230 SDL_CalculateAudioSpec(spec);
231
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;
236
237#ifdef DEBUG_AUDIO
238 fprintf(stderr,
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 */
242
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;
248
249 /* Note that we install the 16bitLittleEndian Converter if needed. */
250 if ( spec->format == 0x8010 ) {
251 header.compressionID = fixedCompression;
252 header.format = k16BitLittleEndianFormat;
253 }
254
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) {
259 SDL_OutOfMemory();
260 return (-1);
261 }
262 memset (buffer[i], 0, spec->size);
263 }
264
265 /* Create the sound manager channel */
266 channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
267 if ( channel == NULL ) {
268 SDL_OutOfMemory();
269 return(-1);
270 }
271 if ( spec->channels >= 2 ) {
272 initOptions = initStereo;
273 } else {
274 initOptions = initMono;
275 }
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");
280 SDL_free(channel);
281 channel = NULL;
282 return(-1);
283 }
284
285 /* start playback */
286 {
287 SndCommand cmd;
288 cmd.cmd = callBackCmd;
289 cmd.param2 = 0;
290 running = 1;
291 SndDoCommand (channel, &cmd, 0);
292 }
293
294 return 1;
295}
296
297static void Mac_CloseAudio(_THIS) {
298
299 int i;
300
301 running = 0;
302
303 if (channel) {
304 SndDisposeChannel (channel, true);
305 channel = NULL;
306 }
307
308 for ( i=0; i<2; ++i ) {
309 if ( buffer[i] ) {
310 SDL_free(buffer[i]);
311 buffer[i] = NULL;
312 }
313 }
314}
315
316#else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */
317
318static void Mac_LockAudio(_THIS)
319{
320 /* no-op. */
321}
322
323static void Mac_UnlockAudio(_THIS)
324{
325 /* no-op. */
326}
327
328
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
331 we're not paused.
332*/
333static pascal
334void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf)
335{
336 SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0];
337
338 /* If audio is quitting, don't do anything */
339 if ( ! audio->enabled ) {
340 return;
341 }
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);
349#if 0
350 if ( audio->convert.len_cvt != audio->spec.size ) {
351 /* Uh oh... probably crashes here */;
352 }
353#endif
354 SDL_memcpy(newbuf->dbSoundData, audio->convert.buf,
355 audio->convert.len_cvt);
356 } else {
357 audio->spec.callback(audio->spec.userdata,
358 (Uint8 *)newbuf->dbSoundData, audio->spec.size);
359 }
360 }
361 newbuf->dbFlags |= dbBufferReady;
362}
363
364static int DoubleBufferAudio_Available(void)
365{
366 int available;
367 NumVersion sndversion;
368 long response;
369
370 available = 0;
371 sndversion = SndSoundManagerVersion();
372 if ( sndversion.majorRev >= 3 ) {
373 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
374 if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) {
375 available = 1;
376 }
377 }
378 } else {
379 if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
380 if ( (response & (1 << gestaltHasASC)) ) {
381 available = 1;
382 }
383 }
384 }
385 return(available);
386}
387
388static void Mac_CloseAudio(_THIS)
389{
390 int i;
391
392 if ( channel != NULL ) {
393 /* Clean up the audio channel */
394 SndDisposeChannel(channel, true);
395 channel = NULL;
396 }
397 for ( i=0; i<2; ++i ) {
398 if ( audio_buf[i] ) {
399 SDL_free(audio_buf[i]);
400 audio_buf[i] = NULL;
401 }
402 }
403}
404
405static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec)
406{
407 SndDoubleBufferHeader2 audio_dbh;
408 int i;
409 long initOptions;
410 int sample_bits;
411 SndDoubleBackUPP doubleBackProc;
412
413 /* Check to make sure double-buffered audio is available */
414 if ( ! DoubleBufferAudio_Available() ) {
415 SDL_SetError("Sound manager doesn't support double-buffering");
416 return(-1);
417 }
418
419 /* Very few conversions are required, but... */
420 switch (spec->format) {
421 case AUDIO_S8:
422 spec->format = AUDIO_U8;
423 break;
424 case AUDIO_U16LSB:
425 spec->format = AUDIO_S16LSB;
426 break;
427 case AUDIO_U16MSB:
428 spec->format = AUDIO_S16MSB;
429 break;
430 }
431 SDL_CalculateAudioSpec(spec);
432
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;
437
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;
445
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;
450 }
451
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 ) {
456 SDL_OutOfMemory();
457 return(-1);
458 }
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];
463 }
464
465 /* Create the sound manager channel */
466 channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
467 if ( channel == NULL ) {
468 SDL_OutOfMemory();
469 return(-1);
470 }
471 if ( spec->channels >= 2 ) {
472 initOptions = initStereo;
473 } else {
474 initOptions = initMono;
475 }
476 channel->userInfo = 0;
477 channel->qLength = 128;
478 if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) {
479 SDL_SetError("Unable to create audio channel");
480 SDL_free(channel);
481 channel = NULL;
482 return(-1);
483 }
484
485 /* Start playback */
486 if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh)
487 != noErr ) {
488 SDL_SetError("Unable to play double buffered audio");
489 return(-1);
490 }
491
492 return 1;
493}
494
495#endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */
496