src/audio/macrom/SDL_romaudio.c
author Sam Lantinga
Mon, 01 Sep 2008 16:04:20 +0000
changeset 2738 79c1bd651f04
parent 2060 866052b01ee5
child 2859 99210400e8b9
permissions -rw-r--r--
Fixed a bunch of compile warnings on Mac OS X
     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@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* This should work on PowerPC and Intel Mac OS X, and Carbonized Mac OS 9. */
    25 
    26 #if defined(__APPLE__) && defined(__MACH__)
    27 #  define SDL_MACOS_NAME "Mac OS X"
    28 #  include <Carbon/Carbon.h>
    29 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
    30 #  define SDL_MACOS_NAME "Mac OS 9"
    31 #  include <Carbon.h>
    32 #else
    33 #  define SDL_MACOS_NAME "Mac OS 9"
    34 #  include <Sound.h>            /* SoundManager interface */
    35 #  include <Gestalt.h>
    36 #  include <DriverServices.h>
    37 #endif
    38 
    39 #if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
    40 #if !defined(NewSndCallBackProc)        /* avoid circular redefinition... */
    41 #define NewSndCallBackUPP NewSndCallBackProc
    42 #endif
    43 #if !defined(NewSndCallBackUPP)
    44 #define NewSndCallBackUPP NewSndCallBackProc
    45 #endif
    46 #endif
    47 
    48 #include "SDL_audio.h"
    49 #include "../SDL_audio_c.h"
    50 #include "../SDL_sysaudio.h"
    51 #include "SDL_romaudio.h"
    52 
    53 #pragma options align=power
    54 
    55 static volatile SInt32 audio_is_locked = 0;
    56 static volatile SInt32 need_to_mix = 0;
    57 
    58 static UInt8 *buffer[2];
    59 static volatile UInt32 running = 0;
    60 static CmpSoundHeader header;
    61 static volatile Uint32 fill_me = 0;
    62 
    63 
    64 static void
    65 mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer)
    66 {
    67     if (!audio->paused) {
    68 #ifdef __MACOSX__
    69         SDL_mutexP(audio->mixer_lock);
    70 #endif
    71         if (audio->convert.needed) {
    72             audio->spec.callback(audio->spec.userdata,
    73                                  (Uint8 *) audio->convert.buf,
    74                                  audio->convert.len);
    75             SDL_ConvertAudio(&audio->convert);
    76             if (audio->convert.len_cvt != audio->spec.size) {
    77                 /* Uh oh... probably crashes here */ ;
    78             }
    79             SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
    80         } else {
    81             audio->spec.callback(audio->spec.userdata, buffer,
    82                                  audio->spec.size);
    83         }
    84 #ifdef __MACOSX__
    85         SDL_mutexV(audio->mixer_lock);
    86 #endif
    87     }
    88 
    89     DecrementAtomic((SInt32 *) & need_to_mix);
    90 }
    91 
    92 #ifndef __MACOSX__
    93 static void
    94 SNDMGR_LockDevice(_THIS)
    95 {
    96     IncrementAtomic((SInt32 *) & audio_is_locked);
    97 }
    98 
    99 static void
   100 SNDMGR_UnlockDevice(_THIS)
   101 {
   102     SInt32 oldval;
   103 
   104     oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
   105     if (oldval != 1)            /* != 1 means audio is still locked. */
   106         return;
   107 
   108     /* Did we miss the chance to mix in an interrupt? Do it now. */
   109     if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
   110         /*
   111          * Note that this could be a problem if you missed an interrupt
   112          *  while the audio was locked, and get preempted by a second
   113          *  interrupt here, but that means you locked for way too long anyhow.
   114          */
   115         mix_buffer(this, buffer[fill_me]);
   116     }
   117 }
   118 #endif // __MACOSX__
   119 
   120 static void
   121 callBackProc(SndChannel * chan, SndCommand * cmd_passed)
   122 {
   123     UInt32 play_me;
   124     SndCommand cmd;
   125     SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo;
   126 
   127     IncrementAtomic((SInt32 *) & need_to_mix);
   128 
   129     fill_me = cmd_passed->param2;       /* buffer that has just finished playing, so fill it */
   130     play_me = !fill_me;         /* filled buffer to play _now_ */
   131 
   132     if (!audio->enabled) {
   133         return;
   134     }
   135 
   136     /* queue previously mixed buffer for playback. */
   137     header.samplePtr = (Ptr) buffer[play_me];
   138     cmd.cmd = bufferCmd;
   139     cmd.param1 = 0;
   140     cmd.param2 = (long) &header;
   141     SndDoCommand(chan, &cmd, 0);
   142 
   143     SDL_memset(buffer[fill_me], 0, audio->spec.size);
   144 
   145     /*
   146      * if audio device isn't locked, mix the next buffer to be queued in
   147      *  the memory block that just finished playing.
   148      */
   149     if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) {
   150         mix_buffer(audio, buffer[fill_me]);
   151     }
   152 
   153     /* set this callback to run again when current buffer drains. */
   154     if (running) {
   155         cmd.cmd = callBackCmd;
   156         cmd.param1 = 0;
   157         cmd.param2 = play_me;
   158 
   159         SndDoCommand(chan, &cmd, 0);
   160     }
   161 }
   162 
   163 static void
   164 SNDMGR_CloseDevice(_THIS)
   165 {
   166     running = 0;
   167 
   168     if (this->hidden != NULL) {
   169         if (this->hidden->channel) {
   170             SndDisposeChannel(this->hidden->channel, true);
   171             this->hidden->channel = NULL;
   172         }
   173 
   174         SDL_free(buffer[0]);
   175         SDL_free(buffer[1]);
   176         buffer[0] = buffer[1] = NULL;
   177 
   178         SDL_free(this->hidden);
   179         this->hidden = NULL;
   180     }
   181 }
   182 
   183 static int
   184 SNDMGR_OpenDevice(_THIS, const char *devname, int iscapture)
   185 {
   186     SDL_AudioSpec *spec = &this->spec;
   187     SndChannelPtr channel = NULL;
   188     SndCallBackUPP callback;
   189     int sample_bits;
   190     int i;
   191     long initOptions;
   192 
   193     /* Initialize all variables that we clean on shutdown */
   194     this->hidden = (struct SDL_PrivateAudioData *)
   195         SDL_malloc((sizeof *this->hidden));
   196     if (this->hidden == NULL) {
   197         SDL_OutOfMemory();
   198         return 0;
   199     }
   200     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   201 
   202     /* !!! FIXME: iterate through format matrix... */
   203     /* Very few conversions are required, but... */
   204     switch (spec->format) {
   205     case AUDIO_S8:
   206         spec->format = AUDIO_U8;
   207         break;
   208     case AUDIO_U16LSB:
   209         spec->format = AUDIO_S16LSB;
   210         break;
   211     case AUDIO_U16MSB:
   212         spec->format = AUDIO_S16MSB;
   213         break;
   214     case AUDIO_F32LSB:
   215         spec->format = AUDIO_F32MSB;
   216         break;
   217     }
   218     SDL_CalculateAudioSpec(&this->spec);
   219 
   220     /* initialize bufferCmd header */
   221     SDL_memset(&header, 0, sizeof(header));
   222     callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
   223     sample_bits = spec->size / spec->samples / spec->channels * 8;
   224 
   225 #ifdef DEBUG_AUDIO
   226     fprintf(stderr,
   227             "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
   228             spec->format, spec->channels, sample_bits, spec->freq);
   229 #endif /* DEBUG_AUDIO */
   230 
   231     header.numChannels = spec->channels;
   232     header.sampleSize = sample_bits;
   233     header.sampleRate = spec->freq << 16;
   234     header.numFrames = spec->samples;
   235     header.encode = cmpSH;
   236 
   237     /* Note that we install the 16bitLittleEndian Converter if needed. */
   238     if (spec->format == AUDIO_S16LSB) {
   239         header.compressionID = fixedCompression;
   240         header.format = k16BitLittleEndianFormat;
   241     } else if (spec->format == AUDIO_S32MSB) {
   242         header.compressionID = fixedCompression;
   243         header.format = k32BitFormat;
   244     } else if (spec->format == AUDIO_S32LSB) {
   245         header.compressionID = fixedCompression;
   246         header.format = k32BitLittleEndianFormat;
   247     } else if (spec->format == AUDIO_F32MSB) {
   248         header.compressionID = fixedCompression;
   249         header.format = kFloat32Format;
   250     }
   251 
   252     /* allocate 2 buffers */
   253     for (i = 0; i < 2; i++) {
   254         buffer[i] = (UInt8 *) SDL_malloc(sizeof(UInt8) * spec->size);
   255         if (buffer[i] == NULL) {
   256             SNDMGR_CloseDevice(this);
   257             SDL_OutOfMemory();
   258             return 0;
   259         }
   260         SDL_memset(buffer[i], 0, spec->size);
   261     }
   262 
   263     /* Create the sound manager channel */
   264     channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
   265     if (channel == NULL) {
   266         SNDMGR_CloseDevice(this);
   267         SDL_OutOfMemory();
   268         return 0;
   269     }
   270     this->hidden->channel = channel;
   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         SNDMGR_CloseDevice(this);
   280         SDL_SetError("Unable to create audio channel");
   281         return 0;
   282     }
   283 
   284     /* start playback */
   285     {
   286         SndCommand cmd;
   287         cmd.cmd = callBackCmd;
   288         cmd.param2 = 0;
   289         running = 1;
   290         SndDoCommand(channel, &cmd, 0);
   291     }
   292 
   293     return 1;
   294 }
   295 
   296 static int
   297 SNDMGR_Init(SDL_AudioDriverImpl * impl)
   298 {
   299     /* Set the function pointers */
   300     impl->OpenDevice = SNDMGR_OpenDevice;
   301     impl->CloseDevice = SNDMGR_CloseDevice;
   302     impl->ProvidesOwnCallbackThread = 1;
   303     impl->OnlyHasDefaultOutputDevice = 1;
   304 
   305 /* Mac OS X uses threaded audio, so normal thread code is okay */
   306 #ifndef __MACOSX__
   307     impl->LockDevice = SNDMGR_LockDevice;
   308     impl->UnlockDevice = SNDMGR_UnlockDevice;
   309     impl->SkipMixerLock = 1;
   310 #endif
   311 
   312     return 1;
   313 }
   314 
   315 AudioBootStrap SNDMGR_bootstrap = {
   316     "sndmgr", SDL_MACOS_NAME " SoundManager", SNDMGR_Init, 0
   317 };
   318 
   319 /* vi: set ts=4 sw=4 expandtab: */