SDL-1.2.14
[sdl_omap.git] / src / audio / macrom / SDL_romaudio.c
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
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 Audio_Available(void)
59 {
60     return(1);
61 }
62
63 static void Audio_DeleteDevice(SDL_AudioDevice *device)
64 {
65     SDL_free(device->hidden);
66     SDL_free(device);
67 }
68
69 static 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
103 AudioBootStrap 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
113 static volatile SInt32 audio_is_locked = 0;
114 static volatile SInt32 need_to_mix = 0;
115
116 static UInt8  *buffer[2];
117 static volatile UInt32 running = 0;
118 static CmpSoundHeader header;
119 static volatile Uint32 fill_me = 0;
120
121 static 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
146 static void Mac_LockAudio(_THIS)
147 {
148     IncrementAtomic((SInt32 *) &audio_is_locked);
149 }
150
151 static 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
170 static 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
211 static 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
297 static 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
318 static void Mac_LockAudio(_THIS)
319 {
320     /* no-op. */
321 }
322
323 static 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 */
333 static pascal
334 void 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
364 static 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
388 static 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
405 static 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