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
25 /* This file contains a relatively simple streaming audio player. */
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. */
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
];
52 /* Handle for the audio file */
57 /* The format of the output stream (sample rate is in sfinfo) */
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)
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");
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
));
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
)
117 ClosePlayerFile(player
);
119 /* Open the audio file and check that it's usable. */
120 player
->sndfile
= sf_open(filename
, SFM_READ
, &player
->sfinfo
);
123 fprintf(stderr
, "Could not open audio in %s: %s\n", filename
, sf_strerror(NULL
));
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
;
144 fprintf(stderr
, "Unsupported channel count: %d\n", player
->sfinfo
.channels
);
145 sf_close(player
->sndfile
);
146 player
->sndfile
= NULL
;
150 frame_size
= (size_t)(BUFFER_SAMPLES
* player
->sfinfo
.channels
) * sizeof(short);
151 player
->membuf
= malloc(frame_size
);
156 /* Closes the audio file stream */
157 static void ClosePlayerFile(StreamPlayer
*player
)
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
)
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
);
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");
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");
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");
219 /* Unqueue and handle each processed buffer */
225 alSourceUnqueueBuffers(player
->source
, 1, &bufid
);
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
);
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");
245 /* Make sure the source hasn't underrun */
246 if(state
!= AL_PLAYING
&& state
!= AL_PAUSED
)
250 /* If no buffers are queued, playback is finished */
251 alGetSourcei(player
->source
, AL_BUFFERS_QUEUED
, &queued
);
255 alSourcePlay(player
->source
);
256 if(alGetError() != AL_NO_ERROR
)
258 fprintf(stderr
, "Error restarting playback\n");
267 int main(int argc
, char **argv
)
269 StreamPlayer
*player
;
272 /* Print out usage if no arguments were specified */
275 fprintf(stderr
, "Usage: %s [-device <name>] <filenames...>\n", argv
[0]);
280 if(InitAL(&argv
, &argc
) != 0)
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
]))
293 /* Get the name portion, without the path, for display. */
294 namepart
= strrchr(argv
[i
], '/');
295 if(namepart
|| (namepart
=strrchr(argv
[i
], '\\')))
300 printf("Playing: %s (%s, %dhz)\n", namepart
, FormatName(player
->format
),
301 player
->sfinfo
.samplerate
);
304 if(!StartPlayer(player
))
306 ClosePlayerFile(player
);
310 while(UpdatePlayer(player
))
311 al_nssleep(10000000);
313 /* All done with this file. Close it and go to the next */
314 ClosePlayerFile(player
);
318 /* All files done. Delete the player, and close down OpenAL */
319 DeletePlayer(player
);