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. */
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. */
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
];
51 /* Handle for the audio file */
56 /* The format of the output stream (sample rate is in sfinfo) */
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)
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");
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
));
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
)
116 ClosePlayerFile(player
);
118 /* Open the audio file and check that it's usable. */
119 player
->sndfile
= sf_open(filename
, SFM_READ
, &player
->sfinfo
);
122 fprintf(stderr
, "Could not open audio in %s: %s\n", filename
, sf_strerror(NULL
));
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
;
133 fprintf(stderr
, "Unsupported channel count: %d\n", player
->sfinfo
.channels
);
134 sf_close(player
->sndfile
);
135 player
->sndfile
= NULL
;
139 frame_size
= (size_t)(BUFFER_SAMPLES
* player
->sfinfo
.channels
) * sizeof(short);
140 player
->membuf
= malloc(frame_size
);
145 /* Closes the audio file stream */
146 static void ClosePlayerFile(StreamPlayer
*player
)
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
)
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
);
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");
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");
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");
208 /* Unqueue and handle each processed buffer */
214 alSourceUnqueueBuffers(player
->source
, 1, &bufid
);
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
);
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");
234 /* Make sure the source hasn't underrun */
235 if(state
!= AL_PLAYING
&& state
!= AL_PAUSED
)
239 /* If no buffers are queued, playback is finished */
240 alGetSourcei(player
->source
, AL_BUFFERS_QUEUED
, &queued
);
244 alSourcePlay(player
->source
);
245 if(alGetError() != AL_NO_ERROR
)
247 fprintf(stderr
, "Error restarting playback\n");
256 int main(int argc
, char **argv
)
258 StreamPlayer
*player
;
261 /* Print out usage if no arguments were specified */
264 fprintf(stderr
, "Usage: %s [-device <name>] <filenames...>\n", argv
[0]);
269 if(InitAL(&argv
, &argc
) != 0)
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
]))
282 /* Get the name portion, without the path, for display. */
283 namepart
= strrchr(argv
[i
], '/');
284 if(namepart
|| (namepart
=strrchr(argv
[i
], '\\')))
289 printf("Playing: %s (%s, %dhz)\n", namepart
, FormatName(player
->format
),
290 player
->sfinfo
.samplerate
);
293 if(!StartPlayer(player
))
295 ClosePlayerFile(player
);
299 while(UpdatePlayer(player
))
300 al_nssleep(10000000);
302 /* All done with this file. Close it and go to the next */
303 ClosePlayerFile(player
);
307 /* All files done. Delete the player, and close down OpenAL */
308 DeletePlayer(player
);