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 "alhelpers.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. */
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
];
56 /* Handles for the audio stream */
60 /* A temporary data buffer for readAVAudioData to write to and pass to
65 /* The format of the output stream */
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)
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");
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
));
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);
135 fprintf(stderr
, "Could not open audio in %s\n", filename
);
139 /* Get the stream format, and figure out the OpenAL format */
140 if(getAVAudioInfo(player
->stream
, &player
->rate
, &player
->channels
,
143 fprintf(stderr
, "Error getting audio info for %s\n", filename
);
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
),
156 /* Allocate enough space for the temp buffer, given the format */
157 player
->datasize
= FramesToBytes(BUFFER_SIZE
, player
->channels
,
159 player
->data
= malloc(player
->datasize
);
160 if(player
->data
== NULL
)
162 fprintf(stderr
, "Error allocating %d bytes\n", player
->datasize
);
169 closeAVFile(player
->file
);
171 player
->stream
= NULL
;
172 player
->datasize
= 0;
177 /* Closes the audio file stream */
178 static void ClosePlayerFile(StreamPlayer
*player
)
180 closeAVFile(player
->file
);
182 player
->stream
= NULL
;
186 player
->datasize
= 0;
190 /* Prebuffers some audio from the file, and starts playing the source */
191 static int StartPlayer(StreamPlayer
*player
)
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
);
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");
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");
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");
241 /* Unqueue and handle each processed buffer */
247 alSourceUnqueueBuffers(player
->source
, 1, &bufid
);
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
);
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");
267 /* Make sure the source hasn't underrun */
268 if(state
!= AL_PLAYING
&& state
!= AL_PAUSED
)
272 /* If no buffers are queued, playback is finished */
273 alGetSourcei(player
->source
, AL_BUFFERS_QUEUED
, &queued
);
277 alSourcePlay(player
->source
);
278 if(alGetError() != AL_NO_ERROR
)
280 fprintf(stderr
, "Error restarting playback\n");
289 int main(int argc
, char **argv
)
291 StreamPlayer
*player
;
294 /* Print out usage if no file was specified */
297 fprintf(stderr
, "Usage: %s <filenames...>\n", argv
[0]);
304 if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
306 printf("AL_SOFT_buffer_samples supported!\n");
307 palBufferSamplesSOFT
= alGetProcAddress("alBufferSamplesSOFT");
308 palIsBufferFormatSupportedSOFT
= alGetProcAddress("alIsBufferFormatSupportedSOFT");
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
]))
321 fprintf(stderr
, "Playing %s (%s, %s, %dhz)\n", argv
[i
],
322 TypeName(player
->type
), ChannelsName(player
->channels
),
325 if(!StartPlayer(player
))
327 ClosePlayerFile(player
);
331 while(UpdatePlayer(player
))
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
);