2 * ALURE OpenAL utility library
3 * Copyright (c) 2009 by Chris Robinson.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 /* Title: Streaming */
34 static bool SizeIsUS
= false;
36 static alureStream
*InitStream(alureStream
*instream
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
38 std::auto_ptr
<std::istream
> fstream(instream
->fstream
);
39 std::auto_ptr
<alureStream
> stream(instream
);
41 ALuint freq
, blockAlign
;
43 if(!stream
->GetFormat(&format
, &freq
, &blockAlign
))
45 SetError("Could not get stream format");
51 SetError("No valid format");
56 SetError("Invalid block size");
61 SetError("Invalid sample rate");
67 alureUInt64 len64
= chunkLength
;
68 ALuint framesPerBlock
= DetectCompressionRate(format
);
69 ALuint blockSize
= DetectBlockAlignment(format
);
70 if(framesPerBlock
== 0 || blockSize
== 0)
72 SetError("Unknown compression rate");
76 len64
= len64
* freq
/ 1000000 / framesPerBlock
* blockSize
;
77 if(len64
> 0x7FFFFFFF)
79 SetError("Chunk length too large");
85 chunkLength
-= chunkLength
%blockAlign
;
88 SetError("Chunk length too small");
92 stream
->dataChunk
.resize(chunkLength
);
96 alGenBuffers(numBufs
, bufs
);
97 if(alGetError() != AL_NO_ERROR
)
99 SetError("Buffer creation failed");
105 for(filled
= 0;filled
< numBufs
;filled
++)
107 ALuint got
= stream
->GetData(&stream
->dataChunk
[0], stream
->dataChunk
.size());
108 got
-= got
%blockAlign
;
111 alBufferData(bufs
[filled
], format
, &stream
->dataChunk
[0], got
, freq
);
114 while(filled
< numBufs
)
116 alBufferData(bufs
[filled
], format
, &stream
->dataChunk
[0], 0, freq
);
119 if(alGetError() != AL_NO_ERROR
)
121 alDeleteBuffers(numBufs
, bufs
);
124 SetError("Buffering error");
129 return stream
.release();
135 /* Function: alureStreamSizeIsMicroSec
137 * Specifies if the chunk size value given to the alureCreateStream functions
138 * is in bytes (default) or microseconds. Specifying the size in microseconds
139 * can help manage the time needed in between needed updates (since the format
140 * and sample rate of the stream may not be known), while specifying the size
141 * in bytes can help control memory usage.
144 * Previously set value.
146 * *Version Added*: 1.1
149 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
150 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
152 ALURE_API ALboolean ALURE_APIENTRY
alureStreamSizeIsMicroSec(ALboolean useUS
)
154 ALboolean old
= (SizeIsUS
? AL_TRUE
: AL_FALSE
);
159 /* Function: alureCreateStreamFromFile
161 * Opens a file and sets it up for streaming. The given chunkLength is the
162 * number of bytes, or microseconds worth of bytes if
163 * <alureStreamSizeIsMicroSec> was last called with AL_TRUE, each buffer will
164 * fill with. ALURE will optionally generate the specified number of buffer
165 * objects, fill them with the beginning of the data, then place the new IDs
166 * into the provided storage, before returning. Requires an active context.
169 * An opaque handle used to control the opened stream, or NULL on error.
172 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromMemory>,
173 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
175 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromFile(const ALchar
*fname
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
177 if(alGetError() != AL_NO_ERROR
)
179 SetError("Existing OpenAL error");
185 SetError("Invalid chunk length");
191 SetError("Invalid buffer count");
195 alureStream
*stream
= create_stream(fname
);
196 if(!stream
->IsValid())
202 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
205 /* Function: alureCreateStreamFromMemory
207 * Opens a file image from memory and sets it up for streaming, similar to
208 * <alureCreateStreamFromFile>. The given data buffer can be safely deleted
209 * after calling this function. Requires an active context.
212 * An opaque handle used to control the opened stream, or NULL on error.
215 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
216 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
218 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromMemory(const ALubyte
*fdata
, ALuint length
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
220 if(alGetError() != AL_NO_ERROR
)
222 SetError("Existing OpenAL error");
228 SetError("Invalid chunk length");
234 SetError("Invalid buffer count");
240 SetError("Invalid data length");
244 ALubyte
*streamData
= new ALubyte
[length
];
245 memcpy(streamData
, fdata
, length
);
248 memData
.Data
= streamData
;
249 memData
.Length
= length
;
252 alureStream
*stream
= create_stream(memData
);
253 stream
->data
= streamData
;
254 if(!stream
->IsValid())
260 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
263 /* Function: alureCreateStreamFromStaticMemory
265 * Identical to <alureCreateStreamFromMemory>, except the given memory is used
266 * directly and not duplicated. As a consequence, the data buffer must remain
267 * valid while the stream is alive. Requires an active context.
270 * An opaque handle used to control the opened stream, or NULL on error.
273 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
274 * <alureCreateStreamFromMemory>, <alureCreateStreamFromCallback>
276 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromStaticMemory(const ALubyte
*fdata
, ALuint length
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
278 if(alGetError() != AL_NO_ERROR
)
280 SetError("Existing OpenAL error");
286 SetError("Invalid chunk length");
292 SetError("Invalid buffer count");
298 SetError("Invalid data length");
303 memData
.Data
= fdata
;
304 memData
.Length
= length
;
307 alureStream
*stream
= create_stream(memData
);
308 if(!stream
->IsValid())
314 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
317 /* Function: alureCreateStreamFromCallback
319 * Creates a stream using the specified callback to retrieve data. Requires an
323 * callback - This is called when more data is needed from the stream. Up to
324 * the specified number of bytes should be written to the data
325 * pointer, and the number of bytes actually written should be
326 * returned. The number of bytes written must be block aligned for
327 * the format (eg. a multiple of 4 for AL_FORMAT_STEREO16), or an
328 * OpenAL error may occur during buffering.
329 * userdata - A handle passed through to the callback.
330 * format - The format of the data the callback will be giving. The format must
331 * be valid for the context.
332 * samplerate - The sample rate (frequency) of the stream
335 * An opaque handle used to control the opened stream, or NULL on error.
338 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
339 * <alureCreateStreamFromMemory>, <alureCreateStreamFromStaticMemory>
341 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromCallback(
342 ALuint (*callback
)(void *userdata
, ALubyte
*data
, ALuint bytes
),
343 void *userdata
, ALenum format
, ALuint samplerate
,
344 ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
346 if(alGetError() != AL_NO_ERROR
)
348 SetError("Existing OpenAL error");
354 SetError("Invalid callback");
360 SetError("Invalid chunk length");
366 SetError("Invalid buffer count");
371 newcb
.open_file
= NULL
;
372 newcb
.open_mem
= NULL
;
373 newcb
.get_fmt
= NULL
;
374 newcb
.decode
= callback
;
378 alureStream
*stream
= create_stream(userdata
, format
, samplerate
, newcb
);
379 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
382 /* Function: alureGetStreamFrequency
384 * Retrieves the frequency used for the given stream.
389 * *Version Added*: 1.1
391 ALURE_API ALsizei ALURE_APIENTRY
alureGetStreamFrequency(alureStream
*stream
)
396 if(!alureStream::Verify(stream
))
398 SetError("Invalid stream pointer");
402 if(!stream
->GetFormat(&format
, &rate
, &balign
))
404 SetError("Could not get stream format");
411 /* Function: alureBufferDataFromStream
413 * Buffers the given buffer objects with the next chunks of data from the
414 * stream. The given buffer objects do not need to be ones given by the
415 * alureCreateStreamFrom* functions. Requires an active context.
418 * The number of buffers filled with new data, or -1 on error. If the value
419 * returned is less than the number requested, the end of the stream has been
422 ALURE_API ALsizei ALURE_APIENTRY
alureBufferDataFromStream(alureStream
*stream
, ALsizei numBufs
, ALuint
*bufs
)
424 if(alGetError() != AL_NO_ERROR
)
426 SetError("Existing OpenAL error");
430 if(!alureStream::Verify(stream
))
432 SetError("Invalid stream pointer");
438 SetError("Invalid buffer count");
443 ALuint freq
, blockAlign
;
445 if(!stream
->GetFormat(&format
, &freq
, &blockAlign
))
447 SetError("Could not get stream format");
452 for(filled
= 0;filled
< numBufs
;filled
++)
454 ALuint got
= stream
->GetData(&stream
->dataChunk
[0], stream
->dataChunk
.size());
455 got
-= got
%blockAlign
;
458 alBufferData(bufs
[filled
], format
, &stream
->dataChunk
[0], got
, freq
);
459 if(alGetError() != AL_NO_ERROR
)
461 SetError("Buffer load failed");
469 /* Function: alureRewindStream
471 * Rewinds the stream so that the next alureBufferDataFromStream call will
472 * restart from the beginning of the audio file.
478 * <alureSetStreamOrder>
480 ALURE_API ALboolean ALURE_APIENTRY
alureRewindStream(alureStream
*stream
)
482 if(!alureStream::Verify(stream
))
484 SetError("Invalid stream pointer");
488 return stream
->Rewind();
491 /* Function: alureSetStreamOrder
493 * Skips the module decoder to the specified order, so following buffering
494 * calls will decode from the specified order. For non-module formats, setting
495 * order 0 is identical to rewinding the stream (other orders will fail).
500 * *Version Added*: 1.1
503 * <alureRewindStream>
505 ALURE_API ALboolean ALURE_APIENTRY
alureSetStreamOrder(alureStream
*stream
, ALuint order
)
507 if(!alureStream::Verify(stream
))
509 SetError("Invalid stream pointer");
513 return stream
->SetOrder(order
);
516 /* Function: alureSetStreamPatchset
518 * Specifies the patchset to use for MIDI streams. By default, the FluidSynth
519 * decoder will look for one in the FLUID_SOUNDFONT environment variable, but
520 * this can be used to change it to something different. On non-MIDI streams,
521 * this has no effect.
526 * *Version Added*: 1.1
528 ALURE_API ALboolean ALURE_APIENTRY
alureSetStreamPatchset(alureStream
*stream
, const ALchar
*patchset
)
530 if(!alureStream::Verify(stream
))
532 SetError("Invalid stream pointer");
536 return stream
->SetPatchset(patchset
);
539 /* Function: alureDestroyStream
541 * Closes an opened stream. For convenience, it will also delete the given
542 * buffer objects. The given buffer objects do not need to be ones given by the
543 * alureCreateStreamFrom* functions. Requires an active context.
548 ALURE_API ALboolean ALURE_APIENTRY
alureDestroyStream(alureStream
*stream
, ALsizei numBufs
, ALuint
*bufs
)
550 if(alGetError() != AL_NO_ERROR
)
552 SetError("Existing OpenAL error");
558 SetError("Invalid buffer count");
562 if(stream
&& !alureStream::Verify(stream
))
564 SetError("Invalid stream pointer");
568 alDeleteBuffers(numBufs
, bufs
);
569 if(alGetError() != AL_NO_ERROR
)
571 SetError("Buffer deletion failed");
578 std::istream
*f
= stream
->fstream
;