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 /* Define the number of buffers and buffer size (in samples) to use. 4 buffers
42 * with 8192 samples each gives a nice per-chunk size, and lets the queue last
43 * for almost 3/4ths of a second for a 44.1khz stream. */
45 #define BUFFER_SIZE 8192
47 typedef struct StreamPlayer
{
48 /* These are the buffers and source to play out through OpenAL with */
49 ALuint buffers
[NUM_BUFFERS
];
52 /* Handles for the audio stream */
56 /* A temporary data buffer for readAVAudioData to write to and pass to
61 /* The format of the output stream */
68 static StreamPlayer
*NewPlayer(void);
69 static void DeletePlayer(StreamPlayer
*player
);
70 static int OpenPlayerFile(StreamPlayer
*player
, const char *filename
);
71 static void ClosePlayerFile(StreamPlayer
*player
);
72 static int StartPlayer(StreamPlayer
*player
);
73 static int UpdatePlayer(StreamPlayer
*player
);
75 /* Creates a new player object, and allocates the needed OpenAL source and
76 * buffer objects. Error checking is simplified for the purposes of this
77 * example, and will cause an abort if needed. */
78 static StreamPlayer
*NewPlayer(void)
82 player
= malloc(sizeof(*player
));
83 assert(player
!= NULL
);
85 memset(player
, 0, sizeof(*player
));
87 /* Generate the buffers and source */
88 alGenBuffers(NUM_BUFFERS
, player
->buffers
);
89 assert(alGetError() == AL_NO_ERROR
&& "Could not create buffers");
91 alGenSources(1, &player
->source
);
92 assert(alGetError() == AL_NO_ERROR
&& "Could not create source");
94 /* Set parameters so mono sources play out the front-center speaker and
95 * won't distance attenuate. */
96 alSource3i(player
->source
, AL_POSITION
, 0, 0, -1);
97 alSourcei(player
->source
, AL_SOURCE_RELATIVE
, AL_TRUE
);
98 alSourcei(player
->source
, AL_ROLLOFF_FACTOR
, 0);
99 assert(alGetError() == AL_NO_ERROR
&& "Could not set source parameters");
104 /* Destroys a player object, deleting the source and buffers. No error handling
105 * since these calls shouldn't fail with a properly-made player object. */
106 static void DeletePlayer(StreamPlayer
*player
)
108 ClosePlayerFile(player
);
110 alDeleteSources(1, &player
->source
);
111 alDeleteBuffers(NUM_BUFFERS
, player
->buffers
);
112 if(alGetError() != AL_NO_ERROR
)
113 fprintf(stderr
, "Failed to delete object IDs\n");
115 memset(player
, 0, sizeof(*player
));
120 /* Opens the first audio stream of the named file. If a file is already open,
121 * it will be closed first. */
122 static int OpenPlayerFile(StreamPlayer
*player
, const char *filename
)
124 ClosePlayerFile(player
);
126 /* Open the file and get the first stream from it */
127 player
->file
= openAVFile(filename
);
128 player
->stream
= getAVAudioStream(player
->file
, 0);
131 fprintf(stderr
, "Could not open audio in %s\n", filename
);
135 /* Get the stream format, and figure out the OpenAL format */
136 if(getAVAudioInfo(player
->stream
, &player
->rate
, &player
->channels
,
139 fprintf(stderr
, "Error getting audio info for %s\n", filename
);
143 player
->format
= GetFormat(player
->channels
, player
->type
);
144 if(player
->format
== 0)
146 fprintf(stderr
, "Unsupported format (%s, %s) for %s\n",
147 ChannelsName(player
->channels
), TypeName(player
->type
),
152 /* Allocate enough space for the temp buffer, given the format */
153 player
->datasize
= FramesToBytes(BUFFER_SIZE
, player
->channels
,
155 player
->data
= malloc(player
->datasize
);
156 if(player
->data
== NULL
)
158 fprintf(stderr
, "Error allocating %d bytes\n", player
->datasize
);
165 closeAVFile(player
->file
);
167 player
->stream
= NULL
;
168 player
->datasize
= 0;
173 /* Closes the audio file stream */
174 static void ClosePlayerFile(StreamPlayer
*player
)
176 closeAVFile(player
->file
);
178 player
->stream
= NULL
;
182 player
->datasize
= 0;
186 /* Prebuffers some audio from the file, and starts playing the source */
187 static int StartPlayer(StreamPlayer
*player
)
191 /* Rewind the source position and clear the buffer queue */
192 alSourceRewind(player
->source
);
193 alSourcei(player
->source
, AL_BUFFER
, 0);
195 /* Fill the buffer queue */
196 for(i
= 0;i
< NUM_BUFFERS
;i
++)
198 /* Get some data to give it to the buffer */
199 got
= readAVAudioData(player
->stream
, player
->data
, player
->datasize
);
202 alBufferData(player
->buffers
[i
], player
->format
, player
->data
, got
,
205 if(alGetError() != AL_NO_ERROR
)
207 fprintf(stderr
, "Error buffering for playback\n");
211 /* Now queue and start playback! */
212 alSourceQueueBuffers(player
->source
, i
, player
->buffers
);
213 alSourcePlay(player
->source
);
214 if(alGetError() != AL_NO_ERROR
)
216 fprintf(stderr
, "Error starting playback\n");
223 static int UpdatePlayer(StreamPlayer
*player
)
225 ALint processed
, state
;
227 /* Get relevant source info */
228 alGetSourcei(player
->source
, AL_SOURCE_STATE
, &state
);
229 alGetSourcei(player
->source
, AL_BUFFERS_PROCESSED
, &processed
);
230 if(alGetError() != AL_NO_ERROR
)
232 fprintf(stderr
, "Error checking source state\n");
236 /* Unqueue and handle each processed buffer */
242 alSourceUnqueueBuffers(player
->source
, 1, &bufid
);
245 /* Read the next chunk of data, refill the buffer, and queue it
246 * back on the source */
247 got
= readAVAudioData(player
->stream
, player
->data
, player
->datasize
);
250 alBufferData(bufid
, player
->format
, player
->data
, got
,
252 alSourceQueueBuffers(player
->source
, 1, &bufid
);
254 if(alGetError() != AL_NO_ERROR
)
256 fprintf(stderr
, "Error buffering data\n");
261 /* Make sure the source hasn't underrun */
262 if(state
!= AL_PLAYING
&& state
!= AL_PAUSED
)
266 /* If no buffers are queued, playback is finished */
267 alGetSourcei(player
->source
, AL_BUFFERS_QUEUED
, &queued
);
271 alSourcePlay(player
->source
);
272 if(alGetError() != AL_NO_ERROR
)
274 fprintf(stderr
, "Error restarting playback\n");
283 int main(int argc
, char **argv
)
285 StreamPlayer
*player
;
288 /* Print out usage if no file was specified */
291 fprintf(stderr
, "Usage: %s <filenames...>\n", argv
[0]);
298 player
= NewPlayer();
300 /* Play each file listed on the command line */
301 for(i
= 1;i
< argc
;i
++)
303 if(!OpenPlayerFile(player
, argv
[i
]))
306 fprintf(stderr
, "Playing %s (%s, %s, %dhz)\n", argv
[i
],
307 TypeName(player
->type
), ChannelsName(player
->channels
),
310 if(!StartPlayer(player
))
312 ClosePlayerFile(player
);
316 while(UpdatePlayer(player
))
319 /* All done with this file. Close it and go to the next */
320 ClosePlayerFile(player
);
322 fprintf(stderr
, "Done.\n");
324 /* All files done. Delete the player, and close OpenAL */
325 DeletePlayer(player
);