1 /*
2 * ALURE OpenAL utility library
3 * Copyright (c) 2009 by Chris Robinson.
4 *
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:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
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.
22 */
23
24 /* Title: Streaming */
25
26 #include "config.h"
27
28 #include "main.h"
29
30 #include <string.h>
31
32 #include <memory>
33
34 static bool SizeIsUS = false;
35
InitStream(alureStream * instream,ALsizei chunkLength,ALsizei numBufs,ALuint * bufs)36 static alureStream *InitStream(alureStream *instream, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
37 {
38 std::auto_ptr<std::istream> fstream(instream->fstream);
39 std::auto_ptr<alureStream> stream(instream);
40 ALenum format;
41 ALuint freq, blockAlign;
42
43 if(!stream->GetFormat(&format, &freq, &blockAlign))
44 {
45 SetError("Could not get stream format");
46 return NULL;
47 }
48
49 if(format == AL_NONE || format == -1)
50 {
51 SetError("No valid format");
52 return NULL;
53 }
54 if(blockAlign == 0)
55 {
56 SetError("Invalid block size");
57 return NULL;
58 }
59 if(freq == 0)
60 {
61 SetError("Invalid sample rate");
62 return NULL;
63 }
64
65 if(SizeIsUS)
66 {
67 ALuint framesPerBlock = DetectCompressionRate(format);
68 ALuint blockSize = DetectBlockAlignment(format);
69 if(framesPerBlock == 0 || blockSize == 0)
70 {
71 SetError("Unknown compression rate");
72 return NULL;
73 }
74
75 alureUInt64 len64 = chunkLength;
76 len64 = len64 * freq / 1000000 / framesPerBlock * blockSize;
77 if(len64 > 0x7FFFFFFF)
78 {
79 SetError("Chunk length too large");
80 return NULL;
81 }
82 chunkLength = len64;
83 }
84
85 chunkLength -= chunkLength%blockAlign;
86 if(chunkLength <= 0)
87 {
88 SetError("Chunk length too small");
89 return NULL;
90 }
91
92 stream->dataChunk.resize(chunkLength);
93
94 if(numBufs > 0)
95 {
96 alGenBuffers(numBufs, bufs);
97 if(alGetError() != AL_NO_ERROR)
98 {
99 SetError("Buffer creation failed");
100 return NULL;
101 }
102 }
103
104 ALsizei filled;
105 for(filled = 0;filled < numBufs;filled++)
106 {
107 ALuint got = stream->GetData(&stream->dataChunk[0], stream->dataChunk.size());
108 got -= got%blockAlign;
109 if(got == 0) break;
110
111 alBufferData(bufs[filled], format, &stream->dataChunk[0], got, freq);
112 }
113
114 while(filled < numBufs)
115 {
116 alBufferData(bufs[filled], format, &stream->dataChunk[0], 0, freq);
117 filled++;
118 }
119 if(alGetError() != AL_NO_ERROR)
120 {
121 alDeleteBuffers(numBufs, bufs);
122 alGetError();
123
124 SetError("Buffering error");
125 return NULL;
126 }
127
128 fstream.release();
129 return stream.release();
130 }
131
132
133 extern "C" {
134
135 /* Function: alureStreamSizeIsMicroSec
136 *
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.
142 *
143 * Returns:
144 * Previously set value.
145 *
146 * *Version Added*: 1.1
147 *
148 * See Also:
149 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
150 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
151 */
alureStreamSizeIsMicroSec(ALboolean useUS)152 ALURE_API ALboolean ALURE_APIENTRY alureStreamSizeIsMicroSec(ALboolean useUS)
153 {
154 ALboolean old = (SizeIsUS ? AL_TRUE : AL_FALSE);
155 SizeIsUS = !!useUS;
156 return old;
157 }
158
159 /* Function: alureCreateStreamFromFile
160 *
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.
167 *
168 * Returns:
169 * An opaque handle used to control the opened stream, or NULL on error.
170 *
171 * See Also:
172 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromMemory>,
173 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
174 */
alureCreateStreamFromFile(const ALchar * fname,ALsizei chunkLength,ALsizei numBufs,ALuint * bufs)175 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromFile(const ALchar *fname, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
176 {
177 if(alGetError() != AL_NO_ERROR)
178 {
179 SetError("Existing OpenAL error");
180 return NULL;
181 }
182
183 if(chunkLength < 0)
184 {
185 SetError("Invalid chunk length");
186 return NULL;
187 }
188
189 if(numBufs < 0)
190 {
191 SetError("Invalid buffer count");
192 return NULL;
193 }
194
195 alureStream *stream = create_stream(fname);
196 if(!stream) return NULL;
197
198 return InitStream(stream, chunkLength, numBufs, bufs);
199 }
200
201 /* Function: alureCreateStreamFromMemory
202 *
203 * Opens a file image from memory and sets it up for streaming, similar to
204 * <alureCreateStreamFromFile>. The given data buffer can be safely deleted
205 * after calling this function. Requires an active context.
206 *
207 * Returns:
208 * An opaque handle used to control the opened stream, or NULL on error.
209 *
210 * See Also:
211 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
212 * <alureCreateStreamFromStaticMemory>, <alureCreateStreamFromCallback>
213 */
alureCreateStreamFromMemory(const ALubyte * fdata,ALuint length,ALsizei chunkLength,ALsizei numBufs,ALuint * bufs)214 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromMemory(const ALubyte *fdata, ALuint length, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
215 {
216 if(alGetError() != AL_NO_ERROR)
217 {
218 SetError("Existing OpenAL error");
219 return NULL;
220 }
221
222 if(chunkLength < 0)
223 {
224 SetError("Invalid chunk length");
225 return NULL;
226 }
227
228 if(numBufs < 0)
229 {
230 SetError("Invalid buffer count");
231 return NULL;
232 }
233
234 if(length <= 0)
235 {
236 SetError("Invalid data length");
237 return NULL;
238 }
239
240 ALubyte *streamData = new ALubyte[length];
241 memcpy(streamData, fdata, length);
242
243 MemDataInfo memData;
244 memData.Data = streamData;
245 memData.Length = length;
246 memData.Pos = 0;
247
248 alureStream *stream = create_stream(memData);
249 if(!stream) return NULL;
250
251 stream->data = streamData;
252 return InitStream(stream, chunkLength, numBufs, bufs);
253 }
254
255 /* Function: alureCreateStreamFromStaticMemory
256 *
257 * Identical to <alureCreateStreamFromMemory>, except the given memory is used
258 * directly and not duplicated. As a consequence, the data buffer must remain
259 * valid while the stream is alive. Requires an active context.
260 *
261 * Returns:
262 * An opaque handle used to control the opened stream, or NULL on error.
263 *
264 * See Also:
265 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
266 * <alureCreateStreamFromMemory>, <alureCreateStreamFromCallback>
267 */
alureCreateStreamFromStaticMemory(const ALubyte * fdata,ALuint length,ALsizei chunkLength,ALsizei numBufs,ALuint * bufs)268 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromStaticMemory(const ALubyte *fdata, ALuint length, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
269 {
270 if(alGetError() != AL_NO_ERROR)
271 {
272 SetError("Existing OpenAL error");
273 return NULL;
274 }
275
276 if(chunkLength < 0)
277 {
278 SetError("Invalid chunk length");
279 return NULL;
280 }
281
282 if(numBufs < 0)
283 {
284 SetError("Invalid buffer count");
285 return NULL;
286 }
287
288 if(length <= 0)
289 {
290 SetError("Invalid data length");
291 return NULL;
292 }
293
294 MemDataInfo memData;
295 memData.Data = fdata;
296 memData.Length = length;
297 memData.Pos = 0;
298
299 alureStream *stream = create_stream(memData);
300 if(!stream) return NULL;
301
302 return InitStream(stream, chunkLength, numBufs, bufs);
303 }
304
305 /* Function: alureCreateStreamFromCallback
306 *
307 * Creates a stream using the specified callback to retrieve data. Requires an
308 * active context.
309 *
310 * Parameters:
311 * callback - This is called when more data is needed from the stream. Up to
312 * the specified number of bytes should be written to the data
313 * pointer, and the number of bytes actually written should be
314 * returned. The number of bytes written must be block aligned for
315 * the format (eg. a multiple of 4 for AL_FORMAT_STEREO16), or an
316 * OpenAL error may occur during buffering.
317 * userdata - A handle passed through to the callback.
318 * format - The format of the data the callback will be giving. The format must
319 * be valid for the context.
320 * samplerate - The sample rate (frequency) of the stream
321 *
322 * Returns:
323 * An opaque handle used to control the opened stream, or NULL on error.
324 *
325 * See Also:
326 * <alureStreamSizeIsMicroSec>, <alureCreateStreamFromFile>,
327 * <alureCreateStreamFromMemory>, <alureCreateStreamFromStaticMemory>
328 */
alureCreateStreamFromCallback(ALuint (* callback)(void * userdata,ALubyte * data,ALuint bytes),void * userdata,ALenum format,ALuint samplerate,ALsizei chunkLength,ALsizei numBufs,ALuint * bufs)329 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromCallback(
330 ALuint (*callback)(void *userdata, ALubyte *data, ALuint bytes),
331 void *userdata, ALenum format, ALuint samplerate,
332 ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
333 {
334 if(alGetError() != AL_NO_ERROR)
335 {
336 SetError("Existing OpenAL error");
337 return NULL;
338 }
339
340 if(callback == NULL)
341 {
342 SetError("Invalid callback");
343 return NULL;
344 }
345
346 if(chunkLength < 0)
347 {
348 SetError("Invalid chunk length");
349 return NULL;
350 }
351
352 if(numBufs < 0)
353 {
354 SetError("Invalid buffer count");
355 return NULL;
356 }
357
358 UserCallbacks newcb;
359 newcb.open_file = NULL;
360 newcb.open_mem = NULL;
361 newcb.get_fmt = NULL;
362 newcb.decode = callback;
363 newcb.rewind = NULL;
364 newcb.close = NULL;
365
366 alureStream *stream = create_stream(userdata, format, samplerate, newcb);
367 return InitStream(stream, chunkLength, numBufs, bufs);
368 }
369
370 /* Function: alureGetStreamFrequency
371 *
372 * Retrieves the frequency used by the given stream.
373 *
374 * Returns:
375 * 0 on error.
376 *
377 * *Version Added*: 1.1
378 */
alureGetStreamFrequency(alureStream * stream)379 ALURE_API ALsizei ALURE_APIENTRY alureGetStreamFrequency(alureStream *stream)
380 {
381 ALenum format;
382 ALuint rate, balign;
383
384 if(!alureStream::Verify(stream))
385 {
386 SetError("Invalid stream pointer");
387 return 0;
388 }
389
390 if(!stream->GetFormat(&format, &rate, &balign))
391 {
392 SetError("Could not get stream format");
393 return 0;
394 }
395
396 return rate;
397 }
398
399 /* Function: alureBufferDataFromStream
400 *
401 * Buffers the given buffer objects with the next chunks of data from the
402 * stream. The given buffer objects do not need to be ones given by the
403 * alureCreateStream functions. Requires an active context.
404 *
405 * Returns:
406 * The number of buffers filled with new data, or -1 on error. If the value
407 * returned is less than the number requested, the end of the stream has been
408 * reached.
409 */
alureBufferDataFromStream(alureStream * stream,ALsizei numBufs,ALuint * bufs)410 ALURE_API ALsizei ALURE_APIENTRY alureBufferDataFromStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
411 {
412 if(alGetError() != AL_NO_ERROR)
413 {
414 SetError("Existing OpenAL error");
415 return -1;
416 }
417
418 if(!alureStream::Verify(stream))
419 {
420 SetError("Invalid stream pointer");
421 return -1;
422 }
423
424 if(numBufs < 0)
425 {
426 SetError("Invalid buffer count");
427 return -1;
428 }
429
430 for(ALsizei i = 0;i < numBufs;i++)
431 {
432 if(!bufs[i] || !alIsBuffer(bufs[i]))
433 {
434 SetError("Invalid buffer ID");
435 return -1;
436 }
437 }
438
439 ALenum format;
440 ALuint freq, blockAlign;
441
442 if(!stream->GetFormat(&format, &freq, &blockAlign))
443 {
444 SetError("Could not get stream format");
445 return -1;
446 }
447
448 ALsizei filled;
449 for(filled = 0;filled < numBufs;filled++)
450 {
451 ALuint got = stream->GetData(&stream->dataChunk[0], stream->dataChunk.size());
452 got -= got%blockAlign;
453 if(got == 0) break;
454
455 alBufferData(bufs[filled], format, &stream->dataChunk[0], got, freq);
456 if(alGetError() != AL_NO_ERROR)
457 {
458 SetError("Buffer load failed");
459 return -1;
460 }
461 }
462
463 return filled;
464 }
465
466 /* Function: alureRewindStream
467 *
468 * Rewinds the stream so that the next alureBufferDataFromStream call will
469 * restart from the beginning of the audio file.
470 *
471 * Returns:
472 * AL_FALSE on error.
473 *
474 * See Also:
475 * <alureSetStreamOrder>
476 */
alureRewindStream(alureStream * stream)477 ALURE_API ALboolean ALURE_APIENTRY alureRewindStream(alureStream *stream)
478 {
479 if(!alureStream::Verify(stream))
480 {
481 SetError("Invalid stream pointer");
482 return AL_FALSE;
483 }
484
485 return stream->Rewind();
486 }
487
488 /* Function: alureSetStreamOrder
489 *
490 * Skips the module decoder to the specified order, so following buffering
491 * calls will decode from the specified order. For non-module formats, setting
492 * order 0 is identical to rewinding the stream (other orders will fail).
493 *
494 * Returns:
495 * AL_FALSE on error.
496 *
497 * *Version Added*: 1.1
498 *
499 * See Also:
500 * <alureRewindStream>
501 */
alureSetStreamOrder(alureStream * stream,ALuint order)502 ALURE_API ALboolean ALURE_APIENTRY alureSetStreamOrder(alureStream *stream, ALuint order)
503 {
504 if(!alureStream::Verify(stream))
505 {
506 SetError("Invalid stream pointer");
507 return AL_FALSE;
508 }
509
510 return stream->SetOrder(order);
511 }
512
513 /* Function: alureSetStreamPatchset
514 *
515 * Specifies the patchset to use for MIDI streams. By default, the FluidSynth
516 * decoder will look for one in the FLUID_SOUNDFONT environment variable, but
517 * this can be used to change it to something different. On non-MIDI streams,
518 * this has no effect.
519 *
520 * Returns:
521 * AL_FALSE on error.
522 *
523 * *Version Added*: 1.1
524 */
alureSetStreamPatchset(alureStream * stream,const ALchar * patchset)525 ALURE_API ALboolean ALURE_APIENTRY alureSetStreamPatchset(alureStream *stream, const ALchar *patchset)
526 {
527 if(!alureStream::Verify(stream))
528 {
529 SetError("Invalid stream pointer");
530 return AL_FALSE;
531 }
532
533 return stream->SetPatchset(patchset);
534 }
535
536 /* Function: alureGetStreamLength
537 *
538 * Retrieves an approximate number of samples for the stream. Not all streams
539 * or decoders can return such info, and may return 0 if the stream length is
540 * unknown.
541 *
542 * Returns:
543 * -1 on error.
544 *
545 * *Version Added*: 1.2
546 */
alureGetStreamLength(alureStream * stream)547 ALURE_API alureInt64 ALURE_APIENTRY alureGetStreamLength(alureStream *stream)
548 {
549 if(!alureStream::Verify(stream))
550 {
551 SetError("Invalid stream pointer");
552 return -1;
553 }
554
555 return stream->GetLength();
556 }
557
558 /* Function: alureDestroyStream
559 *
560 * Closes an opened stream. For convenience, it will also delete the given
561 * buffer objects. The given buffer objects do not need to be ones given by the
562 * alureCreateStream functions. Requires an active context.
563 *
564 * Returns:
565 * AL_FALSE on error.
566 */
alureDestroyStream(alureStream * stream,ALsizei numBufs,ALuint * bufs)567 ALURE_API ALboolean ALURE_APIENTRY alureDestroyStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
568 {
569 if(alGetError() != AL_NO_ERROR)
570 {
571 SetError("Existing OpenAL error");
572 return AL_FALSE;
573 }
574
575 if(numBufs < 0)
576 {
577 SetError("Invalid buffer count");
578 return AL_FALSE;
579 }
580
581 if(stream && !alureStream::Verify(stream))
582 {
583 SetError("Invalid stream pointer");
584 return AL_FALSE;
585 }
586
587 if(numBufs > 0)
588 {
589 alDeleteBuffers(numBufs, bufs);
590 if(alGetError() != AL_NO_ERROR)
591 {
592 SetError("Buffer deletion failed");
593 return AL_FALSE;
594 }
595 }
596
597 if(stream)
598 {
599 StopStream(stream);
600 std::istream *f = stream->fstream;
601 delete stream;
602 delete f;
603 }
604 return AL_TRUE;
605 }
606
607 }
608