Move the decoder classes to main.h
[alure.git] / src / stream.cpp
blobeb6a301ba777d7d202f5d33426fe64fd89a503e2
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<std::istream> fstream(instream->fstream);
39 std::auto_ptr<alureStream> stream(instream);
40 ALenum format;
41 ALuint freq, blockAlign;
43 if(!stream->GetFormat(&format, &freq, &blockAlign))
45 SetError("Could not get stream format");
46 return NULL;
49 if(format == AL_NONE)
51 SetError("No valid format");
52 return NULL;
54 if(blockAlign == 0)
56 SetError("Invalid block size");
57 return NULL;
59 if(freq == 0)
61 SetError("Invalid sample rate");
62 return NULL;
65 if(SizeIsUS)
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");
73 return NULL;
76 len64 = len64 * freq / 1000000 / framesPerBlock * blockSize;
77 if(len64 > 0x7FFFFFFF)
79 SetError("Chunk length too large");
80 return NULL;
82 chunkLength = len64;
85 chunkLength -= chunkLength%blockAlign;
86 if(chunkLength <= 0)
88 SetError("Chunk length too small");
89 return NULL;
92 stream->dataChunk.resize(chunkLength);
94 if(numBufs > 0)
96 alGenBuffers(numBufs, bufs);
97 if(alGetError() != AL_NO_ERROR)
99 SetError("Buffer creation failed");
100 return NULL;
104 ALsizei filled;
105 for(filled = 0;filled < numBufs;filled++)
107 ALuint got = stream->GetData(&stream->dataChunk[0], stream->dataChunk.size());
108 got -= got%blockAlign;
109 if(got == 0) break;
111 alBufferData(bufs[filled], format, &stream->dataChunk[0], got, freq);
114 while(filled < numBufs)
116 alBufferData(bufs[filled], format, &stream->dataChunk[0], 0, freq);
117 filled++;
119 if(alGetError() != AL_NO_ERROR)
121 alDeleteBuffers(numBufs, bufs);
122 alGetError();
124 SetError("Buffering error");
125 return NULL;
128 fstream.release();
129 return stream.release();
133 extern "C" {
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.
143 * Returns:
144 * Previously set value.
146 * *Version Added*: 1.1
148 * See Also:
149 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
150 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
152 ALURE_API ALboolean ALURE_APIENTRY alureStreamSizeIsMicroSec(ALboolean useUS)
154 ALboolean old = (SizeIsUS ? AL_TRUE : AL_FALSE);
155 SizeIsUS = !!useUS;
156 return old;
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.
168 * Returns:
169 * An opaque handle used to control the opened stream, or NULL on error.
171 * See Also:
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");
180 return NULL;
183 if(chunkLength < 0)
185 SetError("Invalid chunk length");
186 return NULL;
189 if(numBufs < 0)
191 SetError("Invalid buffer count");
192 return NULL;
195 alureStream *stream = create_stream(fname);
196 if(!stream->IsValid())
198 delete stream;
199 return NULL;
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.
211 * Returns:
212 * An opaque handle used to control the opened stream, or NULL on error.
214 * See Also:
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");
223 return NULL;
226 if(chunkLength < 0)
228 SetError("Invalid chunk length");
229 return NULL;
232 if(numBufs < 0)
234 SetError("Invalid buffer count");
235 return NULL;
238 if(length <= 0)
240 SetError("Invalid data length");
241 return NULL;
244 ALubyte *streamData = new ALubyte[length];
245 memcpy(streamData, fdata, length);
247 MemDataInfo memData;
248 memData.Data = streamData;
249 memData.Length = length;
250 memData.Pos = 0;
252 alureStream *stream = create_stream(memData);
253 stream->data = streamData;
254 if(!stream->IsValid())
256 delete stream;
257 return NULL;
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.
269 * Returns:
270 * An opaque handle used to control the opened stream, or NULL on error.
272 * See Also:
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");
281 return NULL;
284 if(chunkLength < 0)
286 SetError("Invalid chunk length");
287 return NULL;
290 if(numBufs < 0)
292 SetError("Invalid buffer count");
293 return NULL;
296 if(length <= 0)
298 SetError("Invalid data length");
299 return NULL;
302 MemDataInfo memData;
303 memData.Data = fdata;
304 memData.Length = length;
305 memData.Pos = 0;
307 alureStream *stream = create_stream(memData);
308 if(!stream->IsValid())
310 delete stream;
311 return NULL;
314 return InitStream(stream, chunkLength, numBufs, bufs);
317 /* Function: alureCreateStreamFromCallback
319 * Creates a stream using the specified callback to retrieve data. Requires an
320 * active context.
322 * Parameters:
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
334 * Returns:
335 * An opaque handle used to control the opened stream, or NULL on error.
337 * See Also:
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");
349 return NULL;
352 if(callback == NULL)
354 SetError("Invalid callback");
355 return NULL;
358 if(chunkLength < 0)
360 SetError("Invalid chunk length");
361 return NULL;
364 if(numBufs < 0)
366 SetError("Invalid buffer count");
367 return NULL;
370 UserCallbacks newcb;
371 newcb.open_file = NULL;
372 newcb.open_mem = NULL;
373 newcb.get_fmt = NULL;
374 newcb.decode = callback;
375 newcb.rewind = NULL;
376 newcb.close = NULL;
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.
386 * Returns:
387 * 0 on error.
389 * *Version Added*: 1.1
391 ALURE_API ALsizei ALURE_APIENTRY alureGetStreamFrequency(alureStream *stream)
393 ALenum format;
394 ALuint rate, balign;
396 if(!alureStream::Verify(stream))
398 SetError("Invalid stream pointer");
399 return 0;
402 if(!stream->GetFormat(&format, &rate, &balign))
404 SetError("Could not get stream format");
405 return 0;
408 return rate;
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.
417 * Returns:
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
420 * reached.
422 ALURE_API ALsizei ALURE_APIENTRY alureBufferDataFromStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
424 if(alGetError() != AL_NO_ERROR)
426 SetError("Existing OpenAL error");
427 return -1;
430 if(!alureStream::Verify(stream))
432 SetError("Invalid stream pointer");
433 return -1;
436 if(numBufs < 0)
438 SetError("Invalid buffer count");
439 return -1;
442 ALenum format;
443 ALuint freq, blockAlign;
445 if(!stream->GetFormat(&format, &freq, &blockAlign))
447 SetError("Could not get stream format");
448 return -1;
451 ALsizei filled;
452 for(filled = 0;filled < numBufs;filled++)
454 ALuint got = stream->GetData(&stream->dataChunk[0], stream->dataChunk.size());
455 got -= got%blockAlign;
456 if(got == 0) break;
458 alBufferData(bufs[filled], format, &stream->dataChunk[0], got, freq);
459 if(alGetError() != AL_NO_ERROR)
461 SetError("Buffer load failed");
462 return -1;
466 return filled;
469 /* Function: alureRewindStream
471 * Rewinds the stream so that the next alureBufferDataFromStream call will
472 * restart from the beginning of the audio file.
474 * Returns:
475 * AL_FALSE on error.
477 * See Also:
478 * <alureSetStreamOrder>
480 ALURE_API ALboolean ALURE_APIENTRY alureRewindStream(alureStream *stream)
482 if(!alureStream::Verify(stream))
484 SetError("Invalid stream pointer");
485 return AL_FALSE;
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).
497 * Returns:
498 * AL_FALSE on error.
500 * *Version Added*: 1.1
502 * See Also:
503 * <alureRewindStream>
505 ALURE_API ALboolean ALURE_APIENTRY alureSetStreamOrder(alureStream *stream, ALuint order)
507 if(!alureStream::Verify(stream))
509 SetError("Invalid stream pointer");
510 return AL_FALSE;
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.
523 * Returns:
524 * AL_FALSE on error.
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");
533 return AL_FALSE;
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.
545 * Returns:
546 * AL_FALSE on error.
548 ALURE_API ALboolean ALURE_APIENTRY alureDestroyStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
550 if(alGetError() != AL_NO_ERROR)
552 SetError("Existing OpenAL error");
553 return AL_FALSE;
556 if(numBufs < 0)
558 SetError("Invalid buffer count");
559 return AL_FALSE;
562 if(stream && !alureStream::Verify(stream))
564 SetError("Invalid stream pointer");
565 return AL_FALSE;
568 alDeleteBuffers(numBufs, bufs);
569 if(alGetError() != AL_NO_ERROR)
571 SetError("Buffer deletion failed");
572 return AL_FALSE;
575 if(stream)
577 StopStream(stream);
578 std::istream *f = stream->fstream;
579 delete stream;
580 delete f;
582 return AL_TRUE;