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
<alureStream
> stream(instream
);
40 ALuint freq
, blockAlign
;
42 if(!stream
->GetFormat(&format
, &freq
, &blockAlign
))
44 SetError("Could not get stream format");
50 SetError("No valid format");
55 SetError("Invalid block size");
60 SetError("Invalid sample rate");
64 ALuint framesPerBlock
;
65 DetectCompressionRate(format
, &framesPerBlock
);
66 alureUInt64 len64
= (SizeIsUS
? (alureUInt64
)chunkLength
* freq
/ 1000000 / framesPerBlock
* blockAlign
:
67 (alureUInt64
)chunkLength
);
68 if(len64
> 0xFFFFFFFF)
70 SetError("Chunk length too large");
74 chunkLength
= len64
- (len64
%blockAlign
);
77 SetError("Chunk length too small");
81 stream
->chunkLen
= chunkLength
;
82 stream
->dataChunk
= new ALubyte
[stream
->chunkLen
];
84 alGenBuffers(numBufs
, bufs
);
85 if(alGetError() != AL_NO_ERROR
)
87 SetError("Buffer creation failed");
92 for(filled
= 0;filled
< numBufs
;filled
++)
94 ALuint got
= stream
->GetData(stream
->dataChunk
, stream
->chunkLen
);
95 got
-= got
%blockAlign
;
98 alBufferData(bufs
[filled
], format
, stream
->dataChunk
, got
, freq
);
101 while(filled
< numBufs
)
103 alBufferData(bufs
[filled
], format
, stream
->dataChunk
, 0, freq
);
106 if(alGetError() != AL_NO_ERROR
)
108 alDeleteBuffers(numBufs
, bufs
);
111 SetError("Buffering error");
115 return stream
.release();
121 /* Function: alureStreamSizeIsMicroSec
123 * Specifies if the chunk size value given to the alureCreateStream functions
124 * is in bytes (default) or microseconds. Specifying the size in microseconds
125 * can help manage the time needed in between needed updates (since the format
126 * and sample rate of the stream may not be known), while specifying the size
127 * in bytes can help control memory usage.
130 * Previously set value.
132 * *Version Added*: 1.1
135 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
136 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
138 ALURE_API ALboolean ALURE_APIENTRY
alureStreamSizeIsMicroSec(ALboolean useUS
)
140 ALboolean old
= (SizeIsUS
? AL_TRUE
: AL_FALSE
);
145 /* Function: alureCreateStreamFromFile
147 * Opens a file and sets it up for streaming. The given chunkLength is the
148 * number of bytes, or microseconds worth of bytes if
149 * <alureStreamSizeIsMicroSec> was last called with AL_TRUE, each buffer will
150 * fill with. ALURE will optionally generate the specified number of buffer
151 * objects, fill them with the beginning of the data, then place the new IDs
152 * into the provided storage, before returning. Requires an active context.
155 * An opaque handle used to control the opened stream, or NULL on error.
158 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromMemory>,
159 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
161 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromFile(const ALchar
*fname
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
163 if(alGetError() != AL_NO_ERROR
)
165 SetError("Existing OpenAL error");
171 SetError("Invalid chunk length");
177 SetError("Invalid buffer count");
181 alureStream
*stream
= create_stream(fname
);
182 if(!stream
->IsValid())
188 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
191 /* Function: alureCreateStreamFromMemory
193 * Opens a file image from memory and sets it up for streaming, similar to
194 * <alureCreateStreamFromFile>. The given data buffer can be safely deleted
195 * after calling this function. Requires an active context.
198 * An opaque handle used to control the opened stream, or NULL on error.
201 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
202 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
204 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromMemory(const ALubyte
*fdata
, ALuint length
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
206 if(alGetError() != AL_NO_ERROR
)
208 SetError("Existing OpenAL error");
214 SetError("Invalid chunk length");
220 SetError("Invalid buffer count");
226 SetError("Invalid data length");
230 ALubyte
*streamData
= new ALubyte
[length
];
231 memcpy(streamData
, fdata
, length
);
234 memData
.Data
= streamData
;
235 memData
.Length
= length
;
238 alureStream
*stream
= create_stream(memData
);
239 stream
->data
= streamData
;
240 if(!stream
->IsValid())
246 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
249 /* Function: alureCreateStreamFromStaticMemory
251 * Identical to <alureCreateStreamFromMemory>, except the given memory is used
252 * directly and not duplicated. As a consequence, the data buffer must remain
253 * valid while the stream is alive. Requires an active context.
256 * An opaque handle used to control the opened stream, or NULL on error.
259 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
260 * <alureCreateStreamFromMemory>, <alureCreateStreamFromCallback>
262 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromStaticMemory(const ALubyte
*fdata
, ALuint length
, ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
264 if(alGetError() != AL_NO_ERROR
)
266 SetError("Existing OpenAL error");
272 SetError("Invalid chunk length");
278 SetError("Invalid buffer count");
284 SetError("Invalid data length");
289 memData
.Data
= fdata
;
290 memData
.Length
= length
;
293 alureStream
*stream
= create_stream(memData
);
294 if(!stream
->IsValid())
300 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
303 /* Function: alureCreateStreamFromCallback
305 * Creates a stream using the specified callback to retrieve data. Requires an
309 * callback - This is called when more data is needed from the stream. Up to
310 * the specified number of bytes should be written to the data
311 * pointer, and the number of bytes actually written should be
312 * returned. The number of bytes written must be block aligned for
313 * the format (eg. a multiple of 4 for AL_FORMAT_STEREO16), or an
314 * OpenAL error may occur during buffering.
315 * userdata - A handle passed through to the callback.
316 * format - The format of the data the callback will be giving. The format must
317 * be valid for the context.
318 * samplerate - The sample rate (frequency) of the stream
321 * An opaque handle used to control the opened stream, or NULL on error.
324 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
325 * <alureCreateStreamFromMemory>, <alureCreateStreamFromStaticMemory>
327 ALURE_API alureStream
* ALURE_APIENTRY
alureCreateStreamFromCallback(
328 ALuint (*callback
)(void *userdata
, ALubyte
*data
, ALuint bytes
),
329 void *userdata
, ALenum format
, ALuint samplerate
,
330 ALsizei chunkLength
, ALsizei numBufs
, ALuint
*bufs
)
332 if(alGetError() != AL_NO_ERROR
)
334 SetError("Existing OpenAL error");
340 SetError("Invalid callback");
346 SetError("Invalid chunk length");
352 SetError("Invalid buffer count");
357 newcb
.open_file
= NULL
;
358 newcb
.open_mem
= NULL
;
359 newcb
.get_fmt
= NULL
;
360 newcb
.decode
= callback
;
364 alureStream
*stream
= create_stream(userdata
, format
, samplerate
, newcb
);
365 return InitStream(stream
, chunkLength
, numBufs
, bufs
);
368 /* Function: alureGetStreamFormat
370 * Retrieves the format, frequency, and block-alignment used for the given
371 * stream. If a parameter is NULL, that value will not be returned.
376 * *Version Added*: 1.1
378 ALURE_API ALboolean ALURE_APIENTRY
alureGetStreamFormat(alureStream
*stream
,
379 ALenum
*format
, ALuint
*frequency
, ALuint
*blockAlign
)
382 ALuint _rate
, _balign
;
384 if(!alureStream::Verify(stream
))
386 SetError("Invalid stream pointer");
390 if(!format
) format
= &_fmt
;
391 if(!frequency
) frequency
= &_rate
;
392 if(!blockAlign
) blockAlign
= &_balign
;
394 if(!stream
->GetFormat(format
, frequency
, blockAlign
))
396 SetError("Could not get stream format");
403 /* Function: alureBufferDataFromStream
405 * Buffers the given buffer objects with the next chunks of data from the
406 * stream. The given buffer objects do not need to be ones given by the
407 * alureCreateStreamFrom* functions. Requires an active context.
410 * The number of buffers filled with new data, or -1 on error. If the value
411 * returned is less than the number requested, the end of the stream has been
414 ALURE_API ALsizei ALURE_APIENTRY
alureBufferDataFromStream(alureStream
*stream
, ALsizei numBufs
, ALuint
*bufs
)
416 if(alGetError() != AL_NO_ERROR
)
418 SetError("Existing OpenAL error");
422 if(!alureStream::Verify(stream
))
424 SetError("Invalid stream pointer");
430 SetError("Invalid buffer count");
435 ALuint freq
, blockAlign
;
437 if(!stream
->GetFormat(&format
, &freq
, &blockAlign
))
439 SetError("Could not get stream format");
444 for(filled
= 0;filled
< numBufs
;filled
++)
446 ALuint got
= stream
->GetData(stream
->dataChunk
, stream
->chunkLen
);
447 got
-= got
%blockAlign
;
450 alBufferData(bufs
[filled
], format
, stream
->dataChunk
, got
, freq
);
451 if(alGetError() != AL_NO_ERROR
)
453 SetError("Buffer load failed");
461 /* Function: alureRewindStream
463 * Rewinds the stream so that the next alureBufferDataFromStream call will
464 * restart from the beginning of the audio file.
470 * <alureSetStreamOrder>
472 ALURE_API ALboolean ALURE_APIENTRY
alureRewindStream(alureStream
*stream
)
474 if(!alureStream::Verify(stream
))
476 SetError("Invalid stream pointer");
480 return stream
->Rewind();
483 /* Function: alureSetStreamOrder
485 * Skips the module decoder to the specified order, so following buffering
486 * calls will decode from the specified order. For non-module formats, setting
487 * order 0 is identical to rewinding the stream (other orders will fail).
492 * *Version Added*: 1.1
495 * <alureRewindStream>
497 ALURE_API ALboolean ALURE_APIENTRY
alureSetStreamOrder(alureStream
*stream
, ALuint order
)
499 if(!alureStream::Verify(stream
))
501 SetError("Invalid stream pointer");
505 return stream
->SetOrder(order
);
508 /* Function: alureDestroyStream
510 * Closes an opened stream. For convenience, it will also delete the given
511 * buffer objects. The given buffer objects do not need to be ones given by the
512 * alureCreateStreamFrom* functions. Requires an active context.
517 ALURE_API ALboolean ALURE_APIENTRY
alureDestroyStream(alureStream
*stream
, ALsizei numBufs
, ALuint
*bufs
)
519 if(alGetError() != AL_NO_ERROR
)
521 SetError("Existing OpenAL error");
527 SetError("Invalid buffer count");
531 if(stream
&& !alureStream::Verify(stream
))
533 SetError("Invalid stream pointer");
537 alDeleteBuffers(numBufs
, bufs
);
538 if(alGetError() != AL_NO_ERROR
)
540 SetError("Buffer deletion failed");
547 std::istream
*f
= stream
->fstream
;