A non-trivial example of how to use user contexts for trivial scheduling.
[mirrors/Programs.git] / c / ghetto-sound-system / sandbox / playmus.c
... / ...
CommitLineData
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. */
33typedef 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 */
45static 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 */
58static 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
108static 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
172int 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
This page took 0.140675 seconds and 4 git commands to generate.