Mercurial > sdl-ios-xcode
comparison src/audio/macrom/SDL_romaudio.c @ 2049:5f6550e5184f
Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 09:15:21 +0000 |
parents | c27292a690b7 |
children | d22372343744 |
comparison
equal
deleted
inserted
replaced
2048:6067c7f9a672 | 2049:5f6550e5184f |
---|---|
19 Sam Lantinga | 19 Sam Lantinga |
20 slouken@libsdl.org | 20 slouken@libsdl.org |
21 */ | 21 */ |
22 #include "SDL_config.h" | 22 #include "SDL_config.h" |
23 | 23 |
24 /* This should work on PowerPC and Intel Mac OS X, and Carbonized Mac OS 9. */ | |
25 | |
24 #if defined(__APPLE__) && defined(__MACH__) | 26 #if defined(__APPLE__) && defined(__MACH__) |
27 # define SDL_MACOS_NAME "Mac OS X" | |
25 # include <Carbon/Carbon.h> | 28 # include <Carbon/Carbon.h> |
26 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) | 29 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) |
30 # define SDL_MACOS_NAME "Mac OS 9" | |
27 # include <Carbon.h> | 31 # include <Carbon.h> |
28 #else | 32 #else |
33 # define SDL_MACOS_NAME "Mac OS 9" | |
29 # include <Sound.h> /* SoundManager interface */ | 34 # include <Sound.h> /* SoundManager interface */ |
30 # include <Gestalt.h> | 35 # include <Gestalt.h> |
31 # include <DriverServices.h> | 36 # include <DriverServices.h> |
32 #endif | 37 #endif |
33 | 38 |
43 #include "SDL_audio.h" | 48 #include "SDL_audio.h" |
44 #include "../SDL_audio_c.h" | 49 #include "../SDL_audio_c.h" |
45 #include "../SDL_sysaudio.h" | 50 #include "../SDL_sysaudio.h" |
46 #include "SDL_romaudio.h" | 51 #include "SDL_romaudio.h" |
47 | 52 |
48 /* Audio driver functions */ | |
49 | |
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); | |
54 | |
55 /* Audio driver bootstrap functions */ | |
56 | |
57 | |
58 static int | |
59 Audio_Available(void) | |
60 { | |
61 return (1); | |
62 } | |
63 | |
64 static void | |
65 Audio_DeleteDevice(SDL_AudioDevice * device) | |
66 { | |
67 SDL_free(device->hidden); | |
68 SDL_free(device); | |
69 } | |
70 | |
71 static SDL_AudioDevice * | |
72 Audio_CreateDevice(int devindex) | |
73 { | |
74 SDL_AudioDevice *this; | |
75 | |
76 /* Initialize all variables that we clean on shutdown */ | |
77 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | |
78 if (this) { | |
79 SDL_memset(this, 0, (sizeof *this)); | |
80 this->hidden = (struct SDL_PrivateAudioData *) | |
81 SDL_malloc((sizeof *this->hidden)); | |
82 } | |
83 if ((this == NULL) || (this->hidden == NULL)) { | |
84 SDL_OutOfMemory(); | |
85 if (this) { | |
86 SDL_free(this); | |
87 } | |
88 return (0); | |
89 } | |
90 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
91 | |
92 /* Set the function pointers */ | |
93 this->OpenAudio = Mac_OpenAudio; | |
94 this->CloseAudio = Mac_CloseAudio; | |
95 this->LockAudio = Mac_LockAudio; | |
96 this->UnlockAudio = Mac_UnlockAudio; | |
97 this->free = Audio_DeleteDevice; | |
98 | |
99 #ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */ | |
100 this->LockAudio = NULL; | |
101 this->UnlockAudio = NULL; | |
102 #endif | |
103 return this; | |
104 } | |
105 | |
106 AudioBootStrap SNDMGR_bootstrap = { | |
107 "sndmgr", "MacOS SoundManager 3.0", | |
108 Audio_Available, Audio_CreateDevice | |
109 }; | |
110 | |
111 #if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE) | |
112 /* This works correctly on Mac OS X */ | |
113 | |
114 #pragma options align=power | 53 #pragma options align=power |
115 | 54 |
116 static volatile SInt32 audio_is_locked = 0; | 55 static volatile SInt32 audio_is_locked = 0; |
117 static volatile SInt32 need_to_mix = 0; | 56 static volatile SInt32 need_to_mix = 0; |
118 | 57 |
119 static UInt8 *buffer[2]; | 58 static UInt8 *buffer[2]; |
120 static volatile UInt32 running = 0; | 59 static volatile UInt32 running = 0; |
121 static CmpSoundHeader header; | 60 static CmpSoundHeader header; |
122 static volatile Uint32 fill_me = 0; | 61 static volatile Uint32 fill_me = 0; |
62 | |
123 | 63 |
124 static void | 64 static void |
125 mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer) | 65 mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer) |
126 { | 66 { |
127 if (!audio->paused) { | 67 if (!audio->paused) { |
148 | 88 |
149 DecrementAtomic((SInt32 *) & need_to_mix); | 89 DecrementAtomic((SInt32 *) & need_to_mix); |
150 } | 90 } |
151 | 91 |
152 static void | 92 static void |
153 Mac_LockAudio(_THIS) | 93 SNDMGR_LockDevice(_THIS) |
154 { | 94 { |
155 IncrementAtomic((SInt32 *) & audio_is_locked); | 95 IncrementAtomic((SInt32 *) & audio_is_locked); |
156 } | 96 } |
157 | 97 |
158 static void | 98 static void |
159 Mac_UnlockAudio(_THIS) | 99 SNDMGR_UnlockDevice(_THIS) |
160 { | 100 { |
161 SInt32 oldval; | 101 SInt32 oldval; |
162 | 102 |
163 oldval = DecrementAtomic((SInt32 *) & audio_is_locked); | 103 oldval = DecrementAtomic((SInt32 *) & audio_is_locked); |
164 if (oldval != 1) /* != 1 means audio is still locked. */ | 104 if (oldval != 1) /* != 1 means audio is still locked. */ |
196 cmd.cmd = bufferCmd; | 136 cmd.cmd = bufferCmd; |
197 cmd.param1 = 0; | 137 cmd.param1 = 0; |
198 cmd.param2 = (long) &header; | 138 cmd.param2 = (long) &header; |
199 SndDoCommand(chan, &cmd, 0); | 139 SndDoCommand(chan, &cmd, 0); |
200 | 140 |
201 memset(buffer[fill_me], 0, audio->spec.size); | 141 SDL_memset(buffer[fill_me], 0, audio->spec.size); |
202 | 142 |
203 /* | 143 /* |
204 * if audio device isn't locked, mix the next buffer to be queued in | 144 * if audio device isn't locked, mix the next buffer to be queued in |
205 * the memory block that just finished playing. | 145 * the memory block that just finished playing. |
206 */ | 146 */ |
217 SndDoCommand(chan, &cmd, 0); | 157 SndDoCommand(chan, &cmd, 0); |
218 } | 158 } |
219 } | 159 } |
220 | 160 |
221 static int | 161 static int |
222 Mac_OpenAudio(_THIS, SDL_AudioSpec * spec) | 162 SNDMGR_OpenDevice(_THIS, const char *devname, int iscapture) |
223 { | 163 { |
224 | 164 SDL_AudioSpec *spec = &this->spec; |
165 SndChannelPtr channel = NULL; | |
225 SndCallBackUPP callback; | 166 SndCallBackUPP callback; |
226 int sample_bits; | 167 int sample_bits; |
227 int i; | 168 int i; |
228 long initOptions; | 169 long initOptions; |
229 | 170 |
171 /* Initialize all variables that we clean on shutdown */ | |
172 this->hidden = (struct SDL_PrivateAudioData *) | |
173 SDL_malloc((sizeof *this->hidden)); | |
174 if (this->hidden == NULL) { | |
175 SDL_OutOfMemory(); | |
176 return 0; | |
177 } | |
178 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
179 | |
180 /* !!! FIXME: iterate through format matrix... */ | |
230 /* Very few conversions are required, but... */ | 181 /* Very few conversions are required, but... */ |
231 switch (spec->format) { | 182 switch (spec->format) { |
232 case AUDIO_S8: | 183 case AUDIO_S8: |
233 spec->format = AUDIO_U8; | 184 spec->format = AUDIO_U8; |
234 break; | 185 break; |
240 break; | 191 break; |
241 case AUDIO_F32LSB: | 192 case AUDIO_F32LSB: |
242 spec->format = AUDIO_F32MSB; | 193 spec->format = AUDIO_F32MSB; |
243 break; | 194 break; |
244 } | 195 } |
245 SDL_CalculateAudioSpec(spec); | 196 SDL_CalculateAudioSpec(&this->spec); |
246 | 197 |
247 /* initialize bufferCmd header */ | 198 /* initialize bufferCmd header */ |
248 memset(&header, 0, sizeof(header)); | 199 SDL_memset(&header, 0, sizeof(header)); |
249 callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc); | 200 callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc); |
250 sample_bits = spec->size / spec->samples / spec->channels * 8; | 201 sample_bits = spec->size / spec->samples / spec->channels * 8; |
251 | 202 |
252 #ifdef DEBUG_AUDIO | 203 #ifdef DEBUG_AUDIO |
253 fprintf(stderr, | 204 fprintf(stderr, |
276 header.format = kFloat32Format; | 227 header.format = kFloat32Format; |
277 } | 228 } |
278 | 229 |
279 /* allocate 2 buffers */ | 230 /* allocate 2 buffers */ |
280 for (i = 0; i < 2; i++) { | 231 for (i = 0; i < 2; i++) { |
281 buffer[i] = (UInt8 *) malloc(sizeof(UInt8) * spec->size); | 232 buffer[i] = (UInt8 *) SDL_malloc(sizeof(UInt8) * spec->size); |
282 if (buffer[i] == NULL) { | 233 if (buffer[i] == NULL) { |
234 SNDMGR_CloseDevice(this); | |
283 SDL_OutOfMemory(); | 235 SDL_OutOfMemory(); |
284 return (-1); | 236 return 0; |
285 } | 237 } |
286 memset(buffer[i], 0, spec->size); | 238 SDL_memset(buffer[i], 0, spec->size); |
287 } | 239 } |
288 | 240 |
289 /* Create the sound manager channel */ | 241 /* Create the sound manager channel */ |
290 channel = (SndChannelPtr) SDL_malloc(sizeof(*channel)); | 242 channel = (SndChannelPtr) SDL_malloc(sizeof(*channel)); |
291 if (channel == NULL) { | 243 if (channel == NULL) { |
244 SNDMGR_CloseDevice(this); | |
292 SDL_OutOfMemory(); | 245 SDL_OutOfMemory(); |
293 return (-1); | 246 return 0; |
294 } | 247 } |
248 this->hidden->channel = channel; | |
295 if (spec->channels >= 2) { | 249 if (spec->channels >= 2) { |
296 initOptions = initStereo; | 250 initOptions = initStereo; |
297 } else { | 251 } else { |
298 initOptions = initMono; | 252 initOptions = initMono; |
299 } | 253 } |
300 channel->userInfo = (long) this; | 254 channel->userInfo = (long) this; |
301 channel->qLength = 128; | 255 channel->qLength = 128; |
302 if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) { | 256 if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) { |
257 SNDMGR_CloseDevice(this); | |
303 SDL_SetError("Unable to create audio channel"); | 258 SDL_SetError("Unable to create audio channel"); |
304 SDL_free(channel); | 259 return 0; |
305 channel = NULL; | |
306 return (-1); | |
307 } | 260 } |
308 | 261 |
309 /* start playback */ | 262 /* start playback */ |
310 { | 263 { |
311 SndCommand cmd; | 264 SndCommand cmd; |
317 | 270 |
318 return 1; | 271 return 1; |
319 } | 272 } |
320 | 273 |
321 static void | 274 static void |
322 Mac_CloseAudio(_THIS) | 275 SNDMGR_CloseDevice(_THIS) |
323 { | 276 { |
324 | |
325 int i; | 277 int i; |
326 | 278 |
327 running = 0; | 279 running = 0; |
328 | 280 |
329 if (channel) { | 281 if (this->hidden->channel) { |
330 SndDisposeChannel(channel, true); | 282 SndDisposeChannel(this->hidden->channel, true); |
331 channel = NULL; | 283 this->hidden->channel = NULL; |
332 } | 284 } |
333 | 285 |
334 for (i = 0; i < 2; ++i) { | 286 for (i = 0; i < 2; ++i) { |
335 if (buffer[i]) { | 287 if (buffer[i]) { |
336 SDL_free(buffer[i]); | 288 SDL_free(buffer[i]); |
337 buffer[i] = NULL; | 289 buffer[i] = NULL; |
338 } | 290 } |
339 } | 291 } |
340 } | 292 SDL_free(this->hidden); |
341 | 293 this->hidden = NULL; |
342 #else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */ | |
343 | |
344 static void | |
345 Mac_LockAudio(_THIS) | |
346 { | |
347 /* no-op. */ | |
348 } | |
349 | |
350 static void | |
351 Mac_UnlockAudio(_THIS) | |
352 { | |
353 /* no-op. */ | |
354 } | |
355 | |
356 | |
357 /* This function is called by Sound Manager when it has exhausted one of | |
358 the buffers, so we'll zero it to silence and fill it with audio if | |
359 we're not paused. | |
360 */ | |
361 static pascal void | |
362 sndDoubleBackProc(SndChannelPtr chan, SndDoubleBufferPtr newbuf) | |
363 { | |
364 SDL_AudioDevice *audio = (SDL_AudioDevice *) newbuf->dbUserInfo[0]; | |
365 | |
366 /* If audio is quitting, don't do anything */ | |
367 if (!audio->enabled) { | |
368 return; | |
369 } | |
370 memset(newbuf->dbSoundData, 0, audio->spec.size); | |
371 newbuf->dbNumFrames = audio->spec.samples; | |
372 if (!audio->paused) { | |
373 if (audio->convert.needed) { | |
374 audio->spec.callback(audio->spec.userdata, | |
375 (Uint8 *) audio->convert.buf, | |
376 audio->convert.len); | |
377 SDL_ConvertAudio(&audio->convert); | |
378 #if 0 | |
379 if (audio->convert.len_cvt != audio->spec.size) { | |
380 /* Uh oh... probably crashes here */ ; | |
381 } | |
382 #endif | |
383 SDL_memcpy(newbuf->dbSoundData, audio->convert.buf, | |
384 audio->convert.len_cvt); | |
385 } else { | |
386 audio->spec.callback(audio->spec.userdata, | |
387 (Uint8 *) newbuf->dbSoundData, | |
388 audio->spec.size); | |
389 } | |
390 } | |
391 newbuf->dbFlags |= dbBufferReady; | |
392 } | 294 } |
393 | 295 |
394 static int | 296 static int |
395 DoubleBufferAudio_Available(void) | 297 SNDMGR_Init(SDL_AudioDriverImpl *impl) |
396 { | 298 { |
397 int available; | 299 /* Set the function pointers */ |
398 NumVersion sndversion; | 300 impl->OpenDevice = SNDMGR_OpenDevice; |
399 long response; | 301 impl->CloseDevice = SNDMGR_CloseDevice; |
400 | 302 impl->ProvidesOwnCallbackThread = 1; |
401 available = 0; | 303 impl->OnlyHasDefaultOutputDevice = 1; |
402 sndversion = SndSoundManagerVersion(); | 304 |
403 if (sndversion.majorRev >= 3) { | 305 /* Mac OS X uses threaded audio, so normal thread code is okay */ |
404 if (Gestalt(gestaltSoundAttr, &response) == noErr) { | 306 #ifndef __MACOSX__ |
405 if ((response & (1 << gestaltSndPlayDoubleBuffer))) { | 307 impl->LockDevice = SNDMGR_LockDevice; |
406 available = 1; | 308 impl->UnlockDevice = SNDMGR_UnlockDevice; |
407 } | 309 impl->SkipMixerLock = 1; |
408 } | 310 #endif |
409 } else { | |
410 if (Gestalt(gestaltSoundAttr, &response) == noErr) { | |
411 if ((response & (1 << gestaltHasASC))) { | |
412 available = 1; | |
413 } | |
414 } | |
415 } | |
416 return (available); | |
417 } | |
418 | |
419 static void | |
420 Mac_CloseAudio(_THIS) | |
421 { | |
422 int i; | |
423 | |
424 if (channel != NULL) { | |
425 /* Clean up the audio channel */ | |
426 SndDisposeChannel(channel, true); | |
427 channel = NULL; | |
428 } | |
429 for (i = 0; i < 2; ++i) { | |
430 if (audio_buf[i]) { | |
431 SDL_free(audio_buf[i]); | |
432 audio_buf[i] = NULL; | |
433 } | |
434 } | |
435 } | |
436 | |
437 static int | |
438 Mac_OpenAudio(_THIS, SDL_AudioSpec * spec) | |
439 { | |
440 SndDoubleBufferHeader2 audio_dbh; | |
441 int i; | |
442 long initOptions; | |
443 int sample_bits; | |
444 SndDoubleBackUPP doubleBackProc; | |
445 | |
446 /* Check to make sure double-buffered audio is available */ | |
447 if (!DoubleBufferAudio_Available()) { | |
448 SDL_SetError("Sound manager doesn't support double-buffering"); | |
449 return (-1); | |
450 } | |
451 | |
452 /* Very few conversions are required, but... */ | |
453 switch (spec->format) { | |
454 case AUDIO_S8: | |
455 spec->format = AUDIO_U8; | |
456 break; | |
457 case AUDIO_U16LSB: | |
458 spec->format = AUDIO_S16LSB; | |
459 break; | |
460 case AUDIO_U16MSB: | |
461 spec->format = AUDIO_S16MSB; | |
462 break; | |
463 } | |
464 SDL_CalculateAudioSpec(spec); | |
465 | |
466 /* initialize the double-back header */ | |
467 SDL_memset(&audio_dbh, 0, sizeof(audio_dbh)); | |
468 doubleBackProc = NewSndDoubleBackProc(sndDoubleBackProc); | |
469 sample_bits = spec->size / spec->samples / spec->channels * 8; | |
470 | |
471 audio_dbh.dbhNumChannels = spec->channels; | |
472 audio_dbh.dbhSampleSize = sample_bits; | |
473 audio_dbh.dbhCompressionID = 0; | |
474 audio_dbh.dbhPacketSize = 0; | |
475 audio_dbh.dbhSampleRate = spec->freq << 16; | |
476 audio_dbh.dbhDoubleBack = doubleBackProc; | |
477 audio_dbh.dbhFormat = 0; | |
478 | |
479 /* Note that we install the 16bitLittleEndian Converter if needed. */ | |
480 if (spec->format == 0x8010) { | |
481 audio_dbh.dbhCompressionID = fixedCompression; | |
482 audio_dbh.dbhFormat = k16BitLittleEndianFormat; | |
483 } | |
484 | |
485 /* allocate the 2 double-back buffers */ | |
486 for (i = 0; i < 2; ++i) { | |
487 audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer) + spec->size); | |
488 if (audio_buf[i] == NULL) { | |
489 SDL_OutOfMemory(); | |
490 return (-1); | |
491 } | |
492 audio_buf[i]->dbNumFrames = spec->samples; | |
493 audio_buf[i]->dbFlags = dbBufferReady; | |
494 audio_buf[i]->dbUserInfo[0] = (long) this; | |
495 audio_dbh.dbhBufferPtr[i] = audio_buf[i]; | |
496 } | |
497 | |
498 /* Create the sound manager channel */ | |
499 channel = (SndChannelPtr) SDL_malloc(sizeof(*channel)); | |
500 if (channel == NULL) { | |
501 SDL_OutOfMemory(); | |
502 return (-1); | |
503 } | |
504 if (spec->channels >= 2) { | |
505 initOptions = initStereo; | |
506 } else { | |
507 initOptions = initMono; | |
508 } | |
509 channel->userInfo = 0; | |
510 channel->qLength = 128; | |
511 if (SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr) { | |
512 SDL_SetError("Unable to create audio channel"); | |
513 SDL_free(channel); | |
514 channel = NULL; | |
515 return (-1); | |
516 } | |
517 | |
518 /* Start playback */ | |
519 if (SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr) & audio_dbh) | |
520 != noErr) { | |
521 SDL_SetError("Unable to play double buffered audio"); | |
522 return (-1); | |
523 } | |
524 | 311 |
525 return 1; | 312 return 1; |
526 } | 313 } |
527 | 314 |
528 #endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */ | 315 AudioBootStrap SNDMGR_bootstrap = { |
316 "sndmgr", SDL_MACOS_NAME " SoundManager", SNDMGR_Init, 0 | |
317 }; | |
318 | |
529 /* vi: set ts=4 sw=4 expandtab: */ | 319 /* vi: set ts=4 sw=4 expandtab: */ |