Okay, I cleaned up the patch, and it appears to run correctly now, but I haven’t tested it too heavily. I’ll need some Mac people to pound on this.
Debugging was exasperated by the fact that SDL_mixer was calling SDL_LockAudio() from it’s audio callback. Who could have introduced THAT stupid bug?! (whistles)
Anyhow, attached is a hopefully fully functional patch to SDL CVS for the Mac audio code and a patch to SDL_mixer CVS to fix my dumbass bug.
Do not apply the previous patch; this one is better.
–ryan.
-------------- next part --------------
diff -u -r1.34 mixer.c
--- mixer.c 2002/01/14 19:35:45 1.34
+++ mixer.c 2002/03/27 23:05:00
@@ -41,6 +41,9 @@
static int audio_opened = 0;
+/* rcg03272002 prevent SDL_LockAudio() calls from inside the audio callback. */
+static volatile int in_mixer_callback = 0;+
static SDL_AudioSpec mixer;
static SDL_mutex *mixer_lock;
@@ -148,6 +151,8 @@
int i, mixable, volume;
Uint32 sdl_ticks;
+ in_mixer_callback = 1;
+
/* Mix the music (must be done before the channels are added) */
if ( music_active || (mix_music != music_mixer) ) {
mix_music(music_data, stream, len);
@@ -236,6 +241,8 @@
mix_postmix(mix_postmix_data, stream, len);
}
SDL_mutexV(mixer_lock);
+
+ in_mixer_callback = 0;
}
static void PrintFormat(char *title, SDL_AudioSpec *fmt)
@@ -1137,7 +1144,9 @@
return(0);
}
- SDL_LockAudio();
+ if (!in_mixer_callback)
+ SDL_LockAudio();
+
for (cur = *e; cur != NULL; cur = next) {
next = cur->next;
if (cur->done_callback != NULL) {
@@ -1146,7 +1155,9 @@
free(cur);
}
*e = NULL;
- SDL_UnlockAudio();
+
+ if (!in_mixer_callback)
+ SDL_UnlockAudio();
return(1);
}
-------------- next part --------------
diff -u -r1.13 SDL_audio.c
--- src/audio/SDL_audio.c 2002/03/06 11:23:02 1.13
+++ src/audio/SDL_audio.c 2002/03/27 23:01:59
@@ -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
--- src/audio/macrom/SDL_romaudio.c 2002/03/06 11:23:02 1.5
+++ src/audio/macrom/SDL_romaudio.c 2002/03/27 23:02:00
@@ -30,6 +30,7 @@
#else
# include <Sound.h> /* SoundManager interface */
# include <Gestalt.h>
+# include <DriverServices.h>
#endif
#include <stdlib.h>
@@ -94,35 +95,20 @@
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 volatile Uint32 fill_me = 0;
-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_ */
-
- if ( ! audio->enabled ) {
- return;
- }
-
- 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);
-
+static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer)
+{
if ( ! audio->paused ) {
if ( audio->convert.needed ) {
#if MACOSX
@@ -139,29 +125,85 @@
/* 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
}
}
- if ( running ) {
+ DecrementAtomic((SInt32 *) &need_to_mix);
+}
+
+void SDL_LockAudio_MacOS(void)
+{
+ IncrementAtomic((SInt32 *) &audio_is_locked);
+}
+
+
+void SDL_UnlockAudio_MacOS(void)
+{
+ SInt32 oldval;
+ 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.
+ */
+ 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;
+
+ IncrementAtomic((SInt32 *) &need_to_mix);
+
+ 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.
+ */
+ if ( ! BitAndAtomic(0xFFFFFFFF, &audio_is_locked) ) {
+ mix_buffer (audio, buffer[fill_me]);
+ }
+
+ /* set this callback to run again when current buffer drains. */
+ if ( running ) {
cmd.cmd = callBackCmd;
cmd.param1 = 0;
cmd.param2 = play_me;
SndDoCommand (chan, &cmd, 0);
}
-
}
static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
@@ -170,7 +212,7 @@
int sample_bits;
int i;
long initOptions;
-
+
/* Very few conversions are required, but... */
switch (spec->format) {
case AUDIO_S8:
@@ -231,8 +273,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;
@@ -271,6 +312,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