Add a config option for reverse-z
[openal-soft.git] / examples / alstream.c
blob2e427a328cffc5f92f27474dc8d4c106fafd33f2
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"
36 #include "AL/alext.h"
38 #include "common/alhelpers.h"
41 /* Define the number of buffers and buffer size (in milliseconds) to use. 4
42 * buffers with 8192 samples each gives a nice per-chunk size, and lets the
43 * queue last for almost one second at 44.1khz. */
44 #define NUM_BUFFERS 4
45 #define BUFFER_SAMPLES 8192
47 typedef struct StreamPlayer {
48 /* These are the buffers and source to play out through OpenAL with */
49 ALuint buffers[NUM_BUFFERS];
50 ALuint source;
52 /* Handle for the audio file */
53 SNDFILE *sndfile;
54 SF_INFO sfinfo;
55 short *membuf;
57 /* The format of the output stream (sample rate is in sfinfo) */
58 ALenum format;
59 } StreamPlayer;
61 static StreamPlayer *NewPlayer(void);
62 static void DeletePlayer(StreamPlayer *player);
63 static int OpenPlayerFile(StreamPlayer *player, const char *filename);
64 static void ClosePlayerFile(StreamPlayer *player);
65 static int StartPlayer(StreamPlayer *player);
66 static int UpdatePlayer(StreamPlayer *player);
68 /* Creates a new player object, and allocates the needed OpenAL source and
69 * buffer objects. Error checking is simplified for the purposes of this
70 * example, and will cause an abort if needed. */
71 static StreamPlayer *NewPlayer(void)
73 StreamPlayer *player;
75 player = calloc(1, sizeof(*player));
76 assert(player != NULL);
78 /* Generate the buffers and source */
79 alGenBuffers(NUM_BUFFERS, player->buffers);
80 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
82 alGenSources(1, &player->source);
83 assert(alGetError() == AL_NO_ERROR && "Could not create source");
85 /* Set parameters so mono sources play out the front-center speaker and
86 * won't distance attenuate. */
87 alSource3i(player->source, AL_POSITION, 0, 0, -1);
88 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
89 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
90 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
92 return player;
95 /* Destroys a player object, deleting the source and buffers. No error handling
96 * since these calls shouldn't fail with a properly-made player object. */
97 static void DeletePlayer(StreamPlayer *player)
99 ClosePlayerFile(player);
101 alDeleteSources(1, &player->source);
102 alDeleteBuffers(NUM_BUFFERS, player->buffers);
103 if(alGetError() != AL_NO_ERROR)
104 fprintf(stderr, "Failed to delete object IDs\n");
106 memset(player, 0, sizeof(*player));
107 free(player);
111 /* Opens the first audio stream of the named file. If a file is already open,
112 * it will be closed first. */
113 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
115 size_t frame_size;
117 ClosePlayerFile(player);
119 /* Open the audio file and check that it's usable. */
120 player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo);
121 if(!player->sndfile)
123 fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL));
124 return 0;
127 /* Get the sound format, and figure out the OpenAL format */
128 if(player->sfinfo.channels == 1)
129 player->format = AL_FORMAT_MONO16;
130 else if(player->sfinfo.channels == 2)
131 player->format = AL_FORMAT_STEREO16;
132 else if(player->sfinfo.channels == 3)
134 if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
135 player->format = AL_FORMAT_BFORMAT2D_16;
137 else if(player->sfinfo.channels == 4)
139 if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
140 player->format = AL_FORMAT_BFORMAT3D_16;
142 if(!player->format)
144 fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels);
145 sf_close(player->sndfile);
146 player->sndfile = NULL;
147 return 0;
150 frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(short);
151 player->membuf = malloc(frame_size);
153 return 1;
156 /* Closes the audio file stream */
157 static void ClosePlayerFile(StreamPlayer *player)
159 if(player->sndfile)
160 sf_close(player->sndfile);
161 player->sndfile = NULL;
163 free(player->membuf);
164 player->membuf = NULL;
168 /* Prebuffers some audio from the file, and starts playing the source */
169 static int StartPlayer(StreamPlayer *player)
171 ALsizei i;
173 /* Rewind the source position and clear the buffer queue */
174 alSourceRewind(player->source);
175 alSourcei(player->source, AL_BUFFER, 0);
177 /* Fill the buffer queue */
178 for(i = 0;i < NUM_BUFFERS;i++)
180 /* Get some data to give it to the buffer */
181 sf_count_t slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
182 if(slen < 1) break;
184 slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
185 alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
186 player->sfinfo.samplerate);
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 sf_count_t slen;
225 alSourceUnqueueBuffers(player->source, 1, &bufid);
226 processed--;
228 /* Read the next chunk of data, refill the buffer, and queue it
229 * back on the source */
230 slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
231 if(slen > 0)
233 slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
234 alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
235 player->sfinfo.samplerate);
236 alSourceQueueBuffers(player->source, 1, &bufid);
238 if(alGetError() != AL_NO_ERROR)
240 fprintf(stderr, "Error buffering data\n");
241 return 0;
245 /* Make sure the source hasn't underrun */
246 if(state != AL_PLAYING && state != AL_PAUSED)
248 ALint queued;
250 /* If no buffers are queued, playback is finished */
251 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
252 if(queued == 0)
253 return 0;
255 alSourcePlay(player->source);
256 if(alGetError() != AL_NO_ERROR)
258 fprintf(stderr, "Error restarting playback\n");
259 return 0;
263 return 1;
267 int main(int argc, char **argv)
269 StreamPlayer *player;
270 int i;
272 /* Print out usage if no arguments were specified */
273 if(argc < 2)
275 fprintf(stderr, "Usage: %s [-device <name>] <filenames...>\n", argv[0]);
276 return 1;
279 argv++; argc--;
280 if(InitAL(&argv, &argc) != 0)
281 return 1;
283 player = NewPlayer();
285 /* Play each file listed on the command line */
286 for(i = 0;i < argc;i++)
288 const char *namepart;
290 if(!OpenPlayerFile(player, argv[i]))
291 continue;
293 /* Get the name portion, without the path, for display. */
294 namepart = strrchr(argv[i], '/');
295 if(namepart || (namepart=strrchr(argv[i], '\\')))
296 namepart++;
297 else
298 namepart = argv[i];
300 printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
301 player->sfinfo.samplerate);
302 fflush(stdout);
304 if(!StartPlayer(player))
306 ClosePlayerFile(player);
307 continue;
310 while(UpdatePlayer(player))
311 al_nssleep(10000000);
313 /* All done with this file. Close it and go to the next */
314 ClosePlayerFile(player);
316 printf("Done.\n");
318 /* All files done. Delete the player, and close down OpenAL */
319 DeletePlayer(player);
320 player = NULL;
322 CloseAL();
324 return 0;