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