Use a specific lock for the backend's stop/reset/play calls
[openal-soft.git] / examples / alstream.c
blob63478d6ab6f0746d7858aa3d104f9088fd362963
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 "common/alhelpers.h"
38 #include "common/sdl_sound.h"
41 static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
42 static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT;
45 /* Define the number of buffers and buffer size (in milliseconds) to use. 4
46 * buffers with 200ms each gives a nice per-chunk size, and lets the queue last
47 * for almost one second. */
48 #define NUM_BUFFERS 4
49 #define BUFFER_TIME_MS 200
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 /* Handle for the audio file */
57 FilePtr file;
59 /* The format of the output stream */
60 ALenum format;
61 ALenum channels;
62 ALenum type;
63 ALuint rate;
64 } StreamPlayer;
66 static StreamPlayer *NewPlayer(void);
67 static void DeletePlayer(StreamPlayer *player);
68 static int OpenPlayerFile(StreamPlayer *player, const char *filename);
69 static void ClosePlayerFile(StreamPlayer *player);
70 static int StartPlayer(StreamPlayer *player);
71 static int UpdatePlayer(StreamPlayer *player);
73 /* Creates a new player object, and allocates the needed OpenAL source and
74 * buffer objects. Error checking is simplified for the purposes of this
75 * example, and will cause an abort if needed. */
76 static StreamPlayer *NewPlayer(void)
78 StreamPlayer *player;
80 player = malloc(sizeof(*player));
81 assert(player != NULL);
83 memset(player, 0, sizeof(*player));
85 /* Generate the buffers and source */
86 alGenBuffers(NUM_BUFFERS, player->buffers);
87 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
89 alGenSources(1, &player->source);
90 assert(alGetError() == AL_NO_ERROR && "Could not create source");
92 /* Set parameters so mono sources play out the front-center speaker and
93 * won't distance attenuate. */
94 alSource3i(player->source, AL_POSITION, 0, 0, -1);
95 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
96 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
97 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
99 return player;
102 /* Destroys a player object, deleting the source and buffers. No error handling
103 * since these calls shouldn't fail with a properly-made player object. */
104 static void DeletePlayer(StreamPlayer *player)
106 ClosePlayerFile(player);
108 alDeleteSources(1, &player->source);
109 alDeleteBuffers(NUM_BUFFERS, player->buffers);
110 if(alGetError() != AL_NO_ERROR)
111 fprintf(stderr, "Failed to delete object IDs\n");
113 memset(player, 0, sizeof(*player));
114 free(player);
118 /* Opens the first audio stream of the named file. If a file is already open,
119 * it will be closed first. */
120 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
122 ClosePlayerFile(player);
124 /* Open the file and get the first stream from it */
125 player->file = openAudioFile(filename, BUFFER_TIME_MS);
126 if(!player->file)
128 fprintf(stderr, "Could not open audio in %s\n", filename);
129 goto error;
132 /* Get the stream format, and figure out the OpenAL format */
133 if(getAudioInfo(player->file, &player->rate, &player->channels, &player->type) != 0)
135 fprintf(stderr, "Error getting audio info for %s\n", filename);
136 goto error;
139 player->format = GetFormat(player->channels, player->type, alIsBufferFormatSupportedSOFT);
140 if(player->format == 0)
142 fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
143 ChannelsName(player->channels), TypeName(player->type),
144 filename);
145 goto error;
148 return 1;
150 error:
151 closeAudioFile(player->file);
152 player->file = NULL;
154 return 0;
157 /* Closes the audio file stream */
158 static void ClosePlayerFile(StreamPlayer *player)
160 closeAudioFile(player->file);
161 player->file = NULL;
165 /* Prebuffers some audio from the file, and starts playing the source */
166 static int StartPlayer(StreamPlayer *player)
168 size_t i;
170 /* Rewind the source position and clear the buffer queue */
171 alSourceRewind(player->source);
172 alSourcei(player->source, AL_BUFFER, 0);
174 /* Fill the buffer queue */
175 for(i = 0;i < NUM_BUFFERS;i++)
177 uint8_t *data;
178 size_t got;
180 /* Get some data to give it to the buffer */
181 data = getAudioData(player->file, &got);
182 if(!data) break;
184 alBufferSamplesSOFT(player->buffers[i], player->rate, player->format,
185 BytesToFrames(got, player->channels, player->type),
186 player->channels, player->type, data);
188 if(alGetError() != AL_NO_ERROR)
190 fprintf(stderr, "Error buffering for playback\n");
191 return 0;
194 /* Now queue and start playback! */
195 alSourceQueueBuffers(player->source, i, player->buffers);
196 alSourcePlay(player->source);
197 if(alGetError() != AL_NO_ERROR)
199 fprintf(stderr, "Error starting playback\n");
200 return 0;
203 return 1;
206 static int UpdatePlayer(StreamPlayer *player)
208 ALint processed, state;
210 /* Get relevant source info */
211 alGetSourcei(player->source, AL_SOURCE_STATE, &state);
212 alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
213 if(alGetError() != AL_NO_ERROR)
215 fprintf(stderr, "Error checking source state\n");
216 return 0;
219 /* Unqueue and handle each processed buffer */
220 while(processed > 0)
222 ALuint bufid;
223 uint8_t *data;
224 size_t got;
226 alSourceUnqueueBuffers(player->source, 1, &bufid);
227 processed--;
229 /* Read the next chunk of data, refill the buffer, and queue it
230 * back on the source */
231 data = getAudioData(player->file, &got);
232 if(data != NULL)
234 alBufferSamplesSOFT(bufid, player->rate, player->format,
235 BytesToFrames(got, player->channels, player->type),
236 player->channels, player->type, data);
237 alSourceQueueBuffers(player->source, 1, &bufid);
239 if(alGetError() != AL_NO_ERROR)
241 fprintf(stderr, "Error buffering data\n");
242 return 0;
246 /* Make sure the source hasn't underrun */
247 if(state != AL_PLAYING && state != AL_PAUSED)
249 ALint queued;
251 /* If no buffers are queued, playback is finished */
252 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
253 if(queued == 0)
254 return 0;
256 alSourcePlay(player->source);
257 if(alGetError() != AL_NO_ERROR)
259 fprintf(stderr, "Error restarting playback\n");
260 return 0;
264 return 1;
268 int main(int argc, char **argv)
270 StreamPlayer *player;
271 int i;
273 /* Print out usage if no file was specified */
274 if(argc < 2)
276 fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]);
277 return 1;
280 if(InitAL() != 0)
281 return 1;
283 if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
285 printf("AL_SOFT_buffer_samples supported!\n");
286 alBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT");
287 alIsBufferFormatSupportedSOFT = alGetProcAddress("alIsBufferFormatSupportedSOFT");
289 else
290 printf("AL_SOFT_buffer_samples not supported\n");
292 player = NewPlayer();
294 /* Play each file listed on the command line */
295 for(i = 1;i < argc;i++)
297 const char *namepart;
299 if(!OpenPlayerFile(player, argv[i]))
300 continue;
302 /* Get the name portion, without the path, for display. */
303 namepart = strrchr(argv[i], '/');
304 if(namepart || (namepart=strrchr(argv[i], '\\')))
305 namepart++;
306 else
307 namepart = argv[i];
309 printf("Playing: %s (%s, %s, %dhz)\n", namepart,
310 TypeName(player->type), ChannelsName(player->channels),
311 player->rate);
312 fflush(stdout);
314 if(!StartPlayer(player))
316 ClosePlayerFile(player);
317 continue;
320 while(UpdatePlayer(player))
321 al_nssleep(10000000);
323 /* All done with this file. Close it and go to the next */
324 ClosePlayerFile(player);
326 printf("Done.\n");
328 /* All files done. Delete the player, and close OpenAL */
329 DeletePlayer(player);
330 player = NULL;
332 CloseAL();
334 return 0;