/
playsound_simple.c
197 lines (166 loc) · 7.08 KB
/
playsound_simple.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
* 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 LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon. (icculus@icculus.org)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 ... */