More SDL_mixer patches

Attached is a patch for SDL_mixer against the latest CVS. It adds two
functions to the library:

void Mix_ChannelFinished(void (*callback)(int channel));

This registers a callback that is called when a channel finishes playing
(including all looping it was commanded to do). Without this, you have to
poll to figure out if a channel has stopped yet, which isn’t as elegant.
The callback is given the number of the channel that has finished. Calling
this function with a NULL argument turns off the callback.

Mix_Chunk *Mix_GetChunk(int channel);

This returns the Mix_Chunk * associated with a channel, or NULL if there
isn’t one (or the channel number is bogus). This, in conjunction with the
above callback, makes it very easy to use a throw-away sample in the mixer:

  void my_callback(int channel)
  {
    Mix_FreeChunk(Mix_GetChunk(channel));
  }

playwave.c is patched, too. Flip the TEST_MIX_CHANNELFINISHED define in
that file to try out these two new functions.

–ryan.

--- SDL_mixer-virgin/SDL_mixer.h	Fri Feb 16 17:46:09 2001
+++ SDL_mixer/SDL_mixer.h	Thu Jun  7 12:24:58 2001
@@ -123,6 +123,11 @@
 /* Get a pointer to the user data for the current music hook */
 extern DECLSPEC void *Mix_GetMusicHookData(void);
 
+/* Add your own callback when a channel has finished playing. NULL
+ * to disable callback.
+ */
+extern DECLSPEC void Mix_ChannelFinished(void (*channel_finished)(int channel));+

 /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
    them dynamically to the next sample if requested with a -1 value below.
    Returns the number of reserved channels.
@@ -218,6 +223,11 @@

 /* Stop music and set external music playback command */
 extern DECLSPEC int Mix_SetMusicCMD(const char *command);
+
+/* Get the Mix_Chunk currently associated with a mixer channel
+    Returns NULL if it's an invalid channel, or there's no chunk associated.
+*/
+extern DECLSPEC Mix_Chunk *Mix_GetChunk(int channel);
 
 /* Close the mixer, halting all playing audio */
 extern DECLSPEC void Mix_CloseAudio(void);
--- SDL_mixer-virgin/mixer.c	Thu May 10 11:17:57 2001
+++ SDL_mixer/mixer.c	Thu Jun  7 12:54:25 2001
@@ -61,6 +61,9 @@
 static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;
 static void *mix_postmix_data = NULL;
 
+/* rcg07062001 callback to alert when channels are done playing. */
+static void (*channel_done_callback)(int channel) = NULL;
+
 /* Music function declarations */
 extern int open_music(SDL_AudioSpec *mixer);
 extern void close_music(void);
@@ -139,6 +142,11 @@
 						mix_channel[i].playing = mix_channel[i].chunk->alen;
 					}
 				}
+
+				/* rcg06072001 Alert app if channel is done playing. */
+				if ( (!mix_channel[i].playing) && (channel_done_callback) ) {
+					channel_done_callback(i);
+				}
 			}
 		}
 	}
@@ -437,6 +445,14 @@
 	return(music_data);
 }
 
+void Mix_ChannelFinished(void (*channel_finished)(int channel))
+{
+    SDL_LockAudio();
+    channel_done_callback = channel_finished;
+    SDL_UnlockAudio();
+}
+
+
 /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
    them dynamically to the next sample if requested with a -1 value below.
    Returns the number of reserved channels.
@@ -710,6 +726,18 @@
 		}
 	}
 	return(status);
+}
+
+/* rcg06072001 Get the chunk associated with a channel. */
+Mix_Chunk *Mix_GetChunk(int channel)
+{
+	Mix_Chunk *retval = NULL;
+
+	if ((channel >= 0) && (channel < num_channels)) {
+		retval = mix_channel[channel].chunk;
+	}
+
+	return(retval);
 }
 
 /* Close the mixer, halting all playing audio */
--- SDL_mixer-virgin/playwave.c	Thu Apr  5 14:25:56 2001
+++ SDL_mixer/playwave.c	Thu Jun  7 12:52:51 2001
@@ -54,7 +54,22 @@
 {
 	fprintf(stderr, "Usage: %s [-8] [-r rate] [-l] [-m] <wavefile>\n", argv0);
 }
-	
+
+
+/*#define TEST_MIX_CHANNELFINISHED*/
+#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
+static volatile int channel_is_done = 0;
+static void channel_complete_callback(int chan)
+{
+	Mix_Chunk *done_chunk = Mix_GetChunk(chan);
+	printf("We were just alerted that Mixer channel #%d is done.\n", chan);
+	printf("Channel's chunk pointer is (%p).\n", done_chunk);
+	printf(" Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
+	channel_is_done = 1;
+}
+#endif
+
+
 main(int argc, char *argv[])
 {
 	int audio_rate;
@@ -126,10 +141,23 @@
 		return(2);
 	}
 
+#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
+	setbuf(stdout, NULL);
+	Mix_ChannelFinished(channel_complete_callback);
+#endif
+
 	/* Play and then exit */
 	Mix_PlayChannel(0, wave, loops);
+
+#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
+	while (!channel_is_done) {
+		SDL_Delay(100);
+	}
+#else
 	while ( Mix_Playing(0) ) {
 		SDL_Delay(100);
 	}
+#endif
+
 	return(0);
 }

Attached is a patch for SDL_mixer against the latest CVS. It adds two
functions to the library:

Great, thanks! These are great additions to the API, and now in CVS.

See ya,
-Sam Lantinga, Lead Programmer, Loki Software, Inc.