2 * SDL_sound -- An abstract sound format decoding API.
3 * Copyright (C) 2001 Ryan C. Gordon.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * This is just a simple "decode sound, play it through SDL" example.
22 * The much more complex, fancy, and robust code is playsound.c.
24 * Please see the file COPYING in the source's root directory.
26 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
30 #include <SDL/SDL_sound.h>
32 /* global decoding state. */
36 SDL_AudioSpec devformat
;
39 } PlaysoundAudioCallbackData
;
42 * This variable is flipped to non-zero when the audio callback has
43 * finished playing the whole file.
45 static volatile int global_done_flag
= 0;
49 * The audio callback. SDL calls this frequently to feed the audio device.
50 * We decode the audio file being played in here in small chunks and feed
51 * the device as necessary. Other solutions may want to predecode more
52 * (or all) of the file, since this needs to run fast and frequently,
53 * but since we're only sitting here and waiting for the file to play,
54 * the only real requirement is that we can decode a given audio file
55 * faster than realtime, which isn't really a problem with any modern format
56 * on even pretty old hardware at this point.
58 static void audio_callback(void *userdata
, Uint8
*stream
, int len
)
60 PlaysoundAudioCallbackData
*data
= (PlaysoundAudioCallbackData
*) userdata
;
61 Sound_Sample
*sample
= data
->sample
;
62 int bw
= 0; /* bytes written to stream this time through the callback */
66 int cpysize
; /* bytes to copy on this iteration of the loop. */
68 if (data
->decoded_bytes
== 0) /* need more data! */
70 /* if there wasn't previously an error or EOF, read more. */
71 if ( ((sample
->flags
& SOUND_SAMPLEFLAG_ERROR
) == 0) &&
72 ((sample
->flags
& SOUND_SAMPLEFLAG_EOF
) == 0) )
74 data
->decoded_bytes
= Sound_Decode(sample
);
75 data
->decoded_ptr
= sample
->buffer
;
78 if (data
->decoded_bytes
== 0)
80 /* ...there isn't any more data to read! */
81 memset(stream
+ bw
, '\0', len
- bw
); /* write silence. */
83 return; /* we're done playback, one way or another. */
87 /* we have data decoded and ready to write to the device... */
88 cpysize
= len
- bw
; /* len - bw == amount device still wants. */
89 if (cpysize
> data
->decoded_bytes
)
90 cpysize
= data
->decoded_bytes
; /* clamp to what we have left. */
92 /* if it's 0, next iteration will decode more or decide we're done. */
95 /* write this iteration's data to the device. */
96 memcpy(stream
+ bw
, (Uint8
*) data
->decoded_ptr
, cpysize
);
98 /* update state for next iteration or callback */
100 data
->decoded_ptr
+= cpysize
;
101 data
->decoded_bytes
-= cpysize
;
104 } /* audio_callback */
108 static void playOneSoundFile(const char *fname
)
110 PlaysoundAudioCallbackData data
;
112 memset(&data
, '\0', sizeof (PlaysoundAudioCallbackData
));
113 data
.sample
= Sound_NewSampleFromFile(fname
, NULL
, 65536);
114 if (data
.sample
== NULL
)
116 fprintf(stderr
, "Couldn't load '%s': %s.\n", fname
, Sound_GetError());
121 * Open device in format of the the sound to be played.
122 * We open and close the device for each sound file, so that SDL
123 * handles the data conversion to hardware format; this is the
124 * easy way out, but isn't practical for most apps. Usually you'll
125 * want to pick one format for all the data or one format for the
126 * audio device and convert the data when needed. This is a more
127 * complex issue than I can describe in a source code comment, though.
129 data
.devformat
.freq
= data
.sample
->actual
.rate
;
130 data
.devformat
.format
= data
.sample
->actual
.format
;
131 data
.devformat
.channels
= data
.sample
->actual
.channels
;
132 data
.devformat
.samples
= 4096; /* I just picked a largish number here. */
133 data
.devformat
.callback
= audio_callback
;
134 data
.devformat
.userdata
= &data
;
135 if (SDL_OpenAudio(&data
.devformat
, NULL
) < 0)
137 fprintf(stderr
, "Couldn't open audio device: %s.\n", SDL_GetError());
138 Sound_FreeSample(data
.sample
);
142 printf("Now playing [%s]...\n", fname
);
143 SDL_PauseAudio(0); /* SDL audio device is "paused" right after opening. */
145 global_done_flag
= 0; /* the audio callback will flip this flag. */
146 while (!global_done_flag
)
147 SDL_Delay(10); /* just wait for the audio callback to finish. */
149 /* at this point, we've played the entire audio file. */
150 SDL_PauseAudio(1); /* so stop the device. */
153 * Sleep two buffers' worth of audio before closing, in order
154 * to allow the playback to finish. This isn't always enough;
155 * perhaps SDL needs a way to explicitly wait for device drain?
156 * Most apps don't have this issue, since they aren't explicitly
157 * closing the device as soon as a sound file is done playback.
158 * As an alternative for this app, you could also change the callback
159 * to write silence for a call or two before flipping global_done_flag.
161 SDL_Delay(2 * 1000 * data
.devformat
.samples
/ data
.devformat
.freq
);
163 /* if there was an error, tell the user. */
164 if (data
.sample
->flags
& SOUND_SAMPLEFLAG_ERROR
)
165 fprintf(stderr
, "Error decoding file: %s\n", Sound_GetError());
167 Sound_FreeSample(data
.sample
); /* clean up SDL_Sound resources... */
168 SDL_CloseAudio(); /* will reopen with next file's format. */
169 } /* playOneSoundFile */
172 int main(int argc
, char **argv
)
176 if (!Sound_Init()) /* this calls SDL_Init(SDL_INIT_AUDIO) ... */
178 fprintf(stderr
, "Sound_Init() failed: %s.\n", Sound_GetError());
183 for (i
= 1; i
< argc
; i
++) /* each arg is an audio file to play. */
184 playOneSoundFile(argv
[i
]);
186 /* Shutdown the libraries... */
192 /* end of playsound-simple.c ... */