MacOS LockAudio() implementation start

Ok, here’s my stab at it. Darrell, you’ll have to tell me if this is completely braindead, since my brain is hurting from considering race condition possibilities and reading Inside Mac. :slight_smile:

This patch is actually broken at the moment, but it’s a good start. It does the locking/unlocking stuff, but the call to catch up on the mixing in the unlocking code is commented out, since it screws up the audio stream; I hope this isn’t a problem with mixing at a later time…it could very well be a late-night logic bug.

At some point (1.3?) we should probably make SDL_LockAudio call into a backend-specific method, but for now I’ve just ifdef’d it.

Like I said, it’s a start. :slight_smile:

cd /where/i/keep/SDL12/src/audio ; patch -p0 < mypatch.diff

–ryan.

diff -u -r1.13 SDL_audio.c
--- SDL_audio.c	2002/03/06 11:23:02	1.13
+++ SDL_audio.c	2002/03/27 13:11:48
@@ -503,6 +503,15 @@
 
 void SDL_LockAudio (void)
 {
+/*
+ * !!! FIXME: Ideally, a LockAudio function pointer should be part
+ * !!! FIXME:  of the SDL_AudioDevice struct, even if most can just use a
+ * !!! FIXME:  default implementation. --ryan.
+ */
+#if ((defined __MACOS__) || (defined __MACOSX__))
+    void SDL_LockAudio_MacOS(void);
+    SDL_LockAudio_MacOS();
+#else
 	SDL_AudioDevice *audio = current_audio;
 
 	/* Obtain a lock on the mixing buffers */
@@ -512,10 +521,20 @@
 		}
 		SDL_mutexP(audio->mixer_lock);
 	}
+#endif
 }
 
 void SDL_UnlockAudio (void)
 {
+/*
+ * !!! FIXME: Ideally, an UnlockAudio function pointer should be part
+ * !!! FIXME:  of the SDL_AudioDevice struct, even if most can just use a
+ * !!! FIXME:  default implementation. --ryan.
+ */
+#if ((defined __MACOS__) || (defined __MACOSX__))
+    void SDL_UnlockAudio_MacOS(void);
+    SDL_UnlockAudio_MacOS();
+#else
 	SDL_AudioDevice *audio = current_audio;
 
 	/* Release lock on the mixing buffers */
@@ -525,6 +544,7 @@
 		}
 		SDL_mutexV(audio->mixer_lock);
 	}
+#endif
 }
 
 void SDL_CloseAudio (void)
diff -u -r1.5 SDL_romaudio.c
--- macrom/SDL_romaudio.c	2002/03/06 11:23:02	1.5
+++ macrom/SDL_romaudio.c	2002/03/27 13:11:49
@@ -30,6 +30,7 @@
 #else
 #  include <Sound.h> /* SoundManager interface */
 #  include <Gestalt.h>
+#  include <DriverServices.h>
 #endif
 
 #include <stdlib.h>
@@ -94,35 +95,24 @@
 	Audio_Available, Audio_CreateDevice
 };
 
-#if TARGET_API_MAC_CARBON
+#if 1 /*TARGET_API_MAC_CARBON*/
 
+#pragma options align=power+
+static volatile SInt32 audio_is_locked = 0;
+static volatile SInt32 need_to_mix = 0;
+
 static UInt8  *buffer[2];
 static volatile UInt32 running = 0;
 static CmpSoundHeader header;
-
-static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
-   
-   UInt32 fill_me, play_me;
-   SndCommand cmd; 
-   SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
-   
-   fill_me = cmd_passed->param2;  /* buffer that has just finished playing, so fill it */      
-   play_me = ! fill_me;           /* filled buffer to play _now_ */
+static volatile Uint32 fill_me = 0;
 
-   if ( ! audio->enabled ) {
-      return;
-   }
-   
-   header.samplePtr = (Ptr)buffer[play_me];
-   
-   cmd.cmd = bufferCmd;
-   cmd.param1 = 0; 
-   cmd.param2 = (long)&header;
+/*static FILE *audiodebug = NULL;*/
 
-   SndDoCommand (chan, &cmd, 0);
-   
-   memset (buffer[fill_me], 0, audio->spec.size);
-   
+static void mix_buffer(_THIS, UInt8 **_buffer, Uint32 fill_buf)
+{
+   UInt8 *buffer = _buffer[fill_buf];
+   SDL_AudioDevice *audio = this; /* God, the _THIS macro needs to go. */
    if ( ! audio->paused ) {
         if ( audio->convert.needed ) {
             #if MACOSX
@@ -139,14 +129,12 @@
                 /* Uh oh... probably crashes here; */
             }
 #endif
-            memcpy(buffer[fill_me], audio->convert.buf,
-                            audio->convert.len_cvt);
+            memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
         } else {
             #if MACOSX
                 SDL_mutexP(audio->mixer_lock);
             #endif
-            audio->spec.callback(audio->spec.userdata,
-                (Uint8 *)buffer[fill_me], audio->spec.size);
+            audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
             #if MACOSX
                 SDL_mutexV(audio->mixer_lock);
             #endif
@@ -154,16 +142,75 @@
     }
 
     if ( running ) {
-         
+      SndCommand cmd; 
       cmd.cmd = callBackCmd;
       cmd.param1 = 0;
-      cmd.param2 = play_me;
+      cmd.param2 = !fill_buf;
    
-      SndDoCommand (chan, &cmd, 0);
+      SndDoCommand (channel, &cmd, 0);
    }
 
+   need_to_mix = 0;
+}
+
+void SDL_LockAudio_MacOS(void)
+{
+    IncrementAtomic((SInt32 *) &audio_is_locked);
+}
+
+void SDL_UnlockAudio_MacOS(void)
+{
+    SInt32 oldval = DecrementAtomic((SInt32 *) &audio_is_locked);
+    if ( oldval != 1 )  /* != 1 means audio is still locked. */
+        return;
+
+    /* Did we miss the chance to mix in an interrupt? Do it now. */
+    if ( BitAndAtomic (0xFFFFFFFF, &need_to_mix) ) {
+        /*
+         * Note that this could be a problem if you missed an interrupt
+         *  while the audio was locked, and get preempted by a second
+         *  interrupt here, but that means you locked for way too long anyhow.
+         */
+/*fprintf(audiodebug, "miss.\n");*/
+        /*mix_buffer (current_audio, buffer, fill_me);*/
+    }
 }
 
+static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
+   UInt32 play_me;
+   SndCommand cmd; 
+   SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
+
+   need_to_mix = 1;
+
+   fill_me = cmd_passed->param2;  /* buffer that has just finished playing, so fill it */      
+   play_me = ! fill_me;           /* filled buffer to play _now_ */
+
+   if ( ! audio->enabled ) {
+      return;
+   }
+   
+   /* queue previously mixed buffer for playback. */
+   header.samplePtr = (Ptr)buffer[play_me];
+   cmd.cmd = bufferCmd;
+   cmd.param1 = 0; 
+   cmd.param2 = (long)&header;
+   SndDoCommand (chan, &cmd, 0);
+
+   memset (buffer[fill_me], 0, audio->spec.size);
+
+   /*
+    * if audio device isn't locked, mix the next buffer to be queued in
+    *  the memory block that just finished playing.
+    *
+    * Note the access to audio_is_locked, etc. need not be atomic, since we're
+    *  guaranteed to not be preempted inside this interrupt handler.
+    */
+   if ( ! audio_is_locked ) {
+      mix_buffer (audio, buffer, fill_me);
+   }
+}
+
 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
 
    SndCallBackUPP callback;
@@ -171,6 +218,8 @@
    int i;
    long initOptions;
    
+/*   audiodebug = fopen("audiodebug.txt", "w");*/
+   
    /* Very few conversions are required, but... */
     switch (spec->format) {
         case AUDIO_S8:
@@ -231,8 +280,7 @@
     }
     channel->userInfo = (long)this;
     channel->qLength = 128;
-    if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) !=
-noErr ) {
+    if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr ) {
         SDL_SetError("Unable to create audio channel");
         free(channel);
         channel = NULL;
@@ -254,6 +302,8 @@
 static void Mac_CloseAudio(_THIS) {
    
    int i;
+/*fflush(audiodebug);*/
+/*fclose(audiodebug);*/
    
    running = 0;
    
@@ -271,6 +321,17 @@
 }
 
 #else /* !TARGET_API_MAC_CARBON */
+
+void SDL_LockAudio_MacOS(void)
+{
+    /* no-op. */
+}
+
+void SDL_UnlockAudio_MacOS(void)
+{
+    /* no-op. */
+}
+
 
 /* This function is called by Sound Manager when it has exhausted one of
    the buffers, so we'll zero it to silence and fill it with audio if