Be more informative with some errors
[alure.git] / src / stream.cpp
blob665dc87e290e0624a0f624fccd81266f19aa7596
1 /*
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
21 * IN THE SOFTWARE.
24 /* Title: Streaming */
26 #include "config.h"
28 #include "main.h"
30 #include <string.h>
32 #include <memory>
34 static bool SizeIsUS = false;
36 static alureStream *InitStream(alureStream *instream, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
38 std::auto_ptr<alureStream> stream(instream);
39 ALenum format;
40 ALuint freq, blockAlign;
42 if(!stream->GetFormat(&format, &freq, &blockAlign))
44 SetError("Could not get stream format");
45 return NULL;
48 if(format == AL_NONE)
50 SetError("No valid format");
51 return NULL;
53 if(blockAlign == 0)
55 SetError("Invalid block size");
56 return NULL;
58 if(freq == 0)
60 SetError("Invalid sample rate");
61 return NULL;
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");
71 return NULL;
74 chunkLength = len64 - (len64%blockAlign);
75 if(chunkLength <= 0)
77 SetError("Chunk length too small");
78 return NULL;
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");
88 return NULL;
91 ALsizei filled;
92 for(filled = 0;filled < numBufs;filled++)
94 ALuint got = stream->GetData(stream->dataChunk, stream->chunkLen);
95 got -= got%blockAlign;
96 if(got == 0) break;
98 alBufferData(bufs[filled], format, stream->dataChunk, got, freq);
101 while(filled < numBufs)
103 alBufferData(bufs[filled], format, stream->dataChunk, 0, freq);
104 filled++;
106 if(alGetError() != AL_NO_ERROR)
108 alDeleteBuffers(numBufs, bufs);
109 alGetError();
111 SetError("Buffering error");
112 return NULL;
115 return stream.release();
119 extern "C" {
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.
129 * Returns:
130 * Previously set value.
132 * *Version Added*: 1.1
134 * See Also:
135 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
136 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
138 ALURE_API ALboolean ALURE_APIENTRY alureStreamSizeIsMicroSec(ALboolean useUS)
140 ALboolean old = (SizeIsUS ? AL_TRUE : AL_FALSE);
141 SizeIsUS = !!useUS;
142 return old;
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.
154 * Returns:
155 * An opaque handle used to control the opened stream, or NULL on error.
157 * See Also:
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");
166 return NULL;
169 if(chunkLength < 0)
171 SetError("Invalid chunk length");
172 return NULL;
175 if(numBufs < 0)
177 SetError("Invalid buffer count");
178 return NULL;
181 alureStream *stream = create_stream(fname);
182 if(!stream->IsValid())
184 delete stream;
185 return NULL;
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.
197 * Returns:
198 * An opaque handle used to control the opened stream, or NULL on error.
200 * See Also:
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");
209 return NULL;
212 if(chunkLength < 0)
214 SetError("Invalid chunk length");
215 return NULL;
218 if(numBufs < 0)
220 SetError("Invalid buffer count");
221 return NULL;
224 if(length <= 0)
226 SetError("Invalid data length");
227 return NULL;
230 ALubyte *streamData = new ALubyte[length];
231 memcpy(streamData, fdata, length);
233 MemDataInfo memData;
234 memData.Data = streamData;
235 memData.Length = length;
236 memData.Pos = 0;
238 alureStream *stream = create_stream(memData);
239 stream->data = streamData;
240 if(!stream->IsValid())
242 delete stream;
243 return NULL;
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.
255 * Returns:
256 * An opaque handle used to control the opened stream, or NULL on error.
258 * See Also:
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");
267 return NULL;
270 if(chunkLength < 0)
272 SetError("Invalid chunk length");
273 return NULL;
276 if(numBufs < 0)
278 SetError("Invalid buffer count");
279 return NULL;
282 if(length <= 0)
284 SetError("Invalid data length");
285 return NULL;
288 MemDataInfo memData;
289 memData.Data = fdata;
290 memData.Length = length;
291 memData.Pos = 0;
293 alureStream *stream = create_stream(memData);
294 if(!stream->IsValid())
296 delete stream;
297 return NULL;
300 return InitStream(stream, chunkLength, numBufs, bufs);
303 /* Function: alureCreateStreamFromCallback
305 * Creates a stream using the specified callback to retrieve data. Requires an
306 * active context.
308 * Parameters:
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
320 * Returns:
321 * An opaque handle used to control the opened stream, or NULL on error.
323 * See Also:
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");
335 return NULL;
338 if(callback == NULL)
340 SetError("Invalid callback");
341 return NULL;
344 if(chunkLength < 0)
346 SetError("Invalid chunk length");
347 return NULL;
350 if(numBufs < 0)
352 SetError("Invalid buffer count");
353 return NULL;
356 UserCallbacks newcb;
357 newcb.open_file = NULL;
358 newcb.open_mem = NULL;
359 newcb.get_fmt = NULL;
360 newcb.decode = callback;
361 newcb.rewind = NULL;
362 newcb.close = NULL;
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.
373 * Returns:
374 * AL_FALSE on error.
376 * *Version Added*: 1.1
378 ALURE_API ALboolean ALURE_APIENTRY alureGetStreamFormat(alureStream *stream,
379 ALenum *format, ALuint *frequency, ALuint *blockAlign)
381 ALenum _fmt;
382 ALuint _rate, _balign;
384 if(!alureStream::Verify(stream))
386 SetError("Invalid stream pointer");
387 return AL_FALSE;
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");
397 return AL_FALSE;
400 return AL_TRUE;
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.
409 * Returns:
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
412 * reached.
414 ALURE_API ALsizei ALURE_APIENTRY alureBufferDataFromStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
416 if(alGetError() != AL_NO_ERROR)
418 SetError("Existing OpenAL error");
419 return -1;
422 if(!alureStream::Verify(stream))
424 SetError("Invalid stream pointer");
425 return -1;
428 if(numBufs < 0)
430 SetError("Invalid buffer count");
431 return -1;
434 ALenum format;
435 ALuint freq, blockAlign;
437 if(!stream->GetFormat(&format, &freq, &blockAlign))
439 SetError("Could not get stream format");
440 return -1;
443 ALsizei filled;
444 for(filled = 0;filled < numBufs;filled++)
446 ALuint got = stream->GetData(stream->dataChunk, stream->chunkLen);
447 got -= got%blockAlign;
448 if(got == 0) break;
450 alBufferData(bufs[filled], format, stream->dataChunk, got, freq);
451 if(alGetError() != AL_NO_ERROR)
453 SetError("Buffer load failed");
454 return -1;
458 return filled;
461 /* Function: alureRewindStream
463 * Rewinds the stream so that the next alureBufferDataFromStream call will
464 * restart from the beginning of the audio file.
466 * Returns:
467 * AL_FALSE on error.
469 * See Also:
470 * <alureSetStreamOrder>
472 ALURE_API ALboolean ALURE_APIENTRY alureRewindStream(alureStream *stream)
474 if(!alureStream::Verify(stream))
476 SetError("Invalid stream pointer");
477 return AL_FALSE;
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).
489 * Returns:
490 * AL_FALSE on error.
492 * *Version Added*: 1.1
494 * See Also:
495 * <alureRewindStream>
497 ALURE_API ALboolean ALURE_APIENTRY alureSetStreamOrder(alureStream *stream, ALuint order)
499 if(!alureStream::Verify(stream))
501 SetError("Invalid stream pointer");
502 return AL_FALSE;
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.
514 * Returns:
515 * AL_FALSE on error.
517 ALURE_API ALboolean ALURE_APIENTRY alureDestroyStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
519 if(alGetError() != AL_NO_ERROR)
521 SetError("Existing OpenAL error");
522 return AL_FALSE;
525 if(numBufs < 0)
527 SetError("Invalid buffer count");
528 return AL_FALSE;
531 if(stream && !alureStream::Verify(stream))
533 SetError("Invalid stream pointer");
534 return AL_FALSE;
537 alDeleteBuffers(numBufs, bufs);
538 if(alGetError() != AL_NO_ERROR)
540 SetError("Buffer deletion failed");
541 return AL_FALSE;
544 if(stream)
546 StopStream(stream);
547 std::istream *f = stream->fstream;
548 delete stream;
549 delete f;
551 return AL_TRUE;