Home | History | Annotate | Download | only in macrom
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 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 (at) 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 
    497