185571bc7Sbellard /* 21d14ffa9Sbellard * QEMU SDL audio driver 385571bc7Sbellard * 41d14ffa9Sbellard * Copyright (c) 2004-2005 Vassili Karpov (malc) 585571bc7Sbellard * 685571bc7Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 785571bc7Sbellard * of this software and associated documentation files (the "Software"), to deal 885571bc7Sbellard * in the Software without restriction, including without limitation the rights 985571bc7Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1085571bc7Sbellard * copies of the Software, and to permit persons to whom the Software is 1185571bc7Sbellard * furnished to do so, subject to the following conditions: 1285571bc7Sbellard * 1385571bc7Sbellard * The above copyright notice and this permission notice shall be included in 1485571bc7Sbellard * all copies or substantial portions of the Software. 1585571bc7Sbellard * 1685571bc7Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1785571bc7Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1885571bc7Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1985571bc7Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2085571bc7Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2185571bc7Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2285571bc7Sbellard * THE SOFTWARE. 2385571bc7Sbellard */ 246086a565SPeter Maydell #include "qemu/osdep.h" 259f059ecaSbellard #include <SDL.h> 269f059ecaSbellard #include <SDL_thread.h> 2787ecb68bSpbrook #include "qemu-common.h" 2887ecb68bSpbrook #include "audio.h" 2985571bc7Sbellard 30e784ba70Sths #ifndef _WIN32 31e784ba70Sths #ifdef __sun__ 32e784ba70Sths #define _POSIX_PTHREAD_SEMANTICS 1 33c5e97233Sblueswir1 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 349b4c14c3Sblueswir1 #include <pthread.h> 35e784ba70Sths #endif 36e784ba70Sths #endif 37e784ba70Sths 381d14ffa9Sbellard #define AUDIO_CAP "sdl" 391d14ffa9Sbellard #include "audio_int.h" 40fb065187Sbellard 41bcf19777SThomas Huth #define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2) 42bcf19777SThomas Huth 431d14ffa9Sbellard typedef struct SDLVoiceOut { 441d14ffa9Sbellard HWVoiceOut hw; 451d14ffa9Sbellard int live; 46bcf19777SThomas Huth #if USE_SEMAPHORE 47ff541499Smalc int rpos; 48bcf19777SThomas Huth #endif 491d14ffa9Sbellard int decr; 501d14ffa9Sbellard } SDLVoiceOut; 5185571bc7Sbellard 5285571bc7Sbellard static struct { 5385571bc7Sbellard int nb_samples; 5485571bc7Sbellard } conf = { 551a40d5e2SJuan Quintela .nb_samples = 1024 5685571bc7Sbellard }; 5785571bc7Sbellard 58b1d8e52eSblueswir1 static struct SDLAudioState { 5985571bc7Sbellard int exit; 60bcf19777SThomas Huth #if USE_SEMAPHORE 6185571bc7Sbellard SDL_mutex *mutex; 6285571bc7Sbellard SDL_sem *sem; 63bcf19777SThomas Huth #endif 6485571bc7Sbellard int initialized; 6581ebb07cSKővágó, Zoltán bool driver_created; 6685571bc7Sbellard } glob_sdl; 6785571bc7Sbellard typedef struct SDLAudioState SDLAudioState; 6885571bc7Sbellard 691d14ffa9Sbellard static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 7085571bc7Sbellard { 711d14ffa9Sbellard va_list ap; 721d14ffa9Sbellard 731d14ffa9Sbellard va_start (ap, fmt); 741d14ffa9Sbellard AUD_vlog (AUDIO_CAP, fmt, ap); 751d14ffa9Sbellard va_end (ap); 761d14ffa9Sbellard 771d14ffa9Sbellard AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 7885571bc7Sbellard } 7985571bc7Sbellard 801d14ffa9Sbellard static int sdl_lock (SDLAudioState *s, const char *forfn) 8185571bc7Sbellard { 82bcf19777SThomas Huth #if USE_SEMAPHORE 8385571bc7Sbellard if (SDL_LockMutex (s->mutex)) { 841d14ffa9Sbellard sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); 8585571bc7Sbellard return -1; 8685571bc7Sbellard } 87bcf19777SThomas Huth #else 88bcf19777SThomas Huth SDL_LockAudio(); 89bcf19777SThomas Huth #endif 90bcf19777SThomas Huth 9185571bc7Sbellard return 0; 9285571bc7Sbellard } 9385571bc7Sbellard 941d14ffa9Sbellard static int sdl_unlock (SDLAudioState *s, const char *forfn) 9585571bc7Sbellard { 96bcf19777SThomas Huth #if USE_SEMAPHORE 9785571bc7Sbellard if (SDL_UnlockMutex (s->mutex)) { 981d14ffa9Sbellard sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); 9985571bc7Sbellard return -1; 10085571bc7Sbellard } 101bcf19777SThomas Huth #else 102bcf19777SThomas Huth SDL_UnlockAudio(); 103bcf19777SThomas Huth #endif 104bcf19777SThomas Huth 10585571bc7Sbellard return 0; 10685571bc7Sbellard } 10785571bc7Sbellard 1081d14ffa9Sbellard static int sdl_post (SDLAudioState *s, const char *forfn) 10985571bc7Sbellard { 110bcf19777SThomas Huth #if USE_SEMAPHORE 11185571bc7Sbellard if (SDL_SemPost (s->sem)) { 1121d14ffa9Sbellard sdl_logerr ("SDL_SemPost for %s failed\n", forfn); 11385571bc7Sbellard return -1; 11485571bc7Sbellard } 115bcf19777SThomas Huth #endif 116bcf19777SThomas Huth 11785571bc7Sbellard return 0; 11885571bc7Sbellard } 11985571bc7Sbellard 120bcf19777SThomas Huth #if USE_SEMAPHORE 1211d14ffa9Sbellard static int sdl_wait (SDLAudioState *s, const char *forfn) 12285571bc7Sbellard { 12385571bc7Sbellard if (SDL_SemWait (s->sem)) { 1241d14ffa9Sbellard sdl_logerr ("SDL_SemWait for %s failed\n", forfn); 12585571bc7Sbellard return -1; 12685571bc7Sbellard } 12785571bc7Sbellard return 0; 12885571bc7Sbellard } 129bcf19777SThomas Huth #endif 13085571bc7Sbellard 1311d14ffa9Sbellard static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) 13285571bc7Sbellard { 1331d14ffa9Sbellard if (sdl_unlock (s, forfn)) { 13485571bc7Sbellard return -1; 13585571bc7Sbellard } 13685571bc7Sbellard 1371d14ffa9Sbellard return sdl_post (s, forfn); 13885571bc7Sbellard } 13985571bc7Sbellard 1406c557ab9SSerge Ziryukin static int aud_to_sdlfmt (audfmt_e fmt) 14185571bc7Sbellard { 1421d14ffa9Sbellard switch (fmt) { 1431d14ffa9Sbellard case AUD_FMT_S8: 1441d14ffa9Sbellard return AUDIO_S8; 1451d14ffa9Sbellard 1461d14ffa9Sbellard case AUD_FMT_U8: 1471d14ffa9Sbellard return AUDIO_U8; 1481d14ffa9Sbellard 1491d14ffa9Sbellard case AUD_FMT_S16: 1501d14ffa9Sbellard return AUDIO_S16LSB; 1511d14ffa9Sbellard 1521d14ffa9Sbellard case AUD_FMT_U16: 1531d14ffa9Sbellard return AUDIO_U16LSB; 1541d14ffa9Sbellard 15585571bc7Sbellard default: 1561d14ffa9Sbellard dolog ("Internal logic error: Bad audio format %d\n", fmt); 1571d14ffa9Sbellard #ifdef DEBUG_AUDIO 1581d14ffa9Sbellard abort (); 1591d14ffa9Sbellard #endif 1601d14ffa9Sbellard return AUDIO_U8; 16185571bc7Sbellard } 16285571bc7Sbellard } 16385571bc7Sbellard 1644ff9786cSStefan Weil static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) 16585571bc7Sbellard { 1661d14ffa9Sbellard switch (sdlfmt) { 1671d14ffa9Sbellard case AUDIO_S8: 1684ff9786cSStefan Weil *endianness = 0; 1691d14ffa9Sbellard *fmt = AUD_FMT_S8; 1701d14ffa9Sbellard break; 1711d14ffa9Sbellard 1721d14ffa9Sbellard case AUDIO_U8: 1734ff9786cSStefan Weil *endianness = 0; 1741d14ffa9Sbellard *fmt = AUD_FMT_U8; 1751d14ffa9Sbellard break; 1761d14ffa9Sbellard 1771d14ffa9Sbellard case AUDIO_S16LSB: 1784ff9786cSStefan Weil *endianness = 0; 1791d14ffa9Sbellard *fmt = AUD_FMT_S16; 1801d14ffa9Sbellard break; 1811d14ffa9Sbellard 1821d14ffa9Sbellard case AUDIO_U16LSB: 1834ff9786cSStefan Weil *endianness = 0; 1841d14ffa9Sbellard *fmt = AUD_FMT_U16; 1851d14ffa9Sbellard break; 1861d14ffa9Sbellard 1871d14ffa9Sbellard case AUDIO_S16MSB: 1884ff9786cSStefan Weil *endianness = 1; 1891d14ffa9Sbellard *fmt = AUD_FMT_S16; 1901d14ffa9Sbellard break; 1911d14ffa9Sbellard 1921d14ffa9Sbellard case AUDIO_U16MSB: 1934ff9786cSStefan Weil *endianness = 1; 1941d14ffa9Sbellard *fmt = AUD_FMT_U16; 1951d14ffa9Sbellard break; 1961d14ffa9Sbellard 19785571bc7Sbellard default: 1981d14ffa9Sbellard dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 1991d14ffa9Sbellard return -1; 20085571bc7Sbellard } 2011d14ffa9Sbellard 2021d14ffa9Sbellard return 0; 20385571bc7Sbellard } 20485571bc7Sbellard 20585571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 20685571bc7Sbellard { 20785571bc7Sbellard int status; 208e784ba70Sths #ifndef _WIN32 209d087bb3eSmalc int err; 210e784ba70Sths sigset_t new, old; 211e784ba70Sths 212e784ba70Sths /* Make sure potential threads created by SDL don't hog signals. */ 213d087bb3eSmalc err = sigfillset (&new); 214d087bb3eSmalc if (err) { 215d087bb3eSmalc dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 21660592eddSmalc return -1; 217d087bb3eSmalc } 218d087bb3eSmalc err = pthread_sigmask (SIG_BLOCK, &new, &old); 219d087bb3eSmalc if (err) { 220d087bb3eSmalc dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 221d087bb3eSmalc return -1; 222d087bb3eSmalc } 223e784ba70Sths #endif 22485571bc7Sbellard 22585571bc7Sbellard status = SDL_OpenAudio (req, obt); 22685571bc7Sbellard if (status) { 2271d14ffa9Sbellard sdl_logerr ("SDL_OpenAudio failed\n"); 22885571bc7Sbellard } 229e784ba70Sths 230e784ba70Sths #ifndef _WIN32 231d087bb3eSmalc err = pthread_sigmask (SIG_SETMASK, &old, NULL); 232d087bb3eSmalc if (err) { 233d087bb3eSmalc dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 234d087bb3eSmalc strerror (errno)); 235d087bb3eSmalc /* We have failed to restore original signal mask, all bets are off, 236d087bb3eSmalc so exit the process */ 237d087bb3eSmalc exit (EXIT_FAILURE); 238d087bb3eSmalc } 239e784ba70Sths #endif 24085571bc7Sbellard return status; 24185571bc7Sbellard } 24285571bc7Sbellard 24385571bc7Sbellard static void sdl_close (SDLAudioState *s) 24485571bc7Sbellard { 24585571bc7Sbellard if (s->initialized) { 2461d14ffa9Sbellard sdl_lock (s, "sdl_close"); 24785571bc7Sbellard s->exit = 1; 2481d14ffa9Sbellard sdl_unlock_and_post (s, "sdl_close"); 24985571bc7Sbellard SDL_PauseAudio (1); 25085571bc7Sbellard SDL_CloseAudio (); 25185571bc7Sbellard s->initialized = 0; 25285571bc7Sbellard } 25385571bc7Sbellard } 25485571bc7Sbellard 25585571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len) 25685571bc7Sbellard { 2571d14ffa9Sbellard SDLVoiceOut *sdl = opaque; 25885571bc7Sbellard SDLAudioState *s = &glob_sdl; 2591d14ffa9Sbellard HWVoiceOut *hw = &sdl->hw; 2601d14ffa9Sbellard int samples = len >> hw->info.shift; 26185571bc7Sbellard 26285571bc7Sbellard if (s->exit) { 26385571bc7Sbellard return; 26485571bc7Sbellard } 26585571bc7Sbellard 26685571bc7Sbellard while (samples) { 2671d14ffa9Sbellard int to_mix, decr; 26885571bc7Sbellard 269ff541499Smalc /* dolog ("in callback samples=%d\n", samples); */ 270bcf19777SThomas Huth #if USE_SEMAPHORE 2711d14ffa9Sbellard sdl_wait (s, "sdl_callback"); 27285571bc7Sbellard if (s->exit) { 27385571bc7Sbellard return; 27485571bc7Sbellard } 27585571bc7Sbellard 2761d14ffa9Sbellard if (sdl_lock (s, "sdl_callback")) { 2771d14ffa9Sbellard return; 2781d14ffa9Sbellard } 279ff541499Smalc 280470bcabdSAlistair Francis if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) { 281ff541499Smalc dolog ("sdl->live=%d hw->samples=%d\n", 282ff541499Smalc sdl->live, hw->samples); 283ff541499Smalc return; 2841d14ffa9Sbellard } 2851d14ffa9Sbellard 286ff541499Smalc if (!sdl->live) { 287ff541499Smalc goto again; 288ff541499Smalc } 289bcf19777SThomas Huth #else 290bcf19777SThomas Huth if (s->exit || !sdl->live) { 291bcf19777SThomas Huth break; 292bcf19777SThomas Huth } 293bcf19777SThomas Huth #endif 294ff541499Smalc 295ff541499Smalc /* dolog ("in callback live=%d\n", live); */ 296ff541499Smalc to_mix = audio_MIN (samples, sdl->live); 297ff541499Smalc decr = to_mix; 298ff541499Smalc while (to_mix) { 299ff541499Smalc int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); 300ff541499Smalc struct st_sample *src = hw->mix_buf + hw->rpos; 301ff541499Smalc 302ff541499Smalc /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 303ff541499Smalc hw->clip (buf, src, chunk); 304bcf19777SThomas Huth #if USE_SEMAPHORE 305ff541499Smalc sdl->rpos = (sdl->rpos + chunk) % hw->samples; 306bcf19777SThomas Huth #else 307bcf19777SThomas Huth hw->rpos = (hw->rpos + chunk) % hw->samples; 308bcf19777SThomas Huth #endif 309ff541499Smalc to_mix -= chunk; 310ff541499Smalc buf += chunk << hw->info.shift; 311ff541499Smalc } 31285571bc7Sbellard samples -= decr; 313ff541499Smalc sdl->live -= decr; 3141d14ffa9Sbellard sdl->decr += decr; 31585571bc7Sbellard 316bcf19777SThomas Huth #if USE_SEMAPHORE 317ff541499Smalc again: 3181d14ffa9Sbellard if (sdl_unlock (s, "sdl_callback")) { 3191d14ffa9Sbellard return; 3201d14ffa9Sbellard } 321bcf19777SThomas Huth #endif 32285571bc7Sbellard } 323ff541499Smalc /* dolog ("done len=%d\n", len); */ 324bcf19777SThomas Huth 325bcf19777SThomas Huth #if (SDL_MAJOR_VERSION >= 2) 326bcf19777SThomas Huth /* SDL2 does not clear the remaining buffer for us, so do it on our own */ 327bcf19777SThomas Huth if (samples) { 328bcf19777SThomas Huth memset(buf, 0, samples << hw->info.shift); 329bcf19777SThomas Huth } 330bcf19777SThomas Huth #endif 331ff541499Smalc } 33285571bc7Sbellard 3331d14ffa9Sbellard static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 33485571bc7Sbellard { 3351d14ffa9Sbellard return audio_pcm_sw_write (sw, buf, len); 3361d14ffa9Sbellard } 3371d14ffa9Sbellard 338bdff253cSmalc static int sdl_run_out (HWVoiceOut *hw, int live) 3391d14ffa9Sbellard { 340bdff253cSmalc int decr; 3411d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 3421d14ffa9Sbellard SDLAudioState *s = &glob_sdl; 3431d14ffa9Sbellard 3443fd7f635Smalc if (sdl_lock (s, "sdl_run_out")) { 3451d14ffa9Sbellard return 0; 3461d14ffa9Sbellard } 3471d14ffa9Sbellard 348ff541499Smalc if (sdl->decr > live) { 349ff541499Smalc ldebug ("sdl->decr %d live %d sdl->live %d\n", 350ff541499Smalc sdl->decr, 351ff541499Smalc live, 352ff541499Smalc sdl->live); 353ff541499Smalc } 354ff541499Smalc 355ff541499Smalc decr = audio_MIN (sdl->decr, live); 356ff541499Smalc sdl->decr -= decr; 357ff541499Smalc 358bcf19777SThomas Huth #if USE_SEMAPHORE 359ff541499Smalc sdl->live = live - decr; 360ff541499Smalc hw->rpos = sdl->rpos; 361bcf19777SThomas Huth #else 362bcf19777SThomas Huth sdl->live = live; 363bcf19777SThomas Huth #endif 3641d14ffa9Sbellard 3651d14ffa9Sbellard if (sdl->live > 0) { 3663fd7f635Smalc sdl_unlock_and_post (s, "sdl_run_out"); 3671d14ffa9Sbellard } 3681d14ffa9Sbellard else { 3693fd7f635Smalc sdl_unlock (s, "sdl_run_out"); 3701d14ffa9Sbellard } 3711d14ffa9Sbellard return decr; 3721d14ffa9Sbellard } 3731d14ffa9Sbellard 3741d14ffa9Sbellard static void sdl_fini_out (HWVoiceOut *hw) 3751d14ffa9Sbellard { 3761d14ffa9Sbellard (void) hw; 3771d14ffa9Sbellard 37885571bc7Sbellard sdl_close (&glob_sdl); 37985571bc7Sbellard } 38085571bc7Sbellard 3815706db1dSKővágó, Zoltán static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 3825706db1dSKővágó, Zoltán void *drv_opaque) 38385571bc7Sbellard { 3841d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 38585571bc7Sbellard SDLAudioState *s = &glob_sdl; 38685571bc7Sbellard SDL_AudioSpec req, obt; 3874ff9786cSStefan Weil int endianness; 3881d14ffa9Sbellard int err; 3891d14ffa9Sbellard audfmt_e effective_fmt; 3901ea879e5Smalc struct audsettings obt_as; 39185571bc7Sbellard 392c0fe3827Sbellard req.freq = as->freq; 3936c557ab9SSerge Ziryukin req.format = aud_to_sdlfmt (as->fmt); 394c0fe3827Sbellard req.channels = as->nchannels; 39585571bc7Sbellard req.samples = conf.nb_samples; 39685571bc7Sbellard req.callback = sdl_callback; 39785571bc7Sbellard req.userdata = sdl; 39885571bc7Sbellard 3991d14ffa9Sbellard if (sdl_open (&req, &obt)) { 40085571bc7Sbellard return -1; 4011d14ffa9Sbellard } 40285571bc7Sbellard 4034ff9786cSStefan Weil err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 4041d14ffa9Sbellard if (err) { 4051d14ffa9Sbellard sdl_close (s); 4061d14ffa9Sbellard return -1; 4071d14ffa9Sbellard } 4081d14ffa9Sbellard 409c0fe3827Sbellard obt_as.freq = obt.freq; 410c0fe3827Sbellard obt_as.nchannels = obt.channels; 411c0fe3827Sbellard obt_as.fmt = effective_fmt; 4124ff9786cSStefan Weil obt_as.endianness = endianness; 413c0fe3827Sbellard 414d929eba5Sbellard audio_pcm_init_info (&hw->info, &obt_as); 415c0fe3827Sbellard hw->samples = obt.samples; 41685571bc7Sbellard 41785571bc7Sbellard s->initialized = 1; 41885571bc7Sbellard s->exit = 0; 41985571bc7Sbellard SDL_PauseAudio (0); 42085571bc7Sbellard return 0; 42185571bc7Sbellard } 42285571bc7Sbellard 4231d14ffa9Sbellard static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 42485571bc7Sbellard { 42585571bc7Sbellard (void) hw; 42685571bc7Sbellard 42785571bc7Sbellard switch (cmd) { 42885571bc7Sbellard case VOICE_ENABLE: 42985571bc7Sbellard SDL_PauseAudio (0); 43085571bc7Sbellard break; 43185571bc7Sbellard 43285571bc7Sbellard case VOICE_DISABLE: 43385571bc7Sbellard SDL_PauseAudio (1); 43485571bc7Sbellard break; 43585571bc7Sbellard } 43685571bc7Sbellard return 0; 43785571bc7Sbellard } 43885571bc7Sbellard 43985571bc7Sbellard static void *sdl_audio_init (void) 44085571bc7Sbellard { 44185571bc7Sbellard SDLAudioState *s = &glob_sdl; 44281ebb07cSKővágó, Zoltán if (s->driver_created) { 44381ebb07cSKővágó, Zoltán sdl_logerr("Can't create multiple sdl backends\n"); 44481ebb07cSKővágó, Zoltán return NULL; 44581ebb07cSKővágó, Zoltán } 44685571bc7Sbellard 44785571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 4481d14ffa9Sbellard sdl_logerr ("SDL failed to initialize audio subsystem\n"); 44985571bc7Sbellard return NULL; 45085571bc7Sbellard } 45185571bc7Sbellard 452bcf19777SThomas Huth #if USE_SEMAPHORE 45385571bc7Sbellard s->mutex = SDL_CreateMutex (); 45485571bc7Sbellard if (!s->mutex) { 4551d14ffa9Sbellard sdl_logerr ("Failed to create SDL mutex\n"); 45685571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 45785571bc7Sbellard return NULL; 45885571bc7Sbellard } 45985571bc7Sbellard 46085571bc7Sbellard s->sem = SDL_CreateSemaphore (0); 46185571bc7Sbellard if (!s->sem) { 4621d14ffa9Sbellard sdl_logerr ("Failed to create SDL semaphore\n"); 46385571bc7Sbellard SDL_DestroyMutex (s->mutex); 46485571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 46585571bc7Sbellard return NULL; 46685571bc7Sbellard } 467bcf19777SThomas Huth #endif 46885571bc7Sbellard 46981ebb07cSKővágó, Zoltán s->driver_created = true; 47085571bc7Sbellard return s; 47185571bc7Sbellard } 47285571bc7Sbellard 47385571bc7Sbellard static void sdl_audio_fini (void *opaque) 47485571bc7Sbellard { 47585571bc7Sbellard SDLAudioState *s = opaque; 47685571bc7Sbellard sdl_close (s); 477bcf19777SThomas Huth #if USE_SEMAPHORE 47885571bc7Sbellard SDL_DestroySemaphore (s->sem); 47985571bc7Sbellard SDL_DestroyMutex (s->mutex); 480bcf19777SThomas Huth #endif 48185571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 48281ebb07cSKővágó, Zoltán s->driver_created = false; 48385571bc7Sbellard } 48485571bc7Sbellard 4851d14ffa9Sbellard static struct audio_option sdl_options[] = { 48698f9f48cSmalc { 48798f9f48cSmalc .name = "SAMPLES", 4882700efa3SJuan Quintela .tag = AUD_OPT_INT, 4892700efa3SJuan Quintela .valp = &conf.nb_samples, 49098f9f48cSmalc .descr = "Size of SDL buffer in samples" 49198f9f48cSmalc }, 4922700efa3SJuan Quintela { /* End of list */ } 49385571bc7Sbellard }; 49485571bc7Sbellard 49535f4b58cSblueswir1 static struct audio_pcm_ops sdl_pcm_ops = { 4961dd3e4d1SJuan Quintela .init_out = sdl_init_out, 4971dd3e4d1SJuan Quintela .fini_out = sdl_fini_out, 4981dd3e4d1SJuan Quintela .run_out = sdl_run_out, 4991dd3e4d1SJuan Quintela .write = sdl_write_out, 5001dd3e4d1SJuan Quintela .ctl_out = sdl_ctl_out, 5011d14ffa9Sbellard }; 5021d14ffa9Sbellard 503*d3893a39SGerd Hoffmann static struct audio_driver sdl_audio_driver = { 504bee37f32SJuan Quintela .name = "sdl", 505bee37f32SJuan Quintela .descr = "SDL http://www.libsdl.org", 506bee37f32SJuan Quintela .options = sdl_options, 507bee37f32SJuan Quintela .init = sdl_audio_init, 508bee37f32SJuan Quintela .fini = sdl_audio_fini, 509bee37f32SJuan Quintela .pcm_ops = &sdl_pcm_ops, 510bee37f32SJuan Quintela .can_be_default = 1, 511bee37f32SJuan Quintela .max_voices_out = 1, 512bee37f32SJuan Quintela .max_voices_in = 0, 513bee37f32SJuan Quintela .voice_size_out = sizeof (SDLVoiceOut), 514bee37f32SJuan Quintela .voice_size_in = 0 51585571bc7Sbellard }; 516*d3893a39SGerd Hoffmann 517*d3893a39SGerd Hoffmann static void register_audio_sdl(void) 518*d3893a39SGerd Hoffmann { 519*d3893a39SGerd Hoffmann audio_driver_register(&sdl_audio_driver); 520*d3893a39SGerd Hoffmann } 521*d3893a39SGerd Hoffmann type_init(register_audio_sdl); 522