src/audio/macrom/SDL_romaudio.c
author Holmes Futrell <hfutrell@umail.ucsb.edu>
Mon, 18 Aug 2008 19:39:08 +0000
branchgsoc2008_iphone
changeset 2467 058e1f140ff3
parent 2060 866052b01ee5
child 2738 79c1bd651f04
permissions -rw-r--r--
Fixed problem where creating the view passed NULL as the context for setting current context ... needed to actually pass the context.
     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 static void
    93 SNDMGR_LockDevice(_THIS)
    94 {
    95     IncrementAtomic((SInt32 *) & audio_is_locked);
    96 }
    97 
    98 static void
    99 SNDMGR_UnlockDevice(_THIS)
   100 {
   101     SInt32 oldval;
   102 
   103     oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
   104     if (oldval != 1)            /* != 1 means audio is still locked. */
   105         return;
   106 
   107     /* Did we miss the chance to mix in an interrupt? Do it now. */
   108     if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
   109         /*
   110          * Note that this could be a problem if you missed an interrupt
   111          *  while the audio was locked, and get preempted by a second
   112          *  interrupt here, but that means you locked for way too long anyhow.
   113          */
   114         mix_buffer(this, buffer[fill_me]);
   115     }
   116 }
   117 
   118 static void
   119 callBackProc(SndChannel * chan, SndCommand * cmd_passed)
   120 {
   121     UInt32 play_me;
   122     SndCommand cmd;
   123     SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo;
   124 
   125     IncrementAtomic((SInt32 *) & need_to_mix);
   126 
   127     fill_me = cmd_passed->param2;       /* buffer that has just finished playing, so fill it */
   128     play_me = !fill_me;         /* filled buffer to play _now_ */
   129 
   130     if (!audio->enabled) {
   131         return;
   132     }
   133 
   134     /* queue previously mixed buffer for playback. */
   135     header.samplePtr = (Ptr) buffer[play_me];
   136     cmd.cmd = bufferCmd;
   137     cmd.param1 = 0;
   138     cmd.param2 = (long) &header;
   139     SndDoCommand(chan, &cmd, 0);
   140 
   141     SDL_memset(buffer[fill_me], 0, audio->spec.size);
   142 
   143     /*
   144      * if audio device isn't locked, mix the next buffer to be queued in
   145      *  the memory block that just finished playing.
   146      */
   147     if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) {
   148         mix_buffer(audio, buffer[fill_me]);
   149     }
   150 
   151     /* set this callback to run again when current buffer drains. */
   152     if (running) {
   153         cmd.cmd = callBackCmd;
   154         cmd.param1 = 0;
   155         cmd.param2 = play_me;
   156 
   157         SndDoCommand(chan, &cmd, 0);
   158     }
   159 }
   160 
   161 static void
   162 SNDMGR_CloseDevice(_THIS)
   163 {
   164     running = 0;
   165 
   166     if (this->hidden != NULL) {
   167         if (this->hidden->channel) {
   168             SndDisposeChannel(this->hidden->channel, true);
   169             this->hidden->channel = NULL;
   170         }
   171 
   172         SDL_free(buffer[0]);
   173         SDL_free(buffer[1]);
   174         buffer[0] = buffer[1] = NULL;
   175 
   176         SDL_free(this->hidden);
   177         this->hidden = NULL;
   178     }
   179 }
   180 
   181 static int
   182 SNDMGR_OpenDevice(_THIS, const char *devname, int iscapture)
   183 {
   184     SDL_AudioSpec *spec = &this->spec;
   185     SndChannelPtr channel = NULL;
   186     SndCallBackUPP callback;
   187     int sample_bits;
   188     int i;
   189     long initOptions;
   190 
   191     /* Initialize all variables that we clean on shutdown */
   192     this->hidden = (struct SDL_PrivateAudioData *)
   193         SDL_malloc((sizeof *this->hidden));
   194     if (this->hidden == NULL) {
   195         SDL_OutOfMemory();
   196         return 0;
   197     }
   198     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   199 
   200     /* !!! FIXME: iterate through format matrix... */
   201     /* Very few conversions are required, but... */
   202     switch (spec->format) {
   203     case AUDIO_S8:
   204         spec->format = AUDIO_U8;
   205         break;
   206     case AUDIO_U16LSB:
   207         spec->format = AUDIO_S16LSB;
   208         break;
   209     case AUDIO_U16MSB:
   210         spec->format = AUDIO_S16MSB;
   211         break;
   212     case AUDIO_F32LSB:
   213         spec->format = AUDIO_F32MSB;
   214         break;
   215     }
   216     SDL_CalculateAudioSpec(&this->spec);
   217 
   218     /* initialize bufferCmd header */
   219     SDL_memset(&header, 0, sizeof(header));
   220     callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
   221     sample_bits = spec->size / spec->samples / spec->channels * 8;
   222 
   223 #ifdef DEBUG_AUDIO
   224     fprintf(stderr,
   225             "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
   226             spec->format, spec->channels, sample_bits, spec->freq);
   227 #endif /* DEBUG_AUDIO */
   228 
   229     header.numChannels = spec->channels;
   230     header.sampleSize = sample_bits;
   231     header.sampleRate = spec->freq << 16;
   232     header.numFrames = spec->samples;
   233     header.encode = cmpSH;
   234 
   235     /* Note that we install the 16bitLittleEndian Converter if needed. */
   236     if (spec->format == AUDIO_S16LSB) {
   237         header.compressionID = fixedCompression;
   238         header.format = k16BitLittleEndianFormat;
   239     } else if (spec->format == AUDIO_S32MSB) {
   240         header.compressionID = fixedCompression;
   241         header.format = k32BitFormat;
   242     } else if (spec->format == AUDIO_S32LSB) {
   243         header.compressionID = fixedCompression;
   244         header.format = k32BitLittleEndianFormat;
   245     } else if (spec->format == AUDIO_F32MSB) {
   246         header.compressionID = fixedCompression;
   247         header.format = kFloat32Format;
   248     }
   249 
   250     /* allocate 2 buffers */
   251     for (i = 0; i < 2; i++) {
   252         buffer[i] = (UInt8 *) SDL_malloc(sizeof(UInt8) * spec->size);
   253         if (buffer[i] == NULL) {
   254             SNDMGR_CloseDevice(this);
   255             SDL_OutOfMemory();
   256             return 0;
   257         }
   258         SDL_memset(buffer[i], 0, spec->size);
   259     }
   260 
   261     /* Create the sound manager channel */
   262     channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
   263     if (channel == NULL) {
   264         SNDMGR_CloseDevice(this);
   265         SDL_OutOfMemory();
   266         return 0;
   267     }
   268     this->hidden->channel = channel;
   269     if (spec->channels >= 2) {
   270         initOptions = initStereo;
   271     } else {
   272         initOptions = initMono;
   273     }
   274     channel->userInfo = (long) this;
   275     channel->qLength = 128;
   276     if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) {
   277         SNDMGR_CloseDevice(this);
   278         SDL_SetError("Unable to create audio channel");
   279         return 0;
   280     }
   281 
   282     /* start playback */
   283     {
   284         SndCommand cmd;
   285         cmd.cmd = callBackCmd;
   286         cmd.param2 = 0;
   287         running = 1;
   288         SndDoCommand(channel, &cmd, 0);
   289     }
   290 
   291     return 1;
   292 }
   293 
   294 static int
   295 SNDMGR_Init(SDL_AudioDriverImpl * impl)
   296 {
   297     /* Set the function pointers */
   298     impl->OpenDevice = SNDMGR_OpenDevice;
   299     impl->CloseDevice = SNDMGR_CloseDevice;
   300     impl->ProvidesOwnCallbackThread = 1;
   301     impl->OnlyHasDefaultOutputDevice = 1;
   302 
   303 /* Mac OS X uses threaded audio, so normal thread code is okay */
   304 #ifndef __MACOSX__
   305     impl->LockDevice = SNDMGR_LockDevice;
   306     impl->UnlockDevice = SNDMGR_UnlockDevice;
   307     impl->SkipMixerLock = 1;
   308 #endif
   309 
   310     return 1;
   311 }
   312 
   313 AudioBootStrap SNDMGR_bootstrap = {
   314     "sndmgr", SDL_MACOS_NAME " SoundManager", SNDMGR_Init, 0
   315 };
   316 
   317 /* vi: set ts=4 sw=4 expandtab: */