Commit | Line | Data |
---|---|---|
21c4e167 H |
1 | /* |
2 | * SDL_sound -- An abstract sound format decoding API. | |
3 | * Copyright (C) 2001 Ryan C. Gordon. | |
4 | * | |
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. | |
9 | * | |
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. | |
14 | * | |
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 | |
18 | */ | |
19 | ||
20 | /** | |
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. | |
23 | * | |
24 | * Please see the file COPYING in the source's root directory. | |
25 | * | |
26 | * This file written by Ryan C. Gordon. (icculus@clutteredmind.org) | |
27 | */ | |
28 | ||
29 | #include <SDL/SDL.h> | |
30 | #include <SDL/SDL_sound.h> | |
31 | ||
32 | /* global decoding state. */ | |
33 | typedef struct | |
34 | { | |
35 | Sound_Sample *sample; | |
36 | SDL_AudioSpec devformat; | |
37 | Uint8 *decoded_ptr; | |
38 | Uint32 decoded_bytes; | |
39 | } PlaysoundAudioCallbackData; | |
40 | ||
41 | /* | |
42 | * This variable is flipped to non-zero when the audio callback has | |
43 | * finished playing the whole file. | |
44 | */ | |
45 | static volatile int global_done_flag = 0; | |
46 | ||
47 | ||
48 | /* | |
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. | |
57 | */ | |
58 | static void audio_callback(void *userdata, Uint8 *stream, int len) | |
59 | { | |
60 | PlaysoundAudioCallbackData *data = (PlaysoundAudioCallbackData *) userdata; | |
61 | Sound_Sample *sample = data->sample; | |
62 | int bw = 0; /* bytes written to stream this time through the callback */ | |
63 | ||
64 | while (bw < len) | |
65 | { | |
66 | int cpysize; /* bytes to copy on this iteration of the loop. */ | |
67 | ||
68 | if (data->decoded_bytes == 0) /* need more data! */ | |
69 | { | |
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) ) | |
73 | { | |
74 | data->decoded_bytes = Sound_Decode(sample); | |
75 | data->decoded_ptr = sample->buffer; | |
76 | } /* if */ | |
77 | ||
78 | if (data->decoded_bytes == 0) | |
79 | { | |
80 | /* ...there isn't any more data to read! */ | |
81 | memset(stream + bw, '\0', len - bw); /* write silence. */ | |
82 | global_done_flag = 1; | |
83 | return; /* we're done playback, one way or another. */ | |
84 | } /* if */ | |
85 | } /* if */ | |
86 | ||
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. */ | |
91 | ||
92 | /* if it's 0, next iteration will decode more or decide we're done. */ | |
93 | if (cpysize > 0) | |
94 | { | |
95 | /* write this iteration's data to the device. */ | |
96 | memcpy(stream + bw, (Uint8 *) data->decoded_ptr, cpysize); | |
97 | ||
98 | /* update state for next iteration or callback */ | |
99 | bw += cpysize; | |
100 | data->decoded_ptr += cpysize; | |
101 | data->decoded_bytes -= cpysize; | |
102 | } /* if */ | |
103 | } /* while */ | |
104 | } /* audio_callback */ | |
105 | ||
106 | ||
107 | ||
108 | static void playOneSoundFile(const char *fname) | |
109 | { | |
110 | PlaysoundAudioCallbackData data; | |
111 | ||
112 | memset(&data, '\0', sizeof (PlaysoundAudioCallbackData)); | |
113 | data.sample = Sound_NewSampleFromFile(fname, NULL, 65536); | |
114 | if (data.sample == NULL) | |
115 | { | |
116 | fprintf(stderr, "Couldn't load '%s': %s.\n", fname, Sound_GetError()); | |
117 | return; | |
118 | } /* if */ | |
119 | ||
120 | /* | |
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. | |
128 | */ | |
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) | |
136 | { | |
137 | fprintf(stderr, "Couldn't open audio device: %s.\n", SDL_GetError()); | |
138 | Sound_FreeSample(data.sample); | |
139 | return; | |
140 | } /* if */ | |
141 | ||
142 | printf("Now playing [%s]...\n", fname); | |
143 | SDL_PauseAudio(0); /* SDL audio device is "paused" right after opening. */ | |
144 | ||
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. */ | |
148 | ||
149 | /* at this point, we've played the entire audio file. */ | |
150 | SDL_PauseAudio(1); /* so stop the device. */ | |
151 | ||
152 | /* | |
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. | |
160 | */ | |
161 | SDL_Delay(2 * 1000 * data.devformat.samples / data.devformat.freq); | |
162 | ||
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()); | |
166 | ||
167 | Sound_FreeSample(data.sample); /* clean up SDL_Sound resources... */ | |
168 | SDL_CloseAudio(); /* will reopen with next file's format. */ | |
169 | } /* playOneSoundFile */ | |
170 | ||
171 | ||
172 | int main(int argc, char **argv) | |
173 | { | |
174 | int i; | |
175 | ||
176 | if (!Sound_Init()) /* this calls SDL_Init(SDL_INIT_AUDIO) ... */ | |
177 | { | |
178 | fprintf(stderr, "Sound_Init() failed: %s.\n", Sound_GetError()); | |
179 | SDL_Quit(); | |
180 | return(42); | |
181 | } /* if */ | |
182 | ||
183 | for (i = 1; i < argc; i++) /* each arg is an audio file to play. */ | |
184 | playOneSoundFile(argv[i]); | |
185 | ||
186 | /* Shutdown the libraries... */ | |
187 | Sound_Quit(); | |
188 | SDL_Quit(); | |
189 | return(0); | |
190 | } /* main */ | |
191 | ||
192 | /* end of playsound-simple.c ... */ | |
193 |