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 */ 240b8fa32fSMarkus Armbruster 256086a565SPeter Maydell #include "qemu/osdep.h" 269f059ecaSbellard #include <SDL.h> 279f059ecaSbellard #include <SDL_thread.h> 280b8fa32fSMarkus Armbruster #include "qemu/module.h" 2987ecb68bSpbrook #include "audio.h" 3085571bc7Sbellard 31e784ba70Sths #ifndef _WIN32 32e784ba70Sths #ifdef __sun__ 33e784ba70Sths #define _POSIX_PTHREAD_SEMANTICS 1 34c5e97233Sblueswir1 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 359b4c14c3Sblueswir1 #include <pthread.h> 36e784ba70Sths #endif 37e784ba70Sths #endif 38e784ba70Sths 391d14ffa9Sbellard #define AUDIO_CAP "sdl" 401d14ffa9Sbellard #include "audio_int.h" 41fb065187Sbellard 421d14ffa9Sbellard typedef struct SDLVoiceOut { 431d14ffa9Sbellard HWVoiceOut hw; 441d14ffa9Sbellard } SDLVoiceOut; 4585571bc7Sbellard 46b1d8e52eSblueswir1 static struct SDLAudioState { 4785571bc7Sbellard int exit; 4885571bc7Sbellard int initialized; 4981ebb07cSKővágó, Zoltán bool driver_created; 5057dea553SKővágó, Zoltán Audiodev *dev; 5185571bc7Sbellard } glob_sdl; 5285571bc7Sbellard typedef struct SDLAudioState SDLAudioState; 5385571bc7Sbellard 541d14ffa9Sbellard static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 5585571bc7Sbellard { 561d14ffa9Sbellard va_list ap; 571d14ffa9Sbellard 581d14ffa9Sbellard va_start (ap, fmt); 591d14ffa9Sbellard AUD_vlog (AUDIO_CAP, fmt, ap); 601d14ffa9Sbellard va_end (ap); 611d14ffa9Sbellard 621d14ffa9Sbellard AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 6385571bc7Sbellard } 6485571bc7Sbellard 6585bc5852SKővágó, Zoltán static int aud_to_sdlfmt (AudioFormat fmt) 6685571bc7Sbellard { 671d14ffa9Sbellard switch (fmt) { 6885bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 691d14ffa9Sbellard return AUDIO_S8; 701d14ffa9Sbellard 7185bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 721d14ffa9Sbellard return AUDIO_U8; 731d14ffa9Sbellard 7485bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 751d14ffa9Sbellard return AUDIO_S16LSB; 761d14ffa9Sbellard 7785bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 781d14ffa9Sbellard return AUDIO_U16LSB; 791d14ffa9Sbellard 8085571bc7Sbellard default: 811d14ffa9Sbellard dolog ("Internal logic error: Bad audio format %d\n", fmt); 821d14ffa9Sbellard #ifdef DEBUG_AUDIO 831d14ffa9Sbellard abort (); 841d14ffa9Sbellard #endif 851d14ffa9Sbellard return AUDIO_U8; 8685571bc7Sbellard } 8785571bc7Sbellard } 8885571bc7Sbellard 8985bc5852SKővágó, Zoltán static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) 9085571bc7Sbellard { 911d14ffa9Sbellard switch (sdlfmt) { 921d14ffa9Sbellard case AUDIO_S8: 934ff9786cSStefan Weil *endianness = 0; 9485bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S8; 951d14ffa9Sbellard break; 961d14ffa9Sbellard 971d14ffa9Sbellard case AUDIO_U8: 984ff9786cSStefan Weil *endianness = 0; 9985bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U8; 1001d14ffa9Sbellard break; 1011d14ffa9Sbellard 1021d14ffa9Sbellard case AUDIO_S16LSB: 1034ff9786cSStefan Weil *endianness = 0; 10485bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1051d14ffa9Sbellard break; 1061d14ffa9Sbellard 1071d14ffa9Sbellard case AUDIO_U16LSB: 1084ff9786cSStefan Weil *endianness = 0; 10985bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1101d14ffa9Sbellard break; 1111d14ffa9Sbellard 1121d14ffa9Sbellard case AUDIO_S16MSB: 1134ff9786cSStefan Weil *endianness = 1; 11485bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1151d14ffa9Sbellard break; 1161d14ffa9Sbellard 1171d14ffa9Sbellard case AUDIO_U16MSB: 1184ff9786cSStefan Weil *endianness = 1; 11985bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1201d14ffa9Sbellard break; 1211d14ffa9Sbellard 12285571bc7Sbellard default: 1231d14ffa9Sbellard dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 1241d14ffa9Sbellard return -1; 12585571bc7Sbellard } 1261d14ffa9Sbellard 1271d14ffa9Sbellard return 0; 12885571bc7Sbellard } 12985571bc7Sbellard 13085571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 13185571bc7Sbellard { 13285571bc7Sbellard int status; 133e784ba70Sths #ifndef _WIN32 134d087bb3eSmalc int err; 135e784ba70Sths sigset_t new, old; 136e784ba70Sths 137e784ba70Sths /* Make sure potential threads created by SDL don't hog signals. */ 138d087bb3eSmalc err = sigfillset (&new); 139d087bb3eSmalc if (err) { 140d087bb3eSmalc dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 14160592eddSmalc return -1; 142d087bb3eSmalc } 143d087bb3eSmalc err = pthread_sigmask (SIG_BLOCK, &new, &old); 144d087bb3eSmalc if (err) { 145d087bb3eSmalc dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 146d087bb3eSmalc return -1; 147d087bb3eSmalc } 148e784ba70Sths #endif 14985571bc7Sbellard 15085571bc7Sbellard status = SDL_OpenAudio (req, obt); 15185571bc7Sbellard if (status) { 1521d14ffa9Sbellard sdl_logerr ("SDL_OpenAudio failed\n"); 15385571bc7Sbellard } 154e784ba70Sths 155e784ba70Sths #ifndef _WIN32 156d087bb3eSmalc err = pthread_sigmask (SIG_SETMASK, &old, NULL); 157d087bb3eSmalc if (err) { 158d087bb3eSmalc dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 159d087bb3eSmalc strerror (errno)); 160d087bb3eSmalc /* We have failed to restore original signal mask, all bets are off, 161d087bb3eSmalc so exit the process */ 162d087bb3eSmalc exit (EXIT_FAILURE); 163d087bb3eSmalc } 164e784ba70Sths #endif 16585571bc7Sbellard return status; 16685571bc7Sbellard } 16785571bc7Sbellard 16885571bc7Sbellard static void sdl_close (SDLAudioState *s) 16985571bc7Sbellard { 17085571bc7Sbellard if (s->initialized) { 1718a7816c4SThomas Huth SDL_LockAudio(); 17285571bc7Sbellard s->exit = 1; 1738a7816c4SThomas Huth SDL_UnlockAudio(); 17485571bc7Sbellard SDL_PauseAudio (1); 17585571bc7Sbellard SDL_CloseAudio (); 17685571bc7Sbellard s->initialized = 0; 17785571bc7Sbellard } 17885571bc7Sbellard } 17985571bc7Sbellard 18085571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len) 18185571bc7Sbellard { 1821d14ffa9Sbellard SDLVoiceOut *sdl = opaque; 18385571bc7Sbellard SDLAudioState *s = &glob_sdl; 1841d14ffa9Sbellard HWVoiceOut *hw = &sdl->hw; 18585571bc7Sbellard 186ff718767SKővágó, Zoltán if (s->exit) { 18785571bc7Sbellard return; 18885571bc7Sbellard } 18985571bc7Sbellard 1907520462bSKővágó, Zoltán /* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */ 19185571bc7Sbellard 192ff718767SKővágó, Zoltán while (hw->pending_emul && len) { 193ff718767SKővágó, Zoltán size_t write_len; 194ff718767SKővágó, Zoltán ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; 195ff718767SKővágó, Zoltán if (start < 0) { 196ff718767SKővágó, Zoltán start += hw->size_emul; 197ff541499Smalc } 198ff718767SKővágó, Zoltán assert(start >= 0 && start < hw->size_emul); 1999399ef16SThomas Huth 200ff718767SKővágó, Zoltán write_len = MIN(MIN(hw->pending_emul, len), 201ff718767SKővágó, Zoltán hw->size_emul - start); 202bcf19777SThomas Huth 203ff718767SKővágó, Zoltán memcpy(buf, hw->buf_emul + start, write_len); 204ff718767SKővágó, Zoltán hw->pending_emul -= write_len; 205ff718767SKővágó, Zoltán len -= write_len; 206ff718767SKővágó, Zoltán buf += write_len; 207ff718767SKővágó, Zoltán } 208ff718767SKővágó, Zoltán 209ff718767SKővágó, Zoltán /* clear remaining buffer that we couldn't fill with data */ 210ff718767SKővágó, Zoltán if (len) { 211ff718767SKővágó, Zoltán memset(buf, 0, len); 212bcf19777SThomas Huth } 213ff541499Smalc } 21485571bc7Sbellard 215ff718767SKővágó, Zoltán #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \ 216ff718767SKővágó, Zoltán static ret_type glue(sdl_, name)args_decl \ 217ff718767SKővágó, Zoltán { \ 218ff718767SKővágó, Zoltán ret_type ret; \ 219ff718767SKővágó, Zoltán \ 220ff718767SKővágó, Zoltán SDL_LockAudio(); \ 221ff718767SKővágó, Zoltán \ 222ff718767SKővágó, Zoltán ret = glue(audio_generic_, name)args; \ 223ff718767SKővágó, Zoltán \ 224ff718767SKővágó, Zoltán SDL_UnlockAudio(); \ 225ff718767SKővágó, Zoltán return ret; \ 226ff541499Smalc } 227ff541499Smalc 228ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), 229ff718767SKővágó, Zoltán (hw, size), *size = 0, sdl_unlock) 230ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, 231ff718767SKővágó, Zoltán (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), 232ff718767SKővágó, Zoltán /*nothing*/, sdl_unlock_and_post) 233ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(write, size_t, 234ff718767SKővágó, Zoltán (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), 235ff718767SKővágó, Zoltán /*nothing*/, sdl_unlock_and_post) 236ff541499Smalc 237ff718767SKővágó, Zoltán #undef SDL_WRAPPER_FUNC 2381d14ffa9Sbellard 2391d14ffa9Sbellard static void sdl_fini_out (HWVoiceOut *hw) 2401d14ffa9Sbellard { 2411d14ffa9Sbellard (void) hw; 2421d14ffa9Sbellard 24385571bc7Sbellard sdl_close (&glob_sdl); 24485571bc7Sbellard } 24585571bc7Sbellard 2465706db1dSKővágó, Zoltán static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 2475706db1dSKővágó, Zoltán void *drv_opaque) 24885571bc7Sbellard { 2491d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 25085571bc7Sbellard SDLAudioState *s = &glob_sdl; 25185571bc7Sbellard SDL_AudioSpec req, obt; 2524ff9786cSStefan Weil int endianness; 2531d14ffa9Sbellard int err; 25485bc5852SKővágó, Zoltán AudioFormat effective_fmt; 2551ea879e5Smalc struct audsettings obt_as; 25685571bc7Sbellard 257c0fe3827Sbellard req.freq = as->freq; 2586c557ab9SSerge Ziryukin req.format = aud_to_sdlfmt (as->fmt); 259c0fe3827Sbellard req.channels = as->nchannels; 26057dea553SKővágó, Zoltán req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); 26185571bc7Sbellard req.callback = sdl_callback; 26285571bc7Sbellard req.userdata = sdl; 26385571bc7Sbellard 2641d14ffa9Sbellard if (sdl_open (&req, &obt)) { 26585571bc7Sbellard return -1; 2661d14ffa9Sbellard } 26785571bc7Sbellard 2684ff9786cSStefan Weil err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 2691d14ffa9Sbellard if (err) { 2701d14ffa9Sbellard sdl_close (s); 2711d14ffa9Sbellard return -1; 2721d14ffa9Sbellard } 2731d14ffa9Sbellard 274c0fe3827Sbellard obt_as.freq = obt.freq; 275c0fe3827Sbellard obt_as.nchannels = obt.channels; 276c0fe3827Sbellard obt_as.fmt = effective_fmt; 2774ff9786cSStefan Weil obt_as.endianness = endianness; 278c0fe3827Sbellard 279d929eba5Sbellard audio_pcm_init_info (&hw->info, &obt_as); 280c0fe3827Sbellard hw->samples = obt.samples; 28185571bc7Sbellard 28285571bc7Sbellard s->initialized = 1; 28385571bc7Sbellard s->exit = 0; 28485571bc7Sbellard SDL_PauseAudio (0); 28585571bc7Sbellard return 0; 28685571bc7Sbellard } 28785571bc7Sbellard 288*571a8c52SKővágó, Zoltán static void sdl_enable_out(HWVoiceOut *hw, bool enable) 28985571bc7Sbellard { 290*571a8c52SKővágó, Zoltán SDL_PauseAudio(!enable); 29185571bc7Sbellard } 29285571bc7Sbellard 29371830221SKővágó, Zoltán static void *sdl_audio_init(Audiodev *dev) 29485571bc7Sbellard { 29585571bc7Sbellard SDLAudioState *s = &glob_sdl; 29681ebb07cSKővágó, Zoltán if (s->driver_created) { 29781ebb07cSKővágó, Zoltán sdl_logerr("Can't create multiple sdl backends\n"); 29881ebb07cSKővágó, Zoltán return NULL; 29981ebb07cSKővágó, Zoltán } 30085571bc7Sbellard 30185571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 3021d14ffa9Sbellard sdl_logerr ("SDL failed to initialize audio subsystem\n"); 30385571bc7Sbellard return NULL; 30485571bc7Sbellard } 30585571bc7Sbellard 30681ebb07cSKővágó, Zoltán s->driver_created = true; 30757dea553SKővágó, Zoltán s->dev = dev; 30885571bc7Sbellard return s; 30985571bc7Sbellard } 31085571bc7Sbellard 31185571bc7Sbellard static void sdl_audio_fini (void *opaque) 31285571bc7Sbellard { 31385571bc7Sbellard SDLAudioState *s = opaque; 31485571bc7Sbellard sdl_close (s); 31585571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 31681ebb07cSKővágó, Zoltán s->driver_created = false; 31757dea553SKővágó, Zoltán s->dev = NULL; 31885571bc7Sbellard } 31985571bc7Sbellard 32035f4b58cSblueswir1 static struct audio_pcm_ops sdl_pcm_ops = { 3211dd3e4d1SJuan Quintela .init_out = sdl_init_out, 3221dd3e4d1SJuan Quintela .fini_out = sdl_fini_out, 323ff718767SKővágó, Zoltán .write = sdl_write, 324ff718767SKővágó, Zoltán .get_buffer_out = sdl_get_buffer_out, 325ff718767SKővágó, Zoltán .put_buffer_out = sdl_put_buffer_out_nowrite, 326*571a8c52SKővágó, Zoltán .enable_out = sdl_enable_out, 3271d14ffa9Sbellard }; 3281d14ffa9Sbellard 329d3893a39SGerd Hoffmann static struct audio_driver sdl_audio_driver = { 330bee37f32SJuan Quintela .name = "sdl", 331bee37f32SJuan Quintela .descr = "SDL http://www.libsdl.org", 332bee37f32SJuan Quintela .init = sdl_audio_init, 333bee37f32SJuan Quintela .fini = sdl_audio_fini, 334bee37f32SJuan Quintela .pcm_ops = &sdl_pcm_ops, 335bee37f32SJuan Quintela .can_be_default = 1, 336bee37f32SJuan Quintela .max_voices_out = 1, 337bee37f32SJuan Quintela .max_voices_in = 0, 338bee37f32SJuan Quintela .voice_size_out = sizeof (SDLVoiceOut), 339bee37f32SJuan Quintela .voice_size_in = 0 34085571bc7Sbellard }; 341d3893a39SGerd Hoffmann 342d3893a39SGerd Hoffmann static void register_audio_sdl(void) 343d3893a39SGerd Hoffmann { 344d3893a39SGerd Hoffmann audio_driver_register(&sdl_audio_driver); 345d3893a39SGerd Hoffmann } 346d3893a39SGerd Hoffmann type_init(register_audio_sdl); 347