diff playsound/playsound_simple.c @ 492:1b91006ebfef stable-1.0

Added playsound_simple.c, which is a better example than the monster playsound.c.
author Ryan C. Gordon <icculus@icculus.org>
date Sat, 01 Oct 2005 15:35:09 +0000
parents
children 6455549cd562
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playsound/playsound_simple.c	Sat Oct 01 15:35:09 2005 +0000
@@ -0,0 +1,193 @@
+/*
+ * SDL_sound -- An abstract sound format decoding API.
+ * Copyright (C) 2001  Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * This is just a simple "decode sound, play it through SDL" example.
+ *  The much more complex, fancy, and robust code is playsound.c.
+ *
+ * Please see the file COPYING in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
+ */
+
+#include "SDL.h"
+#include "SDL_sound.h"
+
+/* global decoding state. */
+typedef struct
+{
+    Sound_Sample *sample;
+    SDL_AudioSpec devformat;
+    Uint8 *decoded_ptr;
+    Uint32 decoded_bytes;
+} PlaysoundAudioCallbackData;
+
+/*
+ * This variable is flipped to non-zero when the audio callback has
+ *  finished playing the whole file.
+ */
+static volatile int global_done_flag = 0;
+
+
+/*
+ * The audio callback. SDL calls this frequently to feed the audio device.
+ *  We decode the audio file being played in here in small chunks and feed
+ *  the device as necessary. Other solutions may want to predecode more
+ *  (or all) of the file, since this needs to run fast and frequently,
+ *  but since we're only sitting here and waiting for the file to play,
+ *  the only real requirement is that we can decode a given audio file
+ *  faster than realtime, which isn't really a problem with any modern format
+ *  on even pretty old hardware at this point.
+ */
+static void audio_callback(void *userdata, Uint8 *stream, int len)
+{
+    PlaysoundAudioCallbackData *data = (PlaysoundAudioCallbackData *) userdata;
+    Sound_Sample *sample = data->sample;
+    int bw = 0; /* bytes written to stream this time through the callback */
+
+    while (bw < len)
+    {
+        int cpysize;  /* bytes to copy on this iteration of the loop. */
+
+        if (data->decoded_bytes == 0) /* need more data! */
+        {
+            /* if there wasn't previously an error or EOF, read more. */
+            if ( ((sample->flags & SOUND_SAMPLEFLAG_ERROR) == 0) &&
+                 ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) )
+            {
+                data->decoded_bytes = Sound_Decode(sample);
+                data->decoded_ptr = sample->buffer;
+            } /* if */
+
+            if (data->decoded_bytes == 0)
+            {
+                /* ...there isn't any more data to read! */
+                memset(stream + bw, '\0', len - bw);  /* write silence. */
+                global_done_flag = 1;
+                return;  /* we're done playback, one way or another. */
+            } /* if */
+        } /* if */
+
+        /* we have data decoded and ready to write to the device... */
+        cpysize = len - bw;  /* len - bw == amount device still wants. */
+        if (cpysize > data->decoded_bytes)
+            cpysize = data->decoded_bytes;  /* clamp to what we have left. */
+
+        /* if it's 0, next iteration will decode more or decide we're done. */
+        if (cpysize > 0)
+        {
+            /* write this iteration's data to the device. */
+            memcpy(stream + bw, (Uint8 *) data->decoded_ptr, cpysize);
+
+            /* update state for next iteration or callback */
+            bw += cpysize;
+            data->decoded_ptr += cpysize;
+            data->decoded_bytes -= cpysize;
+        } /* if */
+    } /* while */
+} /* audio_callback */
+
+
+
+static void playOneSoundFile(const char *fname)
+{
+    PlaysoundAudioCallbackData data;
+
+    memset(&data, '\0', sizeof (PlaysoundAudioCallbackData));
+    data.sample = Sound_NewSampleFromFile(fname, NULL, 65536);
+    if (data.sample == NULL)
+    {
+        fprintf(stderr, "Couldn't load '%s': %s.\n", fname, Sound_GetError());
+        return;
+    } /* if */
+
+    /*
+     * Open device in format of the the sound to be played.
+     *  We open and close the device for each sound file, so that SDL
+     *  handles the data conversion to hardware format; this is the
+     *  easy way out, but isn't practical for most apps. Usually you'll
+     *  want to pick one format for all the data or one format for the
+     *  audio device and convert the data when needed. This is a more
+     *  complex issue than I can describe in a source code comment, though.
+     */
+    data.devformat.freq = data.sample->actual.rate;
+    data.devformat.format = data.sample->actual.format;
+    data.devformat.channels = data.sample->actual.channels;
+    data.devformat.samples = 4096;  /* I just picked a largish number here. */
+    data.devformat.callback = audio_callback;
+    data.devformat.userdata = &data;
+    if (SDL_OpenAudio(&data.devformat, NULL) < 0)
+    {
+        fprintf(stderr, "Couldn't open audio device: %s.\n", SDL_GetError());
+        Sound_FreeSample(data.sample);
+        return;
+    } /* if */
+
+    printf("Now playing [%s]...\n", fname);
+    SDL_PauseAudio(0);  /* SDL audio device is "paused" right after opening. */
+
+    global_done_flag = 0;  /* the audio callback will flip this flag. */
+    while (!global_done_flag)
+        SDL_Delay(10);  /* just wait for the audio callback to finish. */
+
+    /* at this point, we've played the entire audio file. */
+    SDL_PauseAudio(1);  /* so stop the device. */
+
+    /*
+     * Sleep two buffers' worth of audio before closing, in order
+     *  to allow the playback to finish. This isn't always enough;
+     *   perhaps SDL needs a way to explicitly wait for device drain?
+     * Most apps don't have this issue, since they aren't explicitly
+     *  closing the device as soon as a sound file is done playback.
+     * As an alternative for this app, you could also change the callback
+     *  to write silence for a call or two before flipping global_done_flag.
+     */
+    SDL_Delay(2 * 1000 * data.devformat.samples / data.devformat.freq);
+
+    /* if there was an error, tell the user. */
+    if (data.sample->flags & SOUND_SAMPLEFLAG_ERROR)
+        fprintf(stderr, "Error decoding file: %s\n", Sound_GetError());
+
+    Sound_FreeSample(data.sample);  /* clean up SDL_Sound resources... */
+    SDL_CloseAudio();  /* will reopen with next file's format. */
+} /* playOneSoundFile */
+
+
+int main(int argc, char **argv)
+{
+    int i;
+
+    if (!Sound_Init())  /* this calls SDL_Init(SDL_INIT_AUDIO) ... */
+    {
+        fprintf(stderr, "Sound_Init() failed: %s.\n", Sound_GetError());
+        SDL_Quit();
+        return(42);
+    } /* if */
+
+    for (i = 1; i < argc; i++)  /* each arg is an audio file to play. */
+        playOneSoundFile(argv[i]);
+
+    /* Shutdown the libraries... */
+    Sound_Quit();
+    SDL_Quit();
+    return(0);
+} /* main */
+
+/* end of playsound-simple.c ... */
+