Simplify clearing an array
[openal-soft.git] / examples / alstream.c
blob69be236aafc2fb185bc0121b8fec6984be8be194
1 /*
2 * OpenAL Audio Stream Example
4 * Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 /* This file contains a relatively simple streaming audio player. */
27 #include <assert.h>
28 #include <inttypes.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include "sndfile.h"
35 #include "AL/al.h"
37 #include "common/alhelpers.h"
40 /* Define the number of buffers and buffer size (in milliseconds) to use. 4
41 * buffers with 8192 samples each gives a nice per-chunk size, and lets the
42 * queue last for almost one second at 44.1khz. */
43 #define NUM_BUFFERS 4
44 #define BUFFER_SAMPLES 8192
46 typedef struct StreamPlayer {
47 /* These are the buffers and source to play out through OpenAL with */
48 ALuint buffers[NUM_BUFFERS];
49 ALuint source;
51 /* Handle for the audio file */
52 SNDFILE *sndfile;
53 SF_INFO sfinfo;
54 short *membuf;
56 /* The format of the output stream (sample rate is in sfinfo) */
57 ALenum format;
58 } StreamPlayer;
60 static StreamPlayer *NewPlayer(void);
61 static void DeletePlayer(StreamPlayer *player);
62 static int OpenPlayerFile(StreamPlayer *player, const char *filename);
63 static void ClosePlayerFile(StreamPlayer *player);
64 static int StartPlayer(StreamPlayer *player);
65 static int UpdatePlayer(StreamPlayer *player);
67 /* Creates a new player object, and allocates the needed OpenAL source and
68 * buffer objects. Error checking is simplified for the purposes of this
69 * example, and will cause an abort if needed. */
70 static StreamPlayer *NewPlayer(void)
72 StreamPlayer *player;
74 player = calloc(1, sizeof(*player));
75 assert(player != NULL);
77 /* Generate the buffers and source */
78 alGenBuffers(NUM_BUFFERS, player->buffers);
79 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
81 alGenSources(1, &player->source);
82 assert(alGetError() == AL_NO_ERROR && "Could not create source");
84 /* Set parameters so mono sources play out the front-center speaker and
85 * won't distance attenuate. */
86 alSource3i(player->source, AL_POSITION, 0, 0, -1);
87 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
88 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
89 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
91 return player;
94 /* Destroys a player object, deleting the source and buffers. No error handling
95 * since these calls shouldn't fail with a properly-made player object. */
96 static void DeletePlayer(StreamPlayer *player)
98 ClosePlayerFile(player);
100 alDeleteSources(1, &player->source);
101 alDeleteBuffers(NUM_BUFFERS, player->buffers);
102 if(alGetError() != AL_NO_ERROR)
103 fprintf(stderr, "Failed to delete object IDs\n");
105 memset(player, 0, sizeof(*player));
106 free(player);
110 /* Opens the first audio stream of the named file. If a file is already open,
111 * it will be closed first. */
112 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
114 size_t frame_size;
116 ClosePlayerFile(player);
118 /* Open the audio file and check that it's usable. */
119 player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo);
120 if(!player->sndfile)
122 fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL));
123 return 0;
126 /* Get the sound format, and figure out the OpenAL format */
127 if(player->sfinfo.channels == 1)
128 player->format = AL_FORMAT_MONO16;
129 else if(player->sfinfo.channels == 2)
130 player->format = AL_FORMAT_STEREO16;
131 else
133 fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels);
134 sf_close(player->sndfile);
135 player->sndfile = NULL;
136 return 0;
139 frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(short);
140 player->membuf = malloc(frame_size);
142 return 1;
145 /* Closes the audio file stream */
146 static void ClosePlayerFile(StreamPlayer *player)
148 if(player->sndfile)
149 sf_close(player->sndfile);
150 player->sndfile = NULL;
152 free(player->membuf);
153 player->membuf = NULL;
157 /* Prebuffers some audio from the file, and starts playing the source */
158 static int StartPlayer(StreamPlayer *player)
160 ALsizei i;
162 /* Rewind the source position and clear the buffer queue */
163 alSourceRewind(player->source);
164 alSourcei(player->source, AL_BUFFER, 0);
166 /* Fill the buffer queue */
167 for(i = 0;i < NUM_BUFFERS;i++)
169 /* Get some data to give it to the buffer */
170 sf_count_t slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
171 if(slen < 1) break;
173 slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
174 alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
175 player->sfinfo.samplerate);
177 if(alGetError() != AL_NO_ERROR)
179 fprintf(stderr, "Error buffering for playback\n");
180 return 0;
183 /* Now queue and start playback! */
184 alSourceQueueBuffers(player->source, i, player->buffers);
185 alSourcePlay(player->source);
186 if(alGetError() != AL_NO_ERROR)
188 fprintf(stderr, "Error starting playback\n");
189 return 0;
192 return 1;
195 static int UpdatePlayer(StreamPlayer *player)
197 ALint processed, state;
199 /* Get relevant source info */
200 alGetSourcei(player->source, AL_SOURCE_STATE, &state);
201 alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
202 if(alGetError() != AL_NO_ERROR)
204 fprintf(stderr, "Error checking source state\n");
205 return 0;
208 /* Unqueue and handle each processed buffer */
209 while(processed > 0)
211 ALuint bufid;
212 sf_count_t slen;
214 alSourceUnqueueBuffers(player->source, 1, &bufid);
215 processed--;
217 /* Read the next chunk of data, refill the buffer, and queue it
218 * back on the source */
219 slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
220 if(slen > 0)
222 slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
223 alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
224 player->sfinfo.samplerate);
225 alSourceQueueBuffers(player->source, 1, &bufid);
227 if(alGetError() != AL_NO_ERROR)
229 fprintf(stderr, "Error buffering data\n");
230 return 0;
234 /* Make sure the source hasn't underrun */
235 if(state != AL_PLAYING && state != AL_PAUSED)
237 ALint queued;
239 /* If no buffers are queued, playback is finished */
240 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
241 if(queued == 0)
242 return 0;
244 alSourcePlay(player->source);
245 if(alGetError() != AL_NO_ERROR)
247 fprintf(stderr, "Error restarting playback\n");
248 return 0;
252 return 1;
256 int main(int argc, char **argv)
258 StreamPlayer *player;
259 int i;
261 /* Print out usage if no arguments were specified */
262 if(argc < 2)
264 fprintf(stderr, "Usage: %s [-device <name>] <filenames...>\n", argv[0]);
265 return 1;
268 argv++; argc--;
269 if(InitAL(&argv, &argc) != 0)
270 return 1;
272 player = NewPlayer();
274 /* Play each file listed on the command line */
275 for(i = 0;i < argc;i++)
277 const char *namepart;
279 if(!OpenPlayerFile(player, argv[i]))
280 continue;
282 /* Get the name portion, without the path, for display. */
283 namepart = strrchr(argv[i], '/');
284 if(namepart || (namepart=strrchr(argv[i], '\\')))
285 namepart++;
286 else
287 namepart = argv[i];
289 printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
290 player->sfinfo.samplerate);
291 fflush(stdout);
293 if(!StartPlayer(player))
295 ClosePlayerFile(player);
296 continue;
299 while(UpdatePlayer(player))
300 al_nssleep(10000000);
302 /* All done with this file. Close it and go to the next */
303 ClosePlayerFile(player);
305 printf("Done.\n");
307 /* All files done. Delete the player, and close down OpenAL */
308 DeletePlayer(player);
309 player = NULL;
311 CloseAL();
313 return 0;