1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
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.
9
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.
14
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 St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/* Allow access to a raw mixing buffer */
25
26#include "SDL.h"
27#include "SDL_audio_c.h"
28#include "SDL_audiomem.h"
29#include "SDL_sysaudio.h"
30
31#ifdef __OS2__
32/* We'll need the DosSetPriority() API! */
33#define INCL_DOSPROCESS
34#include <os2.h>
35#endif
36
37/* Available audio drivers */
38static AudioBootStrap *bootstrap[] = {
39#if SDL_AUDIO_DRIVER_PULSE
40	&PULSE_bootstrap,
41#endif
42#if SDL_AUDIO_DRIVER_ALSA
43	&ALSA_bootstrap,
44#endif
45#if SDL_AUDIO_DRIVER_BSD
46	&BSD_AUDIO_bootstrap,
47#endif
48#if SDL_AUDIO_DRIVER_OSS
49	&DSP_bootstrap,
50	&DMA_bootstrap,
51#endif
52#if SDL_AUDIO_DRIVER_QNXNTO
53	&QNXNTOAUDIO_bootstrap,
54#endif
55#if SDL_AUDIO_DRIVER_SUNAUDIO
56	&SUNAUDIO_bootstrap,
57#endif
58#if SDL_AUDIO_DRIVER_DMEDIA
59	&DMEDIA_bootstrap,
60#endif
61#if SDL_AUDIO_DRIVER_ARTS
62	&ARTS_bootstrap,
63#endif
64#if SDL_AUDIO_DRIVER_ESD
65	&ESD_bootstrap,
66#endif
67#if SDL_AUDIO_DRIVER_NAS
68	&NAS_bootstrap,
69#endif
70#if SDL_AUDIO_DRIVER_DSOUND
71	&DSOUND_bootstrap,
72#endif
73#if SDL_AUDIO_DRIVER_WAVEOUT
74	&WAVEOUT_bootstrap,
75#endif
76#if SDL_AUDIO_DRIVER_PAUD
77	&Paud_bootstrap,
78#endif
79#if SDL_AUDIO_DRIVER_BAUDIO
80	&BAUDIO_bootstrap,
81#endif
82#if SDL_AUDIO_DRIVER_COREAUDIO
83	&COREAUDIO_bootstrap,
84#endif
85#if SDL_AUDIO_DRIVER_SNDMGR
86	&SNDMGR_bootstrap,
87#endif
88#if SDL_AUDIO_DRIVER_MINT
89	&MINTAUDIO_GSXB_bootstrap,
90	&MINTAUDIO_MCSN_bootstrap,
91	&MINTAUDIO_STFA_bootstrap,
92	&MINTAUDIO_XBIOS_bootstrap,
93	&MINTAUDIO_DMA8_bootstrap,
94#endif
95#if SDL_AUDIO_DRIVER_DISK
96	&DISKAUD_bootstrap,
97#endif
98#if SDL_AUDIO_DRIVER_DUMMY
99	&DUMMYAUD_bootstrap,
100#endif
101#if SDL_AUDIO_DRIVER_DC
102	&DCAUD_bootstrap,
103#endif
104#if SDL_AUDIO_DRIVER_NDS
105	&NDSAUD_bootstrap,
106#endif
107#if SDL_AUDIO_DRIVER_MMEAUDIO
108	&MMEAUDIO_bootstrap,
109#endif
110#if SDL_AUDIO_DRIVER_DART
111	&DART_bootstrap,
112#endif
113#if SDL_AUDIO_DRIVER_EPOCAUDIO
114	&EPOCAudio_bootstrap,
115#endif
116	NULL
117};
118SDL_AudioDevice *current_audio = NULL;
119
120/* Various local functions */
121int SDL_AudioInit(const char *driver_name);
122void SDL_AudioQuit(void);
123
124/* The general mixing thread function */
125int SDLCALL SDL_RunAudio(void *audiop)
126{
127	SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
128	Uint8 *stream;
129	int    stream_len;
130	void  *udata;
131	void (SDLCALL *fill)(void *userdata,Uint8 *stream, int len);
132	int    silence;
133
134	/* Perform any thread setup */
135	if ( audio->ThreadInit ) {
136		audio->ThreadInit(audio);
137	}
138	audio->threadid = SDL_ThreadID();
139
140	/* Set up the mixing function */
141	fill  = audio->spec.callback;
142	udata = audio->spec.userdata;
143
144	if ( audio->convert.needed ) {
145		if ( audio->convert.src_format == AUDIO_U8 ) {
146			silence = 0x80;
147		} else {
148			silence = 0;
149		}
150		stream_len = audio->convert.len;
151	} else {
152		silence = audio->spec.silence;
153		stream_len = audio->spec.size;
154	}
155
156#ifdef __OS2__
157        /* Increase the priority of this thread to make sure that
158           the audio will be continuous all the time! */
159#ifdef USE_DOSSETPRIORITY
160        if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO"))
161        {
162#ifdef DEBUG_BUILD
163          printf("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n", SDL_ThreadID());
164#endif
165          DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
166        }
167        else
168        {
169#ifdef DEBUG_BUILD
170          printf("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n", SDL_ThreadID());
171#endif
172          DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
173        }
174#endif
175#endif
176
177	/* Loop, filling the audio buffers */
178	while ( audio->enabled ) {
179
180		/* Fill the current buffer with sound */
181		if ( audio->convert.needed ) {
182			if ( audio->convert.buf ) {
183				stream = audio->convert.buf;
184			} else {
185				continue;
186			}
187		} else {
188			stream = audio->GetAudioBuf(audio);
189			if ( stream == NULL ) {
190				stream = audio->fake_stream;
191			}
192		}
193
194		SDL_memset(stream, silence, stream_len);
195
196		if ( ! audio->paused ) {
197			SDL_mutexP(audio->mixer_lock);
198			(*fill)(udata, stream, stream_len);
199			SDL_mutexV(audio->mixer_lock);
200		}
201
202		/* Convert the audio if necessary */
203		if ( audio->convert.needed ) {
204			SDL_ConvertAudio(&audio->convert);
205			stream = audio->GetAudioBuf(audio);
206			if ( stream == NULL ) {
207				stream = audio->fake_stream;
208			}
209			SDL_memcpy(stream, audio->convert.buf,
210			               audio->convert.len_cvt);
211		}
212
213		/* Ready current buffer for play and change current buffer */
214		if ( stream != audio->fake_stream ) {
215			audio->PlayAudio(audio);
216		}
217
218		/* Wait for an audio buffer to become available */
219		if ( stream == audio->fake_stream ) {
220			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
221		} else {
222			audio->WaitAudio(audio);
223		}
224	}
225
226	/* Wait for the audio to drain.. */
227	if ( audio->WaitDone ) {
228		audio->WaitDone(audio);
229	}
230
231#ifdef __OS2__
232#ifdef DEBUG_BUILD
233        printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID());
234#endif
235#endif
236	return(0);
237}
238
239static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
240{
241	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
242		return;
243	}
244	SDL_mutexP(audio->mixer_lock);
245}
246
247static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
248{
249	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
250		return;
251	}
252	SDL_mutexV(audio->mixer_lock);
253}
254
255static Uint16 SDL_ParseAudioFormat(const char *string)
256{
257	Uint16 format = 0;
258
259	switch (*string) {
260	    case 'U':
261		++string;
262		format |= 0x0000;
263		break;
264	    case 'S':
265		++string;
266		format |= 0x8000;
267		break;
268	    default:
269		return 0;
270	}
271	switch (SDL_atoi(string)) {
272	    case 8:
273		string += 1;
274		format |= 8;
275		break;
276	    case 16:
277		string += 2;
278		format |= 16;
279		if ( SDL_strcmp(string, "LSB") == 0
280#if SDL_BYTEORDER == SDL_LIL_ENDIAN
281		     || SDL_strcmp(string, "SYS") == 0
282#endif
283		    ) {
284			format |= 0x0000;
285		}
286		if ( SDL_strcmp(string, "MSB") == 0
287#if SDL_BYTEORDER == SDL_BIG_ENDIAN
288		     || SDL_strcmp(string, "SYS") == 0
289#endif
290		    ) {
291			format |= 0x1000;
292		}
293		break;
294	    default:
295		return 0;
296	}
297	return format;
298}
299
300int SDL_AudioInit(const char *driver_name)
301{
302	SDL_AudioDevice *audio;
303	int i = 0, idx;
304
305	/* Check to make sure we don't overwrite 'current_audio' */
306	if ( current_audio != NULL ) {
307		SDL_AudioQuit();
308	}
309
310	/* Select the proper audio driver */
311	audio = NULL;
312	idx = 0;
313#if SDL_AUDIO_DRIVER_ESD
314	if ( (driver_name == NULL) && (SDL_getenv("ESPEAKER") != NULL) ) {
315		/* Ahem, we know that if ESPEAKER is set, user probably wants
316		   to use ESD, but don't start it if it's not already running.
317		   This probably isn't the place to do this, but... Shh! :)
318		 */
319		for ( i=0; bootstrap[i]; ++i ) {
320			if ( SDL_strcasecmp(bootstrap[i]->name, "esd") == 0 ) {
321#ifdef HAVE_PUTENV
322				const char *esd_no_spawn;
323
324				/* Don't start ESD if it's not running */
325				esd_no_spawn = getenv("ESD_NO_SPAWN");
326				if ( esd_no_spawn == NULL ) {
327					putenv("ESD_NO_SPAWN=1");
328				}
329#endif
330				if ( bootstrap[i]->available() ) {
331					audio = bootstrap[i]->create(0);
332					break;
333				}
334#ifdef HAVE_UNSETENV
335				if ( esd_no_spawn == NULL ) {
336					unsetenv("ESD_NO_SPAWN");
337				}
338#endif
339			}
340		}
341	}
342#endif /* SDL_AUDIO_DRIVER_ESD */
343	if ( audio == NULL ) {
344		if ( driver_name != NULL ) {
345#if 0	/* This will be replaced with a better driver selection API */
346			if ( SDL_strrchr(driver_name, ':') != NULL ) {
347				idx = atoi(SDL_strrchr(driver_name, ':')+1);
348			}
349#endif
350			for ( i=0; bootstrap[i]; ++i ) {
351				if (SDL_strcasecmp(bootstrap[i]->name, driver_name) == 0) {
352					if ( bootstrap[i]->available() ) {
353						audio=bootstrap[i]->create(idx);
354						break;
355					}
356				}
357			}
358		} else {
359			for ( i=0; bootstrap[i]; ++i ) {
360				if ( bootstrap[i]->available() ) {
361					audio = bootstrap[i]->create(idx);
362					if ( audio != NULL ) {
363						break;
364					}
365				}
366			}
367		}
368		if ( audio == NULL ) {
369			SDL_SetError("No available audio device");
370#if 0 /* Don't fail SDL_Init() if audio isn't available.
371         SDL_OpenAudio() will handle it at that point.  *sigh*
372       */
373			return(-1);
374#endif
375		}
376	}
377	current_audio = audio;
378	if ( current_audio ) {
379		current_audio->name = bootstrap[i]->name;
380		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
381			current_audio->LockAudio = SDL_LockAudio_Default;
382			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
383		}
384	}
385	return(0);
386}
387
388char *SDL_AudioDriverName(char *namebuf, int maxlen)
389{
390	if ( current_audio != NULL ) {
391		SDL_strlcpy(namebuf, current_audio->name, maxlen);
392		return(namebuf);
393	}
394	return(NULL);
395}
396
397int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
398{
399	SDL_AudioDevice *audio;
400	const char *env;
401
402	/* Start up the audio driver, if necessary */
403	if ( ! current_audio ) {
404		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
405		     (current_audio == NULL) ) {
406			return(-1);
407		}
408	}
409	audio = current_audio;
410
411	if (audio->opened) {
412		SDL_SetError("Audio device is already opened");
413		return(-1);
414	}
415
416	/* Verify some parameters */
417	if ( desired->freq == 0 ) {
418		env = SDL_getenv("SDL_AUDIO_FREQUENCY");
419		if ( env ) {
420			desired->freq = SDL_atoi(env);
421		}
422	}
423	if ( desired->freq == 0 ) {
424		/* Pick some default audio frequency */
425		desired->freq = 22050;
426	}
427	if ( desired->format == 0 ) {
428		env = SDL_getenv("SDL_AUDIO_FORMAT");
429		if ( env ) {
430			desired->format = SDL_ParseAudioFormat(env);
431		}
432	}
433	if ( desired->format == 0 ) {
434		/* Pick some default audio format */
435		desired->format = AUDIO_S16;
436	}
437	if ( desired->channels == 0 ) {
438		env = SDL_getenv("SDL_AUDIO_CHANNELS");
439		if ( env ) {
440			desired->channels = (Uint8)SDL_atoi(env);
441		}
442	}
443	if ( desired->channels == 0 ) {
444		/* Pick a default number of channels */
445		desired->channels = 2;
446	}
447	switch ( desired->channels ) {
448	    case 1:	/* Mono */
449	    case 2:	/* Stereo */
450	    case 4:	/* surround */
451	    case 6:	/* surround with center and lfe */
452		break;
453	    default:
454		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
455		return(-1);
456	}
457	if ( desired->samples == 0 ) {
458		env = SDL_getenv("SDL_AUDIO_SAMPLES");
459		if ( env ) {
460			desired->samples = (Uint16)SDL_atoi(env);
461		}
462	}
463	if ( desired->samples == 0 ) {
464		/* Pick a default of ~46 ms at desired frequency */
465		int samples = (desired->freq / 1000) * 46;
466		int power2 = 1;
467		while ( power2 < samples ) {
468			power2 *= 2;
469		}
470		desired->samples = power2;
471	}
472	if ( desired->callback == NULL ) {
473		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
474		return(-1);
475	}
476
477#if SDL_THREADS_DISABLED
478	/* Uses interrupt driven audio, without thread */
479#else
480	/* Create a semaphore for locking the sound buffers */
481	audio->mixer_lock = SDL_CreateMutex();
482	if ( audio->mixer_lock == NULL ) {
483		SDL_SetError("Couldn't create mixer lock");
484		SDL_CloseAudio();
485		return(-1);
486	}
487#endif /* SDL_THREADS_DISABLED */
488
489	/* Calculate the silence and size of the audio specification */
490	SDL_CalculateAudioSpec(desired);
491
492	/* Open the audio subsystem */
493	SDL_memcpy(&audio->spec, desired, sizeof(audio->spec));
494	audio->convert.needed = 0;
495	audio->enabled = 1;
496	audio->paused  = 1;
497
498	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
499
500	if ( ! audio->opened ) {
501		SDL_CloseAudio();
502		return(-1);
503	}
504
505	/* If the audio driver changes the buffer size, accept it */
506	if ( audio->spec.samples != desired->samples ) {
507		desired->samples = audio->spec.samples;
508		SDL_CalculateAudioSpec(desired);
509	}
510
511	/* Allocate a fake audio memory buffer */
512	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
513	if ( audio->fake_stream == NULL ) {
514		SDL_CloseAudio();
515		SDL_OutOfMemory();
516		return(-1);
517	}
518
519	/* See if we need to do any conversion */
520	if ( obtained != NULL ) {
521		SDL_memcpy(obtained, &audio->spec, sizeof(audio->spec));
522	} else if ( desired->freq != audio->spec.freq ||
523                    desired->format != audio->spec.format ||
524	            desired->channels != audio->spec.channels ) {
525		/* Build an audio conversion block */
526		if ( SDL_BuildAudioCVT(&audio->convert,
527			desired->format, desired->channels,
528					desired->freq,
529			audio->spec.format, audio->spec.channels,
530					audio->spec.freq) < 0 ) {
531			SDL_CloseAudio();
532			return(-1);
533		}
534		if ( audio->convert.needed ) {
535			audio->convert.len = (int) ( ((double) audio->spec.size) /
536                                          audio->convert.len_ratio );
537			audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
538			   audio->convert.len*audio->convert.len_mult);
539			if ( audio->convert.buf == NULL ) {
540				SDL_CloseAudio();
541				SDL_OutOfMemory();
542				return(-1);
543			}
544		}
545	}
546
547	/* Start the audio thread if necessary */
548	switch (audio->opened) {
549		case  1:
550			/* Start the audio thread */
551#if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) && !defined(__SYMBIAN32__)
552#undef SDL_CreateThread
553			audio->thread = SDL_CreateThread(SDL_RunAudio, audio, NULL, NULL);
554#else
555			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
556#endif
557			if ( audio->thread == NULL ) {
558				SDL_CloseAudio();
559				SDL_SetError("Couldn't create audio thread");
560				return(-1);
561			}
562			break;
563
564		default:
565			/* The audio is now playing */
566			break;
567	}
568
569	return(0);
570}
571
572SDL_audiostatus SDL_GetAudioStatus(void)
573{
574	SDL_AudioDevice *audio = current_audio;
575	SDL_audiostatus status;
576
577	status = SDL_AUDIO_STOPPED;
578	if ( audio && audio->enabled ) {
579		if ( audio->paused ) {
580			status = SDL_AUDIO_PAUSED;
581		} else {
582			status = SDL_AUDIO_PLAYING;
583		}
584	}
585	return(status);
586}
587
588void SDL_PauseAudio (int pause_on)
589{
590	SDL_AudioDevice *audio = current_audio;
591
592	if ( audio ) {
593		audio->paused = pause_on;
594	}
595}
596
597void SDL_LockAudio (void)
598{
599	SDL_AudioDevice *audio = current_audio;
600
601	/* Obtain a lock on the mixing buffers */
602	if ( audio && audio->LockAudio ) {
603		audio->LockAudio(audio);
604	}
605}
606
607void SDL_UnlockAudio (void)
608{
609	SDL_AudioDevice *audio = current_audio;
610
611	/* Release lock on the mixing buffers */
612	if ( audio && audio->UnlockAudio ) {
613		audio->UnlockAudio(audio);
614	}
615}
616
617void SDL_CloseAudio (void)
618{
619	SDL_QuitSubSystem(SDL_INIT_AUDIO);
620}
621
622void SDL_AudioQuit(void)
623{
624	SDL_AudioDevice *audio = current_audio;
625
626	if ( audio ) {
627		audio->enabled = 0;
628		if ( audio->thread != NULL ) {
629			SDL_WaitThread(audio->thread, NULL);
630		}
631		if ( audio->mixer_lock != NULL ) {
632			SDL_DestroyMutex(audio->mixer_lock);
633		}
634		if ( audio->fake_stream != NULL ) {
635			SDL_FreeAudioMem(audio->fake_stream);
636		}
637		if ( audio->convert.needed ) {
638			SDL_FreeAudioMem(audio->convert.buf);
639
640		}
641		if ( audio->opened ) {
642			audio->CloseAudio(audio);
643			audio->opened = 0;
644		}
645		/* Free the driver data */
646		audio->free(audio);
647		current_audio = NULL;
648	}
649}
650
651#define NUM_FORMATS	6
652static int format_idx;
653static int format_idx_sub;
654static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
655 { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
656 { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
657 { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
658 { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
659 { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
660 { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
661};
662
663Uint16 SDL_FirstAudioFormat(Uint16 format)
664{
665	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
666		if ( format_list[format_idx][0] == format ) {
667			break;
668		}
669	}
670	format_idx_sub = 0;
671	return(SDL_NextAudioFormat());
672}
673
674Uint16 SDL_NextAudioFormat(void)
675{
676	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
677		return(0);
678	}
679	return(format_list[format_idx][format_idx_sub++]);
680}
681
682void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
683{
684	switch (spec->format) {
685		case AUDIO_U8:
686			spec->silence = 0x80;
687			break;
688		default:
689			spec->silence = 0x00;
690			break;
691	}
692	spec->size = (spec->format&0xFF)/8;
693	spec->size *= spec->channels;
694	spec->size *= spec->samples;
695}
696
697void SDL_Audio_SetCaption(const char *caption)
698{
699	if ((current_audio) && (current_audio->SetCaption)) {
700		current_audio->SetCaption(current_audio, caption);
701	}
702}
703
704