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: Automatic Playback */
25
26 #include "config.h"
27
28 #include "main.h"
29
30 #include <list>
31 #include <vector>
32
33 #ifdef HAVE_WINDOWS_H
34
35 typedef struct {
36 ALuint (*func)(ALvoid*);
37 ALvoid *ptr;
38 HANDLE thread;
39 } ThreadInfo;
40
StarterFunc(void * ptr)41 static DWORD CALLBACK StarterFunc(void *ptr)
42 {
43 ThreadInfo *inf = (ThreadInfo*)ptr;
44 ALint ret;
45
46 ret = inf->func(inf->ptr);
47 ExitThread((DWORD)ret);
48
49 return (DWORD)ret;
50 }
51
StartThread(ALuint (* func)(ALvoid *),ALvoid * ptr)52 static ThreadInfo *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
53 {
54 DWORD dummy;
55 ThreadInfo *inf = new ThreadInfo;
56 inf->func = func;
57 inf->ptr = ptr;
58
59 inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, &dummy);
60 if(!inf->thread)
61 {
62 delete inf;
63 return NULL;
64 }
65
66 return inf;
67 }
68
StopThread(ThreadInfo * inf)69 static ALuint StopThread(ThreadInfo *inf)
70 {
71 WaitForSingleObject(inf->thread, INFINITE);
72 CloseHandle(inf->thread);
73 delete inf;
74
75 return 0;
76 }
77
78 #else
79
80 typedef struct {
81 ALuint (*func)(ALvoid*);
82 ALvoid *ptr;
83 pthread_t thread;
84 } ThreadInfo;
85
StarterFunc(void * ptr)86 static void *StarterFunc(void *ptr)
87 {
88 ThreadInfo *inf = (ThreadInfo*)ptr;
89 void *ret = (void*)(inf->func(inf->ptr));
90 return ret;
91 }
92
StartThread(ALuint (* func)(ALvoid *),ALvoid * ptr)93 static ThreadInfo *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
94 {
95 ThreadInfo *inf = new ThreadInfo;
96 inf->func = func;
97 inf->ptr = ptr;
98
99 if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0)
100 {
101 delete inf;
102 return NULL;
103 }
104
105 return inf;
106 }
107
StopThread(ThreadInfo * inf)108 static ALuint StopThread(ThreadInfo *inf)
109 {
110 pthread_join(inf->thread, NULL);
111 delete inf;
112
113 return 0;
114 }
115
116 #endif
117
118 // This object is used to make sure the current context isn't switched out on
119 // us by another thread, by setting the current thread context to the current
120 // context. The old thread context is then restored when the object goes out
121 // of scope.
122 // This obviously only works when ALC_EXT_thread_local_context is supported
123 struct ProtectContext {
ProtectContextProtectContext124 ProtectContext()
125 { protect(); }
126
~ProtectContextProtectContext127 ~ProtectContext()
128 { unprotect(); }
129
protectProtectContext130 void protect()
131 {
132 old_ctx = (alcGetThreadContext ? alcGetThreadContext() : NULL);
133 if(alcSetThreadContext)
134 alcSetThreadContext(alcGetCurrentContext());
135 }
136
unprotectProtectContext137 void unprotect()
138 {
139 if(alcSetThreadContext)
140 {
141 if(alcSetThreadContext(old_ctx) == ALC_FALSE)
142 alcSetThreadContext(NULL);
143 }
144 }
145
146 private:
147 ALCcontext *old_ctx;
148 };
149 #define PROTECT_CONTEXT() ProtectContext _ctx_prot
150 #define DO_PROTECT() _ctx_prot.protect()
151 #define DO_UNPROTECT() _ctx_prot.unprotect()
152
153 struct AsyncPlayEntry {
154 ALuint source;
155 alureStream *stream;
156 std::vector<ALuint> buffers;
157 ALsizei loopcount;
158 ALsizei maxloops;
159 void (*eos_callback)(void*,ALuint);
160 void *user_data;
161 bool finished;
162 bool paused;
163 ALuint stream_freq;
164 ALenum stream_format;
165 ALuint stream_align;
166 ALCcontext *ctx;
167
AsyncPlayEntryAsyncPlayEntry168 AsyncPlayEntry() : source(0), stream(NULL), loopcount(0), maxloops(0),
169 eos_callback(NULL), user_data(NULL), finished(false),
170 paused(false), stream_freq(0), stream_format(AL_NONE),
171 stream_align(0), ctx(NULL)
172 { }
AsyncPlayEntryAsyncPlayEntry173 AsyncPlayEntry(const AsyncPlayEntry &rhs)
174 : source(rhs.source), stream(rhs.stream), buffers(rhs.buffers),
175 loopcount(rhs.loopcount), maxloops(rhs.maxloops),
176 eos_callback(rhs.eos_callback), user_data(rhs.user_data),
177 finished(rhs.finished), paused(rhs.paused),
178 stream_freq(rhs.stream_freq), stream_format(rhs.stream_format),
179 stream_align(rhs.stream_align), ctx(rhs.ctx)
180 { }
181
UpdateAsyncPlayEntry182 ALenum Update(ALint *queued)
183 {
184 ALint processed, state;
185
186 alGetSourcei(source, AL_SOURCE_STATE, &state);
187 alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
188 while(processed > 0)
189 {
190 ALuint buf;
191
192 alSourceUnqueueBuffers(source, 1, &buf);
193 processed--;
194
195 while(!finished)
196 {
197 ALuint got = stream->GetData(&stream->dataChunk[0], stream->dataChunk.size());
198 got -= got%stream_align;
199 if(got > 0)
200 {
201 alBufferData(buf, stream_format, &stream->dataChunk[0], got, stream_freq);
202 alSourceQueueBuffers(source, 1, &buf);
203
204 break;
205 }
206 if(loopcount == maxloops)
207 {
208 finished = true;
209 break;
210 }
211 if(maxloops != -1)
212 loopcount++;
213 finished = !stream->Rewind();
214 }
215 }
216
217 alGetSourcei(source, AL_BUFFERS_QUEUED, queued);
218 return state;
219 }
220 };
221 static std::list<AsyncPlayEntry> AsyncPlayList;
222 static ThreadInfo *PlayThreadHandle;
223
224 ALfloat CurrentInterval = 0.0f;
225
AsyncPlayFunc(ALvoid *)226 ALuint AsyncPlayFunc(ALvoid*)
227 {
228 EnterCriticalSection(&cs_StreamPlay);
229 while(CurrentInterval > 0.0f)
230 {
231 alureUpdate();
232
233 ALfloat interval = CurrentInterval;
234 LeaveCriticalSection(&cs_StreamPlay);
235 alureSleep(interval);
236 EnterCriticalSection(&cs_StreamPlay);
237 }
238 LeaveCriticalSection(&cs_StreamPlay);
239 return 0;
240 }
241
242
StopStream(alureStream * stream)243 void StopStream(alureStream *stream)
244 {
245 EnterCriticalSection(&cs_StreamPlay);
246
247 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
248 end = AsyncPlayList.end();
249 while(i != end)
250 {
251 if(i->stream == stream)
252 {
253 AsyncPlayEntry ent(*i);
254 AsyncPlayList.erase(i);
255
256 ALCcontext *old_ctx = (alcGetThreadContext ?
257 alcGetThreadContext() : NULL);
258 if(alcSetThreadContext)
259 {
260 if(alcSetThreadContext(ent.ctx) == ALC_FALSE)
261 goto ctx_err;
262 }
263
264 alSourceStop(ent.source);
265 alSourcei(ent.source, AL_BUFFER, 0);
266 alDeleteBuffers(ent.buffers.size(), &ent.buffers[0]);
267 alGetError();
268
269 if(alcSetThreadContext)
270 {
271 if(alcSetThreadContext(old_ctx) == ALC_FALSE)
272 alcSetThreadContext(NULL);
273 }
274
275 ctx_err:
276 if(ent.eos_callback)
277 ent.eos_callback(ent.user_data, ent.source);
278 break;
279 }
280 i++;
281 }
282
283 LeaveCriticalSection(&cs_StreamPlay);
284 }
285
286
287 extern "C" {
288
289 /* Function: alurePlaySourceStream
290 *
291 * Starts playing a stream, using the specified source ID. A stream can only be
292 * played if it is not already playing. You must call <alureUpdate> at regular
293 * intervals to keep the stream playing, or else the stream will underrun and
294 * cause a break in the playback until an update call can restart it. It is
295 * also important that the current context is kept for <alureUpdate> calls if
296 * ALC_EXT_thread_local_context is not supported, otherwise the method may
297 * start calling OpenAL with invalid IDs. Note that checking the state of the
298 * specified source is not a good method to determine if a stream is playing.
299 * If an underrun occurs, the source will enter a stopped state until it is
300 * automatically restarted. Instead, set a flag using the callback to indicate
301 * the stream being stopped.
302 *
303 * Parameters:
304 * source - The source ID to play the stream with. Any buffers on the source
305 * will be unqueued. It is valid to set source properties not related
306 * to the buffer queue or playback state (ie. you may change the
307 * source's position, pitch, gain, etc, but you must not stop the
308 * source or queue/unqueue buffers on it). To pause the source, call
309 * <alurePauseSource>.
310 * stream - The stream to play. Any valid stream will work, although looping
311 * will only work if the stream can be rewound (eg. streams made with
312 * <alureCreateStreamFromCallback> cannot loop, but will play for as
313 * long as the callback provides data).
314 * numBufs - The number of buffers used to queue with the OpenAL source. Each
315 * buffer will be filled with the chunk length specified when the
316 * stream was created. This value must be at least 2. More buffers at
317 * a larger size will increase the time needed between updates, but
318 * at the cost of more memory usage.
319 * loopcount - The number of times to loop the stream. When the stream reaches
320 * the end of processing, it will be rewound to continue buffering
321 * data. A value of -1 will cause the stream to loop indefinitely
322 * (or until <alureStopSource> is called).
323 * eos_callback - This callback will be called when the stream reaches the end,
324 * no more loops are pending, and the source reaches a stopped
325 * state. It will also be called if an error occured and
326 * playback terminated.
327 * userdata - An opaque user pointer passed to the callback.
328 *
329 * Returns:
330 * AL_FALSE on error.
331 *
332 * *Version Added*: 1.1
333 *
334 * See Also:
335 * <alureStopSource>, <alurePauseSource>, <alureUpdate>
336 */
alurePlaySourceStream(ALuint source,alureStream * stream,ALsizei numBufs,ALsizei loopcount,void (* eos_callback)(void * userdata,ALuint source),void * userdata)337 ALURE_API ALboolean ALURE_APIENTRY alurePlaySourceStream(ALuint source,
338 alureStream *stream, ALsizei numBufs, ALsizei loopcount,
339 void (*eos_callback)(void *userdata, ALuint source), void *userdata)
340 {
341 PROTECT_CONTEXT();
342 ALCcontext *current_ctx = alcGetCurrentContext();
343
344 if(alGetError() != AL_NO_ERROR)
345 {
346 SetError("Existing OpenAL error");
347 return AL_FALSE;
348 }
349
350 if(!alureStream::Verify(stream))
351 {
352 SetError("Invalid stream pointer");
353 return AL_FALSE;
354 }
355
356 if(numBufs < 2)
357 {
358 SetError("Invalid buffer count");
359 return AL_FALSE;
360 }
361
362 if(!alIsSource(source))
363 {
364 SetError("Invalid source ID");
365 return AL_FALSE;
366 }
367
368 EnterCriticalSection(&cs_StreamPlay);
369
370 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
371 end = AsyncPlayList.end();
372 while(i != end)
373 {
374 if(i->stream == stream)
375 {
376 SetError("Stream is already playing");
377 LeaveCriticalSection(&cs_StreamPlay);
378 return AL_FALSE;
379 }
380 if(i->source == source && i->ctx == current_ctx)
381 {
382 SetError("Source is already playing");
383 LeaveCriticalSection(&cs_StreamPlay);
384 return AL_FALSE;
385 }
386 i++;
387 }
388
389 AsyncPlayEntry ent;
390 ent.stream = stream;
391 ent.source = source;
392 ent.maxloops = loopcount;
393 ent.eos_callback = eos_callback;
394 ent.user_data = userdata;
395 ent.ctx = current_ctx;
396
397 ent.buffers.resize(numBufs);
398 alGenBuffers(ent.buffers.size(), &ent.buffers[0]);
399 if(alGetError() != AL_NO_ERROR)
400 {
401 LeaveCriticalSection(&cs_StreamPlay);
402 SetError("Error generating buffers");
403 return AL_FALSE;
404 }
405
406 numBufs = 0;
407 if(ent.stream->GetFormat(&ent.stream_format, &ent.stream_freq, &ent.stream_align))
408 {
409 for(size_t i = 0;i < ent.buffers.size();i++)
410 {
411 ALuint got = ent.stream->GetData(&ent.stream->dataChunk[0],
412 ent.stream->dataChunk.size());
413 got -= got%ent.stream_align;
414 if(got <= 0)
415 {
416 if(ent.loopcount == ent.maxloops || i == 0)
417 ent.finished = true;
418 else
419 {
420 if(ent.maxloops != -1)
421 ent.loopcount++;
422 ent.finished = !ent.stream->Rewind();
423 }
424 if(ent.finished)
425 break;
426 i--;
427 continue;
428 }
429 ALuint buf = ent.buffers[i];
430 alBufferData(buf, ent.stream_format, &ent.stream->dataChunk[0], got, ent.stream_freq);
431 numBufs++;
432 }
433 }
434 if(numBufs == 0)
435 {
436 alDeleteBuffers(ent.buffers.size(), &ent.buffers[0]);
437 alGetError();
438 LeaveCriticalSection(&cs_StreamPlay);
439 SetError("Error buffering from stream");
440 return AL_FALSE;
441 }
442
443 if((alSourcei(source, AL_LOOPING, AL_FALSE),
444 alSourcei(source, AL_BUFFER, 0),alGetError()) != AL_NO_ERROR ||
445 (alSourceQueueBuffers(source, numBufs, &ent.buffers[0]),
446 alSourcePlay(source),alGetError()) != AL_NO_ERROR)
447 {
448 alSourcei(source, AL_BUFFER, 0);
449 alDeleteBuffers(ent.buffers.size(), &ent.buffers[0]);
450 alGetError();
451 LeaveCriticalSection(&cs_StreamPlay);
452 SetError("Error starting source");
453 return AL_FALSE;
454 }
455
456 AsyncPlayList.push_front(ent);
457
458 LeaveCriticalSection(&cs_StreamPlay);
459
460 return AL_TRUE;
461 }
462
463 /* Function: alurePlaySource
464 *
465 * Plays the specified source ID and watches for it to stop. When the source
466 * enters an AL_STOPPED or AL_INITIAL state, the specified callback will be
467 * called by <alureUpdate> to alert the application. If
468 * ALC_EXT_thread_local_context is not supported, the current context must not
469 * be changed while the source is being watched (before the callback is called
470 * or <alureStopSource> is called). It also must not be deleted while being
471 * watched.
472 *
473 * Parameters:
474 * source - The source ID to play. As with <alurePlaySourceStream>, it is valid
475 * to set source properties not related to the playback state (ie. you
476 * may change a source's position, pitch, gain, etc). Pausing a source
477 * and restarting a paused source is allowed, and the callback will
478 * still be called when the source reaches an AL_STOPPED or AL_INITIAL
479 * state.
480 * callback - The callback to be called when the source stops.
481 * userdata - An opaque user pointer passed to the callback.
482 *
483 * Returns:
484 * AL_FALSE on error.
485 *
486 * *Version Added*: 1.1
487 *
488 * See Also:
489 * <alureStopSource>, <alureUpdate>
490 */
alurePlaySource(ALuint source,void (* callback)(void * userdata,ALuint source),void * userdata)491 ALURE_API ALboolean ALURE_APIENTRY alurePlaySource(ALuint source,
492 void (*callback)(void *userdata, ALuint source), void *userdata)
493 {
494 PROTECT_CONTEXT();
495 ALCcontext *current_ctx = alcGetCurrentContext();
496
497 if(alGetError() != AL_NO_ERROR)
498 {
499 SetError("Existing OpenAL error");
500 return AL_FALSE;
501 }
502
503 EnterCriticalSection(&cs_StreamPlay);
504
505 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
506 end = AsyncPlayList.end();
507 while(i != end)
508 {
509 if(i->source == source && i->ctx == current_ctx)
510 {
511 SetError("Source is already playing");
512 LeaveCriticalSection(&cs_StreamPlay);
513 return AL_FALSE;
514 }
515 i++;
516 }
517
518 if((alSourcePlay(source),alGetError()) != AL_NO_ERROR)
519 {
520 LeaveCriticalSection(&cs_StreamPlay);
521 SetError("Error starting source");
522 return AL_FALSE;
523 }
524
525 if(callback != NULL)
526 {
527 AsyncPlayEntry ent;
528 ent.source = source;
529 ent.eos_callback = callback;
530 ent.user_data = userdata;
531 ent.ctx = current_ctx;
532 AsyncPlayList.push_front(ent);
533 }
534
535 LeaveCriticalSection(&cs_StreamPlay);
536
537 return AL_TRUE;
538 }
539
540 /* Function: alureStopSource
541 *
542 * Stops the specified source ID, and any associated stream. The previously
543 * specified callback will be invoked if 'run_callback' is not AL_FALSE.
544 * Sources that were not started with <alurePlaySourceStream> or
545 * <alurePlaySource> will still be stopped, but will not have any callback
546 * called for them.
547 *
548 * Returns:
549 * AL_FALSE on error.
550 *
551 * *Version Added*: 1.1
552 *
553 * See Also:
554 * <alurePlaySourceStream>, <alurePlaySource>
555 */
alureStopSource(ALuint source,ALboolean run_callback)556 ALURE_API ALboolean ALURE_APIENTRY alureStopSource(ALuint source, ALboolean run_callback)
557 {
558 PROTECT_CONTEXT();
559 ALCcontext *current_ctx = alcGetCurrentContext();
560
561 if(alGetError() != AL_NO_ERROR)
562 {
563 SetError("Existing OpenAL error");
564 return AL_FALSE;
565 }
566
567 EnterCriticalSection(&cs_StreamPlay);
568
569 if((alSourceStop(source),alGetError()) != AL_NO_ERROR)
570 {
571 LeaveCriticalSection(&cs_StreamPlay);
572 SetError("Error stopping source");
573 return AL_FALSE;
574 }
575
576 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
577 end = AsyncPlayList.end();
578 while(i != end)
579 {
580 if(i->source == source && i->ctx == current_ctx)
581 {
582 AsyncPlayEntry ent(*i);
583 AsyncPlayList.erase(i);
584
585 if(ent.buffers.size() > 0)
586 {
587 alSourcei(ent.source, AL_BUFFER, 0);
588 alDeleteBuffers(ent.buffers.size(), &ent.buffers[0]);
589 alGetError();
590 }
591
592 if(run_callback && ent.eos_callback)
593 {
594 DO_UNPROTECT();
595 ent.eos_callback(ent.user_data, ent.source);
596 DO_PROTECT();
597 }
598 break;
599 }
600 i++;
601 }
602
603 LeaveCriticalSection(&cs_StreamPlay);
604
605 return AL_TRUE;
606 }
607
608 /* Function: alurePauseSource
609 *
610 * Pauses the specified source ID, and any associated stream. This is needed to
611 * avoid potential race conditions with sources that are playing a stream.
612 *
613 * Note that it is possible for the specified source to become stopped, and any
614 * associated stream to finish, before this function is called, causing the
615 * callback to be delayed until after the function returns and <alureUpdate>
616 * detects the stopped source.
617 *
618 * Returns:
619 * AL_FALSE on error.
620 *
621 * *Version Added*: 1.1
622 *
623 * See Also:
624 * <alureResumeSource>, <alurePlaySourceStream>, <alurePlaySource>
625 */
alurePauseSource(ALuint source)626 ALURE_API ALboolean ALURE_APIENTRY alurePauseSource(ALuint source)
627 {
628 PROTECT_CONTEXT();
629 ALCcontext *current_ctx = alcGetCurrentContext();
630
631 if(alGetError() != AL_NO_ERROR)
632 {
633 SetError("Existing OpenAL error");
634 return AL_FALSE;
635 }
636
637 EnterCriticalSection(&cs_StreamPlay);
638
639 if((alSourcePause(source),alGetError()) != AL_NO_ERROR)
640 {
641 SetError("Error pausing source");
642 LeaveCriticalSection(&cs_StreamPlay);
643 return AL_FALSE;
644 }
645
646 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
647 end = AsyncPlayList.end();
648 while(i != end)
649 {
650 if(i->source == source && i->ctx == current_ctx)
651 {
652 i->paused = true;
653 break;
654 }
655 i++;
656 }
657
658 LeaveCriticalSection(&cs_StreamPlay);
659
660 return AL_TRUE;
661 }
662
663 /* Function: alureResumeSource
664 *
665 * Resumes the specified source ID after being paused.
666 *
667 * Returns:
668 * AL_FALSE on error.
669 *
670 * *Version Added*: 1.1
671 *
672 * See Also:
673 * <alurePauseSource>
674 */
alureResumeSource(ALuint source)675 ALURE_API ALboolean ALURE_APIENTRY alureResumeSource(ALuint source)
676 {
677 PROTECT_CONTEXT();
678 ALCcontext *current_ctx = alcGetCurrentContext();
679
680 if(alGetError() != AL_NO_ERROR)
681 {
682 SetError("Existing OpenAL error");
683 return AL_FALSE;
684 }
685
686 EnterCriticalSection(&cs_StreamPlay);
687
688 if((alSourcePlay(source),alGetError()) != AL_NO_ERROR)
689 {
690 SetError("Error playing source");
691 LeaveCriticalSection(&cs_StreamPlay);
692 return AL_FALSE;
693 }
694
695 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
696 end = AsyncPlayList.end();
697 while(i != end)
698 {
699 if(i->source == source && i->ctx == current_ctx)
700 {
701 i->paused = false;
702 break;
703 }
704 i++;
705 }
706
707 LeaveCriticalSection(&cs_StreamPlay);
708
709 return AL_TRUE;
710 }
711
712 /* Function: alureUpdate
713 *
714 * Updates the running list of streams, and checks for stopped sources. This
715 * makes sure that sources played with <alurePlaySourceStream> are kept fed
716 * from their associated stream, and sources played with <alurePlaySource> are
717 * still playing. It will call their callbacks as needed.
718 *
719 * *Version Added*: 1.1
720 *
721 * See Also:
722 * <alurePlaySourceStream>, <alurePlaySource>
723 */
alureUpdate(void)724 ALURE_API void ALURE_APIENTRY alureUpdate(void)
725 {
726 PROTECT_CONTEXT();
727
728 EnterCriticalSection(&cs_StreamPlay);
729 restart:
730 std::list<AsyncPlayEntry>::iterator i = AsyncPlayList.begin(),
731 end = AsyncPlayList.end();
732 for(;i != end;i++)
733 {
734 if(alcSetThreadContext)
735 {
736 if(alcSetThreadContext(i->ctx) == ALC_FALSE)
737 {
738 AsyncPlayEntry ent(*i);
739 AsyncPlayList.erase(i);
740 if(ent.eos_callback)
741 {
742 DO_UNPROTECT();
743 ent.eos_callback(ent.user_data, ent.source);
744 DO_PROTECT();
745 }
746 goto restart;
747 }
748 }
749
750 if(i->stream == NULL)
751 {
752 ALint state;
753 alGetSourcei(i->source, AL_SOURCE_STATE, &state);
754 if(state == AL_STOPPED || state == AL_INITIAL)
755 {
756 AsyncPlayEntry ent(*i);
757 AsyncPlayList.erase(i);
758 DO_UNPROTECT();
759 ent.eos_callback(ent.user_data, ent.source);
760 DO_PROTECT();
761 goto restart;
762 }
763 continue;
764 }
765
766 ALint queued;
767 if(i->Update(&queued) != AL_PLAYING)
768 {
769 if(queued == 0)
770 {
771 AsyncPlayEntry ent(*i);
772 AsyncPlayList.erase(i);
773
774 alSourcei(ent.source, AL_BUFFER, 0);
775 alDeleteBuffers(ent.buffers.size(), &ent.buffers[0]);
776 if(ent.eos_callback)
777 {
778 DO_UNPROTECT();
779 ent.eos_callback(ent.user_data, ent.source);
780 DO_PROTECT();
781 }
782 goto restart;
783 }
784 if(!i->paused)
785 alSourcePlay(i->source);
786 }
787 }
788 LeaveCriticalSection(&cs_StreamPlay);
789 }
790
791 /* Function: alureUpdateInterval
792 *
793 * Sets up a timer or thread to automatically call <alureUpdate> at the given
794 * interval, in seconds. If the timer or thread is already running, the update
795 * interval will be modified. A 0 or negative interval will stop <alureUpdate>
796 * from being called.
797 *
798 * Returns:
799 * AL_FALSE on error.
800 *
801 * *Version Added*: 1.1
802 *
803 * See Also:
804 * <alureUpdate>
805 */
alureUpdateInterval(ALfloat interval)806 ALURE_API ALboolean ALURE_APIENTRY alureUpdateInterval(ALfloat interval)
807 {
808 EnterCriticalSection(&cs_StreamPlay);
809 if(interval <= 0.0f)
810 {
811 CurrentInterval = 0.0f;
812 if(PlayThreadHandle)
813 {
814 ThreadInfo *threadinf = PlayThreadHandle;
815 PlayThreadHandle = NULL;
816 LeaveCriticalSection(&cs_StreamPlay);
817 StopThread(threadinf);
818 EnterCriticalSection(&cs_StreamPlay);
819 }
820 }
821 else if(interval > 0.0f)
822 {
823 if(!PlayThreadHandle)
824 PlayThreadHandle = StartThread(AsyncPlayFunc, NULL);
825 if(!PlayThreadHandle)
826 {
827 SetError("Error starting async thread");
828 LeaveCriticalSection(&cs_StreamPlay);
829 return AL_FALSE;
830 }
831 CurrentInterval = interval;
832 }
833 LeaveCriticalSection(&cs_StreamPlay);
834
835 return AL_TRUE;
836 }
837
838 } // extern "C"
839