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" 29*f6061733SPaolo Bonzini #include "qapi/error.h" 3087ecb68bSpbrook #include "audio.h" 3185571bc7Sbellard 32e784ba70Sths #ifndef _WIN32 33e784ba70Sths #ifdef __sun__ 34e784ba70Sths #define _POSIX_PTHREAD_SEMANTICS 1 35c5e97233Sblueswir1 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 369b4c14c3Sblueswir1 #include <pthread.h> 37e784ba70Sths #endif 38e784ba70Sths #endif 39e784ba70Sths 401d14ffa9Sbellard #define AUDIO_CAP "sdl" 411d14ffa9Sbellard #include "audio_int.h" 42fb065187Sbellard 431d14ffa9Sbellard typedef struct SDLVoiceOut { 441d14ffa9Sbellard HWVoiceOut hw; 4585571bc7Sbellard int exit; 4685571bc7Sbellard int initialized; 4757dea553SKővágó, Zoltán Audiodev *dev; 48ce31f099SVolker Rümelin SDL_AudioDeviceID devid; 49ce31f099SVolker Rümelin } SDLVoiceOut; 5085571bc7Sbellard 51c2031deaSVolker Rümelin typedef struct SDLVoiceIn { 52c2031deaSVolker Rümelin HWVoiceIn hw; 53c2031deaSVolker Rümelin int exit; 54c2031deaSVolker Rümelin int initialized; 55c2031deaSVolker Rümelin Audiodev *dev; 56c2031deaSVolker Rümelin SDL_AudioDeviceID devid; 57c2031deaSVolker Rümelin } SDLVoiceIn; 58c2031deaSVolker Rümelin 599edc6313SMarc-André Lureau static void G_GNUC_PRINTF (1, 2) sdl_logerr (const char *fmt, ...) 6085571bc7Sbellard { 611d14ffa9Sbellard va_list ap; 621d14ffa9Sbellard 631d14ffa9Sbellard va_start (ap, fmt); 641d14ffa9Sbellard AUD_vlog (AUDIO_CAP, fmt, ap); 651d14ffa9Sbellard va_end (ap); 661d14ffa9Sbellard 671d14ffa9Sbellard AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 6885571bc7Sbellard } 6985571bc7Sbellard 7085bc5852SKővágó, Zoltán static int aud_to_sdlfmt (AudioFormat fmt) 7185571bc7Sbellard { 721d14ffa9Sbellard switch (fmt) { 7385bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 741d14ffa9Sbellard return AUDIO_S8; 751d14ffa9Sbellard 7685bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 771d14ffa9Sbellard return AUDIO_U8; 781d14ffa9Sbellard 7985bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 801d14ffa9Sbellard return AUDIO_S16LSB; 811d14ffa9Sbellard 8285bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 831d14ffa9Sbellard return AUDIO_U16LSB; 841d14ffa9Sbellard 85ed2a4a79SKővágó, Zoltán case AUDIO_FORMAT_S32: 86ed2a4a79SKővágó, Zoltán return AUDIO_S32LSB; 87ed2a4a79SKővágó, Zoltán 88ed2a4a79SKővágó, Zoltán /* no unsigned 32-bit support in SDL */ 89ed2a4a79SKővágó, Zoltán 90ed2a4a79SKővágó, Zoltán case AUDIO_FORMAT_F32: 91ed2a4a79SKővágó, Zoltán return AUDIO_F32LSB; 92ed2a4a79SKővágó, Zoltán 9385571bc7Sbellard default: 941d14ffa9Sbellard dolog ("Internal logic error: Bad audio format %d\n", fmt); 951d14ffa9Sbellard #ifdef DEBUG_AUDIO 961d14ffa9Sbellard abort (); 971d14ffa9Sbellard #endif 981d14ffa9Sbellard return AUDIO_U8; 9985571bc7Sbellard } 10085571bc7Sbellard } 10185571bc7Sbellard 10285bc5852SKővágó, Zoltán static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) 10385571bc7Sbellard { 1041d14ffa9Sbellard switch (sdlfmt) { 1051d14ffa9Sbellard case AUDIO_S8: 1064ff9786cSStefan Weil *endianness = 0; 10785bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S8; 1081d14ffa9Sbellard break; 1091d14ffa9Sbellard 1101d14ffa9Sbellard case AUDIO_U8: 1114ff9786cSStefan Weil *endianness = 0; 11285bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U8; 1131d14ffa9Sbellard break; 1141d14ffa9Sbellard 1151d14ffa9Sbellard case AUDIO_S16LSB: 1164ff9786cSStefan Weil *endianness = 0; 11785bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1181d14ffa9Sbellard break; 1191d14ffa9Sbellard 1201d14ffa9Sbellard case AUDIO_U16LSB: 1214ff9786cSStefan Weil *endianness = 0; 12285bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1231d14ffa9Sbellard break; 1241d14ffa9Sbellard 1251d14ffa9Sbellard case AUDIO_S16MSB: 1264ff9786cSStefan Weil *endianness = 1; 12785bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1281d14ffa9Sbellard break; 1291d14ffa9Sbellard 1301d14ffa9Sbellard case AUDIO_U16MSB: 1314ff9786cSStefan Weil *endianness = 1; 13285bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1331d14ffa9Sbellard break; 1341d14ffa9Sbellard 135ed2a4a79SKővágó, Zoltán case AUDIO_S32LSB: 136ed2a4a79SKővágó, Zoltán *endianness = 0; 137ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_S32; 138ed2a4a79SKővágó, Zoltán break; 139ed2a4a79SKővágó, Zoltán 140ed2a4a79SKővágó, Zoltán case AUDIO_S32MSB: 141ed2a4a79SKővágó, Zoltán *endianness = 1; 142ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_S32; 143ed2a4a79SKővágó, Zoltán break; 144ed2a4a79SKővágó, Zoltán 145ed2a4a79SKővágó, Zoltán case AUDIO_F32LSB: 146ed2a4a79SKővágó, Zoltán *endianness = 0; 147ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_F32; 148ed2a4a79SKővágó, Zoltán break; 149ed2a4a79SKővágó, Zoltán 150ed2a4a79SKővágó, Zoltán case AUDIO_F32MSB: 151ed2a4a79SKővágó, Zoltán *endianness = 1; 152ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_F32; 153ed2a4a79SKővágó, Zoltán break; 154ed2a4a79SKővágó, Zoltán 15585571bc7Sbellard default: 1561d14ffa9Sbellard dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 1571d14ffa9Sbellard return -1; 15885571bc7Sbellard } 1591d14ffa9Sbellard 1601d14ffa9Sbellard return 0; 16185571bc7Sbellard } 16285571bc7Sbellard 163ce31f099SVolker Rümelin static SDL_AudioDeviceID sdl_open(SDL_AudioSpec *req, SDL_AudioSpec *obt, 164ce31f099SVolker Rümelin int rec) 16585571bc7Sbellard { 166ce31f099SVolker Rümelin SDL_AudioDeviceID devid; 167e784ba70Sths #ifndef _WIN32 168d087bb3eSmalc int err; 169e784ba70Sths sigset_t new, old; 170e784ba70Sths 171e784ba70Sths /* Make sure potential threads created by SDL don't hog signals. */ 172d087bb3eSmalc err = sigfillset (&new); 173d087bb3eSmalc if (err) { 174d087bb3eSmalc dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 175ce31f099SVolker Rümelin return 0; 176d087bb3eSmalc } 177d087bb3eSmalc err = pthread_sigmask (SIG_BLOCK, &new, &old); 178d087bb3eSmalc if (err) { 179d087bb3eSmalc dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 180ce31f099SVolker Rümelin return 0; 181d087bb3eSmalc } 182e784ba70Sths #endif 18385571bc7Sbellard 184ce31f099SVolker Rümelin devid = SDL_OpenAudioDevice(NULL, rec, req, obt, 0); 185ce31f099SVolker Rümelin if (!devid) { 186ce31f099SVolker Rümelin sdl_logerr("SDL_OpenAudioDevice for %s failed\n", 187ce31f099SVolker Rümelin rec ? "recording" : "playback"); 18885571bc7Sbellard } 189e784ba70Sths 190e784ba70Sths #ifndef _WIN32 191d087bb3eSmalc err = pthread_sigmask (SIG_SETMASK, &old, NULL); 192d087bb3eSmalc if (err) { 193d087bb3eSmalc dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 194d087bb3eSmalc strerror (errno)); 195d087bb3eSmalc /* We have failed to restore original signal mask, all bets are off, 196d087bb3eSmalc so exit the process */ 197d087bb3eSmalc exit (EXIT_FAILURE); 198d087bb3eSmalc } 199e784ba70Sths #endif 200ce31f099SVolker Rümelin return devid; 20185571bc7Sbellard } 20285571bc7Sbellard 203ce31f099SVolker Rümelin static void sdl_close_out(SDLVoiceOut *sdl) 20485571bc7Sbellard { 205ce31f099SVolker Rümelin if (sdl->initialized) { 206ce31f099SVolker Rümelin SDL_LockAudioDevice(sdl->devid); 207ce31f099SVolker Rümelin sdl->exit = 1; 208ce31f099SVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); 209ce31f099SVolker Rümelin SDL_PauseAudioDevice(sdl->devid, 1); 210ce31f099SVolker Rümelin sdl->initialized = 0; 211ce31f099SVolker Rümelin } 212ce31f099SVolker Rümelin if (sdl->devid) { 213ce31f099SVolker Rümelin SDL_CloseAudioDevice(sdl->devid); 214ce31f099SVolker Rümelin sdl->devid = 0; 21585571bc7Sbellard } 21685571bc7Sbellard } 21785571bc7Sbellard 218ce31f099SVolker Rümelin static void sdl_callback_out(void *opaque, Uint8 *buf, int len) 21985571bc7Sbellard { 2201d14ffa9Sbellard SDLVoiceOut *sdl = opaque; 2211d14ffa9Sbellard HWVoiceOut *hw = &sdl->hw; 22285571bc7Sbellard 223ce31f099SVolker Rümelin if (!sdl->exit) { 22485571bc7Sbellard 225ce31f099SVolker Rümelin /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */ 22685571bc7Sbellard 227ff718767SKővágó, Zoltán while (hw->pending_emul && len) { 22818404ff1SVolker Rümelin size_t write_len, start; 22918404ff1SVolker Rümelin 23018404ff1SVolker Rümelin start = audio_ring_posb(hw->pos_emul, hw->pending_emul, 23118404ff1SVolker Rümelin hw->size_emul); 23218404ff1SVolker Rümelin assert(start < hw->size_emul); 2339399ef16SThomas Huth 234ff718767SKővágó, Zoltán write_len = MIN(MIN(hw->pending_emul, len), 235ff718767SKővágó, Zoltán hw->size_emul - start); 236bcf19777SThomas Huth 237ff718767SKővágó, Zoltán memcpy(buf, hw->buf_emul + start, write_len); 238ff718767SKővágó, Zoltán hw->pending_emul -= write_len; 239ff718767SKővágó, Zoltán len -= write_len; 240ff718767SKővágó, Zoltán buf += write_len; 241ff718767SKővágó, Zoltán } 242bcce2ea5SVolker Rümelin } 243ff718767SKővágó, Zoltán 244ff718767SKővágó, Zoltán /* clear remaining buffer that we couldn't fill with data */ 245ff718767SKővágó, Zoltán if (len) { 246e02d178fSVolker Rümelin audio_pcm_info_clear_buf(&hw->info, buf, 247e02d178fSVolker Rümelin len / hw->info.bytes_per_frame); 248bcf19777SThomas Huth } 249ff541499Smalc } 25085571bc7Sbellard 251c2031deaSVolker Rümelin static void sdl_close_in(SDLVoiceIn *sdl) 252c2031deaSVolker Rümelin { 253c2031deaSVolker Rümelin if (sdl->initialized) { 254c2031deaSVolker Rümelin SDL_LockAudioDevice(sdl->devid); 255c2031deaSVolker Rümelin sdl->exit = 1; 256c2031deaSVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); 257c2031deaSVolker Rümelin SDL_PauseAudioDevice(sdl->devid, 1); 258c2031deaSVolker Rümelin sdl->initialized = 0; 259c2031deaSVolker Rümelin } 260c2031deaSVolker Rümelin if (sdl->devid) { 261c2031deaSVolker Rümelin SDL_CloseAudioDevice(sdl->devid); 262c2031deaSVolker Rümelin sdl->devid = 0; 263c2031deaSVolker Rümelin } 264c2031deaSVolker Rümelin } 265c2031deaSVolker Rümelin 266c2031deaSVolker Rümelin static void sdl_callback_in(void *opaque, Uint8 *buf, int len) 267c2031deaSVolker Rümelin { 268c2031deaSVolker Rümelin SDLVoiceIn *sdl = opaque; 269c2031deaSVolker Rümelin HWVoiceIn *hw = &sdl->hw; 270c2031deaSVolker Rümelin 271c2031deaSVolker Rümelin if (sdl->exit) { 272c2031deaSVolker Rümelin return; 273c2031deaSVolker Rümelin } 274c2031deaSVolker Rümelin 275c2031deaSVolker Rümelin /* dolog("callback_in: len=%d pending=%zu\n", len, hw->pending_emul); */ 276c2031deaSVolker Rümelin 277c2031deaSVolker Rümelin while (hw->pending_emul < hw->size_emul && len) { 278c2031deaSVolker Rümelin size_t read_len = MIN(len, MIN(hw->size_emul - hw->pos_emul, 279c2031deaSVolker Rümelin hw->size_emul - hw->pending_emul)); 280c2031deaSVolker Rümelin 281c2031deaSVolker Rümelin memcpy(hw->buf_emul + hw->pos_emul, buf, read_len); 282c2031deaSVolker Rümelin 283c2031deaSVolker Rümelin hw->pending_emul += read_len; 284c2031deaSVolker Rümelin hw->pos_emul = (hw->pos_emul + read_len) % hw->size_emul; 285c2031deaSVolker Rümelin len -= read_len; 286c2031deaSVolker Rümelin buf += read_len; 287c2031deaSVolker Rümelin } 288c2031deaSVolker Rümelin } 289c2031deaSVolker Rümelin 290ce31f099SVolker Rümelin #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \ 291ff718767SKővágó, Zoltán static ret_type glue(sdl_, name)args_decl \ 292ff718767SKővágó, Zoltán { \ 293ff718767SKővágó, Zoltán ret_type ret; \ 294ce31f099SVolker Rümelin glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ 295ff718767SKővágó, Zoltán \ 296ce31f099SVolker Rümelin SDL_LockAudioDevice(sdl->devid); \ 297ff718767SKővágó, Zoltán ret = glue(audio_generic_, name)args; \ 298ce31f099SVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); \ 299ef26632eSVolker Rümelin \ 300ff718767SKővágó, Zoltán return ret; \ 301ff541499Smalc } 302ff541499Smalc 303c2031deaSVolker Rümelin #define SDL_WRAPPER_VOID_FUNC(name, args_decl, args, dir) \ 304c2031deaSVolker Rümelin static void glue(sdl_, name)args_decl \ 305c2031deaSVolker Rümelin { \ 306c2031deaSVolker Rümelin glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ 307c2031deaSVolker Rümelin \ 308c2031deaSVolker Rümelin SDL_LockAudioDevice(sdl->devid); \ 309c2031deaSVolker Rümelin glue(audio_generic_, name)args; \ 310c2031deaSVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); \ 311c2031deaSVolker Rümelin } 312c2031deaSVolker Rümelin 3139833438eSVolker Rümelin SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out) 314ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), 315ce31f099SVolker Rümelin (hw, size), Out) 316fdc8c5f4SVolker Rümelin SDL_WRAPPER_FUNC(put_buffer_out, size_t, 317ce31f099SVolker Rümelin (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) 318ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(write, size_t, 319ce31f099SVolker Rümelin (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) 320c2031deaSVolker Rümelin SDL_WRAPPER_FUNC(read, size_t, (HWVoiceIn *hw, void *buf, size_t size), 321c2031deaSVolker Rümelin (hw, buf, size), In) 322c2031deaSVolker Rümelin SDL_WRAPPER_FUNC(get_buffer_in, void *, (HWVoiceIn *hw, size_t *size), 323c2031deaSVolker Rümelin (hw, size), In) 324c2031deaSVolker Rümelin SDL_WRAPPER_VOID_FUNC(put_buffer_in, (HWVoiceIn *hw, void *buf, size_t size), 325c2031deaSVolker Rümelin (hw, buf, size), In) 326ff718767SKővágó, Zoltán #undef SDL_WRAPPER_FUNC 327c2031deaSVolker Rümelin #undef SDL_WRAPPER_VOID_FUNC 3281d14ffa9Sbellard 3291d14ffa9Sbellard static void sdl_fini_out(HWVoiceOut *hw) 3301d14ffa9Sbellard { 331ce31f099SVolker Rümelin SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 3321d14ffa9Sbellard 333ce31f099SVolker Rümelin sdl_close_out(sdl); 33485571bc7Sbellard } 33585571bc7Sbellard 3365706db1dSKővágó, Zoltán static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 3375706db1dSKővágó, Zoltán void *drv_opaque) 33885571bc7Sbellard { 3391d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 34085571bc7Sbellard SDL_AudioSpec req, obt; 3414ff9786cSStefan Weil int endianness; 3421d14ffa9Sbellard int err; 34385bc5852SKővágó, Zoltán AudioFormat effective_fmt; 344ce31f099SVolker Rümelin Audiodev *dev = drv_opaque; 345ce31f099SVolker Rümelin AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out; 3461ea879e5Smalc struct audsettings obt_as; 34785571bc7Sbellard 348c0fe3827Sbellard req.freq = as->freq; 3496c557ab9SSerge Ziryukin req.format = aud_to_sdlfmt (as->fmt); 350c0fe3827Sbellard req.channels = as->nchannels; 3517b672528SVolker Rümelin /* SDL samples are QEMU frames */ 3527b672528SVolker Rümelin req.samples = audio_buffer_frames( 3535a0926c2SVolker Rümelin qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); 354ce31f099SVolker Rümelin req.callback = sdl_callback_out; 35585571bc7Sbellard req.userdata = sdl; 35685571bc7Sbellard 357ce31f099SVolker Rümelin sdl->dev = dev; 358ce31f099SVolker Rümelin sdl->devid = sdl_open(&req, &obt, 0); 359ce31f099SVolker Rümelin if (!sdl->devid) { 36085571bc7Sbellard return -1; 3611d14ffa9Sbellard } 36285571bc7Sbellard 3634ff9786cSStefan Weil err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 3641d14ffa9Sbellard if (err) { 365ce31f099SVolker Rümelin sdl_close_out(sdl); 3661d14ffa9Sbellard return -1; 3671d14ffa9Sbellard } 3681d14ffa9Sbellard 369c0fe3827Sbellard obt_as.freq = obt.freq; 370c0fe3827Sbellard obt_as.nchannels = obt.channels; 371c0fe3827Sbellard obt_as.fmt = effective_fmt; 3724ff9786cSStefan Weil obt_as.endianness = endianness; 373c0fe3827Sbellard 374d929eba5Sbellard audio_pcm_init_info (&hw->info, &obt_as); 3755a0926c2SVolker Rümelin hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * 3765a0926c2SVolker Rümelin obt.samples; 37785571bc7Sbellard 378ce31f099SVolker Rümelin sdl->initialized = 1; 379ce31f099SVolker Rümelin sdl->exit = 0; 38085571bc7Sbellard return 0; 38185571bc7Sbellard } 38285571bc7Sbellard 383571a8c52SKővágó, Zoltán static void sdl_enable_out(HWVoiceOut *hw, bool enable) 38485571bc7Sbellard { 385ce31f099SVolker Rümelin SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 386ce31f099SVolker Rümelin 387ce31f099SVolker Rümelin SDL_PauseAudioDevice(sdl->devid, !enable); 38885571bc7Sbellard } 38985571bc7Sbellard 390c2031deaSVolker Rümelin static void sdl_fini_in(HWVoiceIn *hw) 391c2031deaSVolker Rümelin { 392c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 393c2031deaSVolker Rümelin 394c2031deaSVolker Rümelin sdl_close_in(sdl); 395c2031deaSVolker Rümelin } 396c2031deaSVolker Rümelin 397c2031deaSVolker Rümelin static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque) 398c2031deaSVolker Rümelin { 399c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 400c2031deaSVolker Rümelin SDL_AudioSpec req, obt; 401c2031deaSVolker Rümelin int endianness; 402c2031deaSVolker Rümelin int err; 403c2031deaSVolker Rümelin AudioFormat effective_fmt; 404c2031deaSVolker Rümelin Audiodev *dev = drv_opaque; 405c2031deaSVolker Rümelin AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in; 406c2031deaSVolker Rümelin struct audsettings obt_as; 407c2031deaSVolker Rümelin 408c2031deaSVolker Rümelin req.freq = as->freq; 409c2031deaSVolker Rümelin req.format = aud_to_sdlfmt(as->fmt); 410c2031deaSVolker Rümelin req.channels = as->nchannels; 411c2031deaSVolker Rümelin /* SDL samples are QEMU frames */ 412c2031deaSVolker Rümelin req.samples = audio_buffer_frames( 413c2031deaSVolker Rümelin qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); 414c2031deaSVolker Rümelin req.callback = sdl_callback_in; 415c2031deaSVolker Rümelin req.userdata = sdl; 416c2031deaSVolker Rümelin 417c2031deaSVolker Rümelin sdl->dev = dev; 418c2031deaSVolker Rümelin sdl->devid = sdl_open(&req, &obt, 1); 419c2031deaSVolker Rümelin if (!sdl->devid) { 420c2031deaSVolker Rümelin return -1; 421c2031deaSVolker Rümelin } 422c2031deaSVolker Rümelin 423c2031deaSVolker Rümelin err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 424c2031deaSVolker Rümelin if (err) { 425c2031deaSVolker Rümelin sdl_close_in(sdl); 426c2031deaSVolker Rümelin return -1; 427c2031deaSVolker Rümelin } 428c2031deaSVolker Rümelin 429c2031deaSVolker Rümelin obt_as.freq = obt.freq; 430c2031deaSVolker Rümelin obt_as.nchannels = obt.channels; 431c2031deaSVolker Rümelin obt_as.fmt = effective_fmt; 432c2031deaSVolker Rümelin obt_as.endianness = endianness; 433c2031deaSVolker Rümelin 434c2031deaSVolker Rümelin audio_pcm_init_info(&hw->info, &obt_as); 435c2031deaSVolker Rümelin hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * 436c2031deaSVolker Rümelin obt.samples; 437c2031deaSVolker Rümelin hw->size_emul = hw->samples * hw->info.bytes_per_frame; 438c2031deaSVolker Rümelin hw->buf_emul = g_malloc(hw->size_emul); 439c2031deaSVolker Rümelin hw->pos_emul = hw->pending_emul = 0; 440c2031deaSVolker Rümelin 441c2031deaSVolker Rümelin sdl->initialized = 1; 442c2031deaSVolker Rümelin sdl->exit = 0; 443c2031deaSVolker Rümelin return 0; 444c2031deaSVolker Rümelin } 445c2031deaSVolker Rümelin 446c2031deaSVolker Rümelin static void sdl_enable_in(HWVoiceIn *hw, bool enable) 447c2031deaSVolker Rümelin { 448c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 449c2031deaSVolker Rümelin 450c2031deaSVolker Rümelin SDL_PauseAudioDevice(sdl->devid, !enable); 451c2031deaSVolker Rümelin } 452c2031deaSVolker Rümelin 453*f6061733SPaolo Bonzini static void *sdl_audio_init(Audiodev *dev, Error **errp) 45485571bc7Sbellard { 45585571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 456*f6061733SPaolo Bonzini error_setg(errp, "SDL failed to initialize audio subsystem"); 45785571bc7Sbellard return NULL; 45885571bc7Sbellard } 45985571bc7Sbellard 460ce31f099SVolker Rümelin return dev; 46185571bc7Sbellard } 46285571bc7Sbellard 46385571bc7Sbellard static void sdl_audio_fini (void *opaque) 46485571bc7Sbellard { 46585571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 46685571bc7Sbellard } 46785571bc7Sbellard 46835f4b58cSblueswir1 static struct audio_pcm_ops sdl_pcm_ops = { 4691dd3e4d1SJuan Quintela .init_out = sdl_init_out, 4701dd3e4d1SJuan Quintela .fini_out = sdl_fini_out, 471fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_write */ 472ff718767SKővágó, Zoltán .write = sdl_write, 4739833438eSVolker Rümelin /* wrapper for audio_generic_buffer_get_free */ 4749833438eSVolker Rümelin .buffer_get_free = sdl_buffer_get_free, 475fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_get_buffer_out */ 476ff718767SKővágó, Zoltán .get_buffer_out = sdl_get_buffer_out, 477fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_put_buffer_out */ 478fdc8c5f4SVolker Rümelin .put_buffer_out = sdl_put_buffer_out, 479571a8c52SKővágó, Zoltán .enable_out = sdl_enable_out, 480c2031deaSVolker Rümelin .init_in = sdl_init_in, 481c2031deaSVolker Rümelin .fini_in = sdl_fini_in, 482c2031deaSVolker Rümelin /* wrapper for audio_generic_read */ 483c2031deaSVolker Rümelin .read = sdl_read, 484c2031deaSVolker Rümelin /* wrapper for audio_generic_get_buffer_in */ 485c2031deaSVolker Rümelin .get_buffer_in = sdl_get_buffer_in, 486c2031deaSVolker Rümelin /* wrapper for audio_generic_put_buffer_in */ 487c2031deaSVolker Rümelin .put_buffer_in = sdl_put_buffer_in, 488c2031deaSVolker Rümelin .enable_in = sdl_enable_in, 4891d14ffa9Sbellard }; 4901d14ffa9Sbellard 491d3893a39SGerd Hoffmann static struct audio_driver sdl_audio_driver = { 492bee37f32SJuan Quintela .name = "sdl", 493bee37f32SJuan Quintela .descr = "SDL http://www.libsdl.org", 494bee37f32SJuan Quintela .init = sdl_audio_init, 495bee37f32SJuan Quintela .fini = sdl_audio_fini, 496bee37f32SJuan Quintela .pcm_ops = &sdl_pcm_ops, 497bee37f32SJuan Quintela .can_be_default = 1, 498bd37ede4SVolker Rümelin .max_voices_out = INT_MAX, 499bd37ede4SVolker Rümelin .max_voices_in = INT_MAX, 500bee37f32SJuan Quintela .voice_size_out = sizeof(SDLVoiceOut), 501c2031deaSVolker Rümelin .voice_size_in = sizeof(SDLVoiceIn), 50285571bc7Sbellard }; 503d3893a39SGerd Hoffmann 504d3893a39SGerd Hoffmann static void register_audio_sdl(void) 505d3893a39SGerd Hoffmann { 506d3893a39SGerd Hoffmann audio_driver_register(&sdl_audio_driver); 507d3893a39SGerd Hoffmann } 508d3893a39SGerd Hoffmann type_init(register_audio_sdl); 509