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"
38 #include "common/sdl_sound.h"
41 static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT
= wrap_BufferSamples
;
42 static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT
;
45 /* Define the number of buffers and buffer size (in milliseconds) to use. 4
46 * buffers with 200ms each gives a nice per-chunk size, and lets the queue last
47 * for almost one second. */
49 #define BUFFER_TIME_MS 200
51 typedef struct StreamPlayer
{
52 /* These are the buffers and source to play out through OpenAL with */
53 ALuint buffers
[NUM_BUFFERS
];
56 /* Handle for the audio file */
59 /* The format of the output stream */
66 static StreamPlayer
*NewPlayer(void);
67 static void DeletePlayer(StreamPlayer
*player
);
68 static int OpenPlayerFile(StreamPlayer
*player
, const char *filename
);
69 static void ClosePlayerFile(StreamPlayer
*player
);
70 static int StartPlayer(StreamPlayer
*player
);
71 static int UpdatePlayer(StreamPlayer
*player
);
73 /* Creates a new player object, and allocates the needed OpenAL source and
74 * buffer objects. Error checking is simplified for the purposes of this
75 * example, and will cause an abort if needed. */
76 static StreamPlayer
*NewPlayer(void)
80 player
= malloc(sizeof(*player
));
81 assert(player
!= NULL
);
83 memset(player
, 0, sizeof(*player
));
85 /* Generate the buffers and source */
86 alGenBuffers(NUM_BUFFERS
, player
->buffers
);
87 assert(alGetError() == AL_NO_ERROR
&& "Could not create buffers");
89 alGenSources(1, &player
->source
);
90 assert(alGetError() == AL_NO_ERROR
&& "Could not create source");
92 /* Set parameters so mono sources play out the front-center speaker and
93 * won't distance attenuate. */
94 alSource3i(player
->source
, AL_POSITION
, 0, 0, -1);
95 alSourcei(player
->source
, AL_SOURCE_RELATIVE
, AL_TRUE
);
96 alSourcei(player
->source
, AL_ROLLOFF_FACTOR
, 0);
97 assert(alGetError() == AL_NO_ERROR
&& "Could not set source parameters");
102 /* Destroys a player object, deleting the source and buffers. No error handling
103 * since these calls shouldn't fail with a properly-made player object. */
104 static void DeletePlayer(StreamPlayer
*player
)
106 ClosePlayerFile(player
);
108 alDeleteSources(1, &player
->source
);
109 alDeleteBuffers(NUM_BUFFERS
, player
->buffers
);
110 if(alGetError() != AL_NO_ERROR
)
111 fprintf(stderr
, "Failed to delete object IDs\n");
113 memset(player
, 0, sizeof(*player
));
118 /* Opens the first audio stream of the named file. If a file is already open,
119 * it will be closed first. */
120 static int OpenPlayerFile(StreamPlayer
*player
, const char *filename
)
122 ClosePlayerFile(player
);
124 /* Open the file and get the first stream from it */
125 player
->file
= openAudioFile(filename
, BUFFER_TIME_MS
);
128 fprintf(stderr
, "Could not open audio in %s\n", filename
);
132 /* Get the stream format, and figure out the OpenAL format */
133 if(getAudioInfo(player
->file
, &player
->rate
, &player
->channels
, &player
->type
) != 0)
135 fprintf(stderr
, "Error getting audio info for %s\n", filename
);
139 player
->format
= GetFormat(player
->channels
, player
->type
, alIsBufferFormatSupportedSOFT
);
140 if(player
->format
== 0)
142 fprintf(stderr
, "Unsupported format (%s, %s) for %s\n",
143 ChannelsName(player
->channels
), TypeName(player
->type
),
151 closeAudioFile(player
->file
);
157 /* Closes the audio file stream */
158 static void ClosePlayerFile(StreamPlayer
*player
)
160 closeAudioFile(player
->file
);
165 /* Prebuffers some audio from the file, and starts playing the source */
166 static int StartPlayer(StreamPlayer
*player
)
170 /* Rewind the source position and clear the buffer queue */
171 alSourceRewind(player
->source
);
172 alSourcei(player
->source
, AL_BUFFER
, 0);
174 /* Fill the buffer queue */
175 for(i
= 0;i
< NUM_BUFFERS
;i
++)
180 /* Get some data to give it to the buffer */
181 data
= getAudioData(player
->file
, &got
);
184 alBufferSamplesSOFT(player
->buffers
[i
], player
->rate
, player
->format
,
185 BytesToFrames(got
, player
->channels
, player
->type
),
186 player
->channels
, player
->type
, data
);
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 */
226 alSourceUnqueueBuffers(player
->source
, 1, &bufid
);
229 /* Read the next chunk of data, refill the buffer, and queue it
230 * back on the source */
231 data
= getAudioData(player
->file
, &got
);
234 alBufferSamplesSOFT(bufid
, player
->rate
, player
->format
,
235 BytesToFrames(got
, player
->channels
, player
->type
),
236 player
->channels
, player
->type
, data
);
237 alSourceQueueBuffers(player
->source
, 1, &bufid
);
239 if(alGetError() != AL_NO_ERROR
)
241 fprintf(stderr
, "Error buffering data\n");
246 /* Make sure the source hasn't underrun */
247 if(state
!= AL_PLAYING
&& state
!= AL_PAUSED
)
251 /* If no buffers are queued, playback is finished */
252 alGetSourcei(player
->source
, AL_BUFFERS_QUEUED
, &queued
);
256 alSourcePlay(player
->source
);
257 if(alGetError() != AL_NO_ERROR
)
259 fprintf(stderr
, "Error restarting playback\n");
268 int main(int argc
, char **argv
)
270 StreamPlayer
*player
;
273 /* Print out usage if no file was specified */
276 fprintf(stderr
, "Usage: %s <filenames...>\n", argv
[0]);
283 if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
285 printf("AL_SOFT_buffer_samples supported!\n");
286 alBufferSamplesSOFT
= alGetProcAddress("alBufferSamplesSOFT");
287 alIsBufferFormatSupportedSOFT
= alGetProcAddress("alIsBufferFormatSupportedSOFT");
290 printf("AL_SOFT_buffer_samples not supported\n");
292 player
= NewPlayer();
294 /* Play each file listed on the command line */
295 for(i
= 1;i
< argc
;i
++)
297 const char *namepart
;
299 if(!OpenPlayerFile(player
, argv
[i
]))
302 /* Get the name portion, without the path, for display. */
303 namepart
= strrchr(argv
[i
], '/');
304 if(namepart
|| (namepart
=strrchr(argv
[i
], '\\')))
309 printf("Playing: %s (%s, %s, %dhz)\n", namepart
,
310 TypeName(player
->type
), ChannelsName(player
->channels
),
314 if(!StartPlayer(player
))
316 ClosePlayerFile(player
);
320 while(UpdatePlayer(player
))
321 al_nssleep(10000000);
323 /* All done with this file. Close it and go to the next */
324 ClosePlayerFile(player
);
328 /* All files done. Delete the player, and close OpenAL */
329 DeletePlayer(player
);