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