Release 1.14
[openal-soft.git] / examples / alstream.c
blob183d54f2d1bf41f7d603c8622f87752ba119ce5b
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 <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <signal.h>
31 #include <assert.h>
33 #include "AL/al.h"
34 #include "AL/alc.h"
35 #include "AL/alext.h"
37 #include "alhelpers.h"
38 #include "alffmpeg.h"
41 LPALBUFFERSAMPLESSOFT palBufferSamplesSOFT = wrap_BufferSamples;
42 LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT = NULL;
45 /* Define the number of buffers and buffer size (in samples) to use. 4 buffers
46 * with 8192 samples each gives a nice per-chunk size, and lets the queue last
47 * for almost 3/4ths of a second for a 44.1khz stream. */
48 #define NUM_BUFFERS 4
49 #define BUFFER_SIZE 8192
51 typedef struct StreamPlayer {
52 /* These are the buffers and source to play out through OpenAL with */
53 ALuint buffers[NUM_BUFFERS];
54 ALuint source;
56 /* Handles for the audio stream */
57 FilePtr file;
58 StreamPtr stream;
60 /* A temporary data buffer for readAVAudioData to write to and pass to
61 * OpenAL with */
62 ALbyte *data;
63 ALsizei datasize;
65 /* The format of the output stream */
66 ALenum format;
67 ALenum channels;
68 ALenum type;
69 ALuint rate;
70 } StreamPlayer;
72 static StreamPlayer *NewPlayer(void);
73 static void DeletePlayer(StreamPlayer *player);
74 static int OpenPlayerFile(StreamPlayer *player, const char *filename);
75 static void ClosePlayerFile(StreamPlayer *player);
76 static int StartPlayer(StreamPlayer *player);
77 static int UpdatePlayer(StreamPlayer *player);
79 /* Creates a new player object, and allocates the needed OpenAL source and
80 * buffer objects. Error checking is simplified for the purposes of this
81 * example, and will cause an abort if needed. */
82 static StreamPlayer *NewPlayer(void)
84 StreamPlayer *player;
86 player = malloc(sizeof(*player));
87 assert(player != NULL);
89 memset(player, 0, sizeof(*player));
91 /* Generate the buffers and source */
92 alGenBuffers(NUM_BUFFERS, player->buffers);
93 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
95 alGenSources(1, &player->source);
96 assert(alGetError() == AL_NO_ERROR && "Could not create source");
98 /* Set parameters so mono sources play out the front-center speaker and
99 * won't distance attenuate. */
100 alSource3i(player->source, AL_POSITION, 0, 0, -1);
101 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
102 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
103 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
105 return player;
108 /* Destroys a player object, deleting the source and buffers. No error handling
109 * since these calls shouldn't fail with a properly-made player object. */
110 static void DeletePlayer(StreamPlayer *player)
112 ClosePlayerFile(player);
114 alDeleteSources(1, &player->source);
115 alDeleteBuffers(NUM_BUFFERS, player->buffers);
116 if(alGetError() != AL_NO_ERROR)
117 fprintf(stderr, "Failed to delete object IDs\n");
119 memset(player, 0, sizeof(*player));
120 free(player);
124 /* Opens the first audio stream of the named file. If a file is already open,
125 * it will be closed first. */
126 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
128 ClosePlayerFile(player);
130 /* Open the file and get the first stream from it */
131 player->file = openAVFile(filename);
132 player->stream = getAVAudioStream(player->file, 0);
133 if(!player->stream)
135 fprintf(stderr, "Could not open audio in %s\n", filename);
136 goto error;
139 /* Get the stream format, and figure out the OpenAL format */
140 if(getAVAudioInfo(player->stream, &player->rate, &player->channels,
141 &player->type) != 0)
143 fprintf(stderr, "Error getting audio info for %s\n", filename);
144 goto error;
147 player->format = GetFormat(player->channels, player->type, palIsBufferFormatSupportedSOFT);
148 if(player->format == 0)
150 fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
151 ChannelsName(player->channels), TypeName(player->type),
152 filename);
153 goto error;
156 /* Allocate enough space for the temp buffer, given the format */
157 player->datasize = FramesToBytes(BUFFER_SIZE, player->channels,
158 player->type);
159 player->data = malloc(player->datasize);
160 if(player->data == NULL)
162 fprintf(stderr, "Error allocating %d bytes\n", player->datasize);
163 goto error;
166 return 1;
168 error:
169 closeAVFile(player->file);
170 player->file = NULL;
171 player->stream = NULL;
172 player->datasize = 0;
174 return 0;
177 /* Closes the audio file stream */
178 static void ClosePlayerFile(StreamPlayer *player)
180 closeAVFile(player->file);
181 player->file = NULL;
182 player->stream = NULL;
184 free(player->data);
185 player->data = NULL;
186 player->datasize = 0;
190 /* Prebuffers some audio from the file, and starts playing the source */
191 static int StartPlayer(StreamPlayer *player)
193 size_t i, got;
195 /* Rewind the source position and clear the buffer queue */
196 alSourceRewind(player->source);
197 alSourcei(player->source, AL_BUFFER, 0);
199 /* Fill the buffer queue */
200 for(i = 0;i < NUM_BUFFERS;i++)
202 /* Get some data to give it to the buffer */
203 got = readAVAudioData(player->stream, player->data, player->datasize);
204 if(got == 0) break;
206 palBufferSamplesSOFT(player->buffers[i], player->rate, player->format,
207 BytesToFrames(got, player->channels, player->type),
208 player->channels, player->type, player->data);
210 if(alGetError() != AL_NO_ERROR)
212 fprintf(stderr, "Error buffering for playback\n");
213 return 0;
216 /* Now queue and start playback! */
217 alSourceQueueBuffers(player->source, i, player->buffers);
218 alSourcePlay(player->source);
219 if(alGetError() != AL_NO_ERROR)
221 fprintf(stderr, "Error starting playback\n");
222 return 0;
225 return 1;
228 static int UpdatePlayer(StreamPlayer *player)
230 ALint processed, state;
232 /* Get relevant source info */
233 alGetSourcei(player->source, AL_SOURCE_STATE, &state);
234 alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
235 if(alGetError() != AL_NO_ERROR)
237 fprintf(stderr, "Error checking source state\n");
238 return 0;
241 /* Unqueue and handle each processed buffer */
242 while(processed > 0)
244 ALuint bufid;
245 size_t got;
247 alSourceUnqueueBuffers(player->source, 1, &bufid);
248 processed--;
250 /* Read the next chunk of data, refill the buffer, and queue it
251 * back on the source */
252 got = readAVAudioData(player->stream, player->data, player->datasize);
253 if(got > 0)
255 palBufferSamplesSOFT(bufid, player->rate, player->format,
256 BytesToFrames(got, player->channels, player->type),
257 player->channels, player->type, player->data);
258 alSourceQueueBuffers(player->source, 1, &bufid);
260 if(alGetError() != AL_NO_ERROR)
262 fprintf(stderr, "Error buffering data\n");
263 return 0;
267 /* Make sure the source hasn't underrun */
268 if(state != AL_PLAYING && state != AL_PAUSED)
270 ALint queued;
272 /* If no buffers are queued, playback is finished */
273 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
274 if(queued == 0)
275 return 0;
277 alSourcePlay(player->source);
278 if(alGetError() != AL_NO_ERROR)
280 fprintf(stderr, "Error restarting playback\n");
281 return 0;
285 return 1;
289 int main(int argc, char **argv)
291 StreamPlayer *player;
292 int i;
294 /* Print out usage if no file was specified */
295 if(argc < 2)
297 fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]);
298 return 1;
301 if(InitAL() != 0)
302 return 1;
304 if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
306 printf("AL_SOFT_buffer_samples supported!\n");
307 palBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT");
308 palIsBufferFormatSupportedSOFT = alGetProcAddress("alIsBufferFormatSupportedSOFT");
310 else
311 printf("AL_SOFT_buffer_samples not supported\n");
313 player = NewPlayer();
315 /* Play each file listed on the command line */
316 for(i = 1;i < argc;i++)
318 if(!OpenPlayerFile(player, argv[i]))
319 continue;
321 fprintf(stderr, "Playing %s (%s, %s, %dhz)\n", argv[i],
322 TypeName(player->type), ChannelsName(player->channels),
323 player->rate);
325 if(!StartPlayer(player))
327 ClosePlayerFile(player);
328 continue;
331 while(UpdatePlayer(player))
332 Sleep(10);
334 /* All done with this file. Close it and go to the next */
335 ClosePlayerFile(player);
337 fprintf(stderr, "Done.\n");
339 /* All files done. Delete the player, and close OpenAL */
340 DeletePlayer(player);
341 player = NULL;
343 CloseAL();
345 return 0;