Avoid more Alure function calls
[alure.git] / src / stream.cpp
blobfce6ff6c47ba686f647617897b78c9f2f270e956
1 /*
2 * ALURE OpenAL utility library
3 * Copyright (C) 2009 by Chris Robinson.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 /* Title: Streaming */
23 #include "config.h"
25 #include "main.h"
27 #include <string.h>
29 #include <memory>
31 static alureStream *InitStream(alureStream *instream, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
33 std::auto_ptr<alureStream> stream(instream);
34 ALenum format;
35 ALuint freq, blockAlign;
37 if(!stream->GetFormat(&format, &freq, &blockAlign))
39 SetError("Could not get stream format");
40 return NULL;
43 if(format == AL_NONE)
45 SetError("No valid format");
46 return NULL;
48 if(blockAlign == 0)
50 SetError("Invalid block size");
51 return NULL;
53 if(freq == 0)
55 SetError("Invalid sample rate");
56 return NULL;
59 chunkLength -= chunkLength%blockAlign;
60 if(chunkLength <= 0)
62 SetError("Chunk length too small");
63 return NULL;
66 stream->chunkLen = chunkLength;
67 stream->dataChunk = new ALubyte[stream->chunkLen];
69 alGenBuffers(numBufs, bufs);
70 if(alGetError() != AL_NO_ERROR)
72 SetError("Buffer creation failed");
73 return NULL;
76 ALsizei filled;
77 for(filled = 0;filled < numBufs;filled++)
79 ALuint got = stream->GetData(stream->dataChunk, stream->chunkLen);
80 got -= got%blockAlign;
81 if(got == 0) break;
83 alBufferData(bufs[filled], format, stream->dataChunk, got, freq);
84 if(alGetError() != AL_NO_ERROR)
86 alDeleteBuffers(numBufs, bufs);
87 alGetError();
89 SetError("Buffering error");
90 return NULL;
94 while(filled < numBufs)
96 alBufferData(bufs[filled], format, stream->dataChunk, 0, freq);
97 if(alGetError() != AL_NO_ERROR)
99 SetError("Buffer load failed");
100 return NULL;
102 filled++;
105 return stream.release();
109 extern "C" {
111 /* Function: alureCreateStreamFromFile
113 * Opens a file and sets it up for streaming. The given chunkLength is the
114 * number of bytes each buffer will fill with. ALURE will optionally generate
115 * the specified number of buffer objects, fill them with the beginning of the
116 * data, then place the new IDs into the provided storage, before returning.
117 * Requires an active context.
119 * Returns:
120 * An opaque handle used to control the opened stream, or NULL on error.
122 * See Also:
123 * <alureCreateStreamFromMemory>, <alureCreateStreamFromStaticMemory>,
124 * <alureCreateStreamFromCallback>
126 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromFile(const ALchar *fname, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
128 if(alGetError() != AL_NO_ERROR)
130 SetError("Existing OpenAL error");
131 return NULL;
134 if(chunkLength < 0)
136 SetError("Invalid chunk length");
137 return NULL;
140 if(numBufs < 0)
142 SetError("Invalid buffer count");
143 return NULL;
146 alureStream *stream = create_stream(fname);
147 if(!stream->IsValid())
149 delete stream;
150 return NULL;
153 return InitStream(stream, chunkLength, numBufs, bufs);
156 /* Function: alureCreateStreamFromMemory
158 * Opens a file image from memory and sets it up for streaming, similar to
159 * alureCreateStreamFromFile. The given data buffer can be safely deleted after
160 * calling this function. Requires an active context.
162 * Returns:
163 * An opaque handle used to control the opened stream, or NULL on error.
165 * See Also:
166 * <alureCreateStreamFromFile>, <alureCreateStreamFromStaticMemory>,
167 * <alureCreateStreamFromCallback>
169 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromMemory(const ALubyte *fdata, ALuint length, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
171 if(alGetError() != AL_NO_ERROR)
173 SetError("Existing OpenAL error");
174 return NULL;
177 if(chunkLength < 0)
179 SetError("Invalid chunk length");
180 return NULL;
183 if(numBufs < 0)
185 SetError("Invalid buffer count");
186 return NULL;
189 if(length <= 0)
191 SetError("Invalid data length");
192 return NULL;
195 ALubyte *streamData = new ALubyte[length];
196 memcpy(streamData, fdata, length);
198 MemDataInfo memData;
199 memData.Data = streamData;
200 memData.Length = length;
201 memData.Pos = 0;
203 alureStream *stream = create_stream(memData);
204 stream->data = streamData;
205 if(!stream->IsValid())
207 delete stream;
208 return NULL;
211 return InitStream(stream, chunkLength, numBufs, bufs);
214 /* Function: alureCreateStreamFromStaticMemory
216 * Identical to alureCreateStreamFromMemory, except the given memory is used
217 * directly and not duplicated. As a consequence, the data buffer must remain
218 * valid while the stream is alive. Requires an active context.
220 * Returns:
221 * An opaque handle used to control the opened stream, or NULL on error.
223 * See Also:
224 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
225 * <alureCreateStreamFromCallback>
227 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromStaticMemory(const ALubyte *fdata, ALuint length, ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
229 if(alGetError() != AL_NO_ERROR)
231 SetError("Existing OpenAL error");
232 return NULL;
235 if(chunkLength < 0)
237 SetError("Invalid chunk length");
238 return NULL;
241 if(numBufs < 0)
243 SetError("Invalid buffer count");
244 return NULL;
247 if(length <= 0)
249 SetError("Invalid data length");
250 return NULL;
253 MemDataInfo memData;
254 memData.Data = fdata;
255 memData.Length = length;
256 memData.Pos = 0;
258 alureStream *stream = create_stream(memData);
259 if(!stream->IsValid())
261 delete stream;
262 return NULL;
265 return InitStream(stream, chunkLength, numBufs, bufs);
268 /* Function: alureCreateStreamFromCallback
270 * Creates a stream using the specified callback to retrieve data. Requires an
271 * active context.
273 * Parameters:
274 * callback - This is called when more data is needed from the stream. Up to
275 * the specified number of bytes should be written to the data
276 * pointer, and the number of bytes actually written should be
277 * returned. The number of bytes written must be block aligned for
278 * the format (eg. a multiple of 4 for AL_FORMAT_STEREO16), or an
279 * OpenAL error may occur during buffering.
280 * userdata - A handle passed through to the callback.
281 * format - The format of the data the callback will be giving. The format must
282 * be valid for the context.
283 * samplerate - The sample rate (frequency) of the stream
285 * Returns:
286 * An opaque handle used to control the opened stream, or NULL on error.
288 * See Also:
289 * <alureCreateStreamFromFile>, <alureCreateStreamFromMemory>,
290 * <alureCreateStreamFromStaticMemory>
292 ALURE_API alureStream* ALURE_APIENTRY alureCreateStreamFromCallback(
293 ALuint (*callback)(void *userdata, ALubyte *data, ALuint bytes),
294 void *userdata, ALenum format, ALuint samplerate,
295 ALsizei chunkLength, ALsizei numBufs, ALuint *bufs)
297 if(alGetError() != AL_NO_ERROR)
299 SetError("Existing OpenAL error");
300 return NULL;
303 if(callback == NULL)
305 SetError("Invalid callback");
306 return NULL;
309 if(chunkLength < 0)
311 SetError("Invalid chunk length");
312 return NULL;
315 if(numBufs < 0)
317 SetError("Invalid buffer count");
318 return NULL;
321 UserCallbacks newcb;
322 newcb.open_file = NULL;
323 newcb.open_mem = NULL;
324 newcb.get_fmt = NULL;
325 newcb.decode = callback;
326 newcb.rewind = NULL;
327 newcb.close = NULL;
329 customStream *stream = new customStream(userdata, format, samplerate, newcb);
330 return InitStream(stream, chunkLength, numBufs, bufs);
333 /* Function: alureGetStreamFormat
335 * Retrieves the format, frequency, and block-alignment used for the given
336 * stream. If a parameter is NULL, that value will not be returned.
338 * Returns:
339 * AL_FALSE on error.
341 ALURE_API ALboolean ALURE_APIENTRY alureGetStreamFormat(alureStream *stream,
342 ALenum *format, ALuint *frequency, ALuint *blockAlign)
344 ALenum _fmt;
345 ALuint _rate, _balign;
347 if(!alureStream::Verify(stream))
349 SetError("Invalid stream pointer");
350 return AL_FALSE;
353 if(!format) format = &_fmt;
354 if(!frequency) frequency = &_rate;
355 if(!blockAlign) blockAlign = &_balign;
357 if(!stream->GetFormat(format, frequency, blockAlign))
359 SetError("Could not get stream format");
360 return AL_FALSE;
363 return AL_TRUE;
366 /* Function: alureBufferDataFromStream
368 * Buffers the given buffer objects with the next chunks of data from the
369 * stream. The given buffer objects do not need to be ones given by the
370 * alureCreateStreamFrom* functions. Requires an active context.
372 * Returns:
373 * The number of buffers filled with new data, or -1 on error. If the value
374 * returned is less than the number requested, the end of the stream has been
375 * reached.
377 ALURE_API ALsizei ALURE_APIENTRY alureBufferDataFromStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
379 if(alGetError() != AL_NO_ERROR)
381 SetError("Existing OpenAL error");
382 return -1;
385 if(!alureStream::Verify(stream))
387 SetError("Invalid stream pointer");
388 return -1;
391 if(numBufs < 0)
393 SetError("Invalid buffer count");
394 return -1;
397 ALenum format;
398 ALuint freq, blockAlign;
400 if(!stream->GetFormat(&format, &freq, &blockAlign))
402 SetError("Could not get stream format");
403 return -1;
406 ALsizei filled;
407 for(filled = 0;filled < numBufs;filled++)
409 ALuint got = stream->GetData(stream->dataChunk, stream->chunkLen);
410 got -= got%blockAlign;
411 if(got == 0) break;
413 alBufferData(bufs[filled], format, stream->dataChunk, got, freq);
414 if(alGetError() != AL_NO_ERROR)
416 SetError("Buffer load failed");
417 return -1;
421 return filled;
424 /* Function: alureRewindStream
426 * Rewinds the stream so that the next alureBufferDataFromStream call will
427 * restart from the beginning of the audio file.
429 * Returns:
430 * AL_FALSE on error.
432 * See Also:
433 * <alureSetStreamOrder>
435 ALURE_API ALboolean ALURE_APIENTRY alureRewindStream(alureStream *stream)
437 if(!alureStream::Verify(stream))
439 SetError("Invalid stream pointer");
440 return AL_FALSE;
443 return stream->Rewind();
446 /* Function: alureSetStreamOrder
448 * Skips the module decoder to the specified order, so following buffering
449 * calls will decode from the specified order. For non-module formats, setting
450 * order 0 is identical to rewinding the stream (other orders will fail).
452 * Returns:
453 * AL_FALSE on error.
455 * See Also:
456 * <alureRewindStream>
458 ALURE_API ALboolean ALURE_APIENTRY alureSetStreamOrder(alureStream *stream, ALuint order)
460 if(!alureStream::Verify(stream))
462 SetError("Invalid stream pointer");
463 return AL_FALSE;
466 return stream->SetOrder(order);
469 /* Function: alureDestroyStream
471 * Closes an opened stream. For convenience, it will also delete the given
472 * buffer objects. The given buffer objects do not need to be ones given by the
473 * alureCreateStreamFrom* functions. Requires an active context.
475 * Returns:
476 * AL_FALSE on error.
478 ALURE_API ALboolean ALURE_APIENTRY alureDestroyStream(alureStream *stream, ALsizei numBufs, ALuint *bufs)
480 if(alGetError() != AL_NO_ERROR)
482 SetError("Existing OpenAL error");
483 return AL_FALSE;
486 if(numBufs < 0)
488 SetError("Invalid buffer count");
489 return AL_FALSE;
492 if(stream && !alureStream::Verify(stream))
494 SetError("Invalid stream pointer");
495 return AL_FALSE;
498 alDeleteBuffers(numBufs, bufs);
499 if(alGetError() != AL_NO_ERROR)
501 SetError("Buffer deletion failed");
502 return AL_FALSE;
505 if(stream)
507 StopStream(stream);
508 std::istream *f = stream->fstream;
509 delete stream; delete f;
511 return AL_TRUE;