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; 4485571bc7Sbellard int exit; 4585571bc7Sbellard int initialized; 4657dea553SKővágó, Zoltán Audiodev *dev; 47ce31f099SVolker Rümelin SDL_AudioDeviceID devid; 48ce31f099SVolker Rümelin } SDLVoiceOut; 4985571bc7Sbellard 50c2031deaSVolker Rümelin typedef struct SDLVoiceIn { 51c2031deaSVolker Rümelin HWVoiceIn hw; 52c2031deaSVolker Rümelin int exit; 53c2031deaSVolker Rümelin int initialized; 54c2031deaSVolker Rümelin Audiodev *dev; 55c2031deaSVolker Rümelin SDL_AudioDeviceID devid; 56c2031deaSVolker Rümelin } SDLVoiceIn; 57c2031deaSVolker Rümelin 581d14ffa9Sbellard static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 5985571bc7Sbellard { 601d14ffa9Sbellard va_list ap; 611d14ffa9Sbellard 621d14ffa9Sbellard va_start (ap, fmt); 631d14ffa9Sbellard AUD_vlog (AUDIO_CAP, fmt, ap); 641d14ffa9Sbellard va_end (ap); 651d14ffa9Sbellard 661d14ffa9Sbellard AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 6785571bc7Sbellard } 6885571bc7Sbellard 6985bc5852SKővágó, Zoltán static int aud_to_sdlfmt (AudioFormat fmt) 7085571bc7Sbellard { 711d14ffa9Sbellard switch (fmt) { 7285bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 731d14ffa9Sbellard return AUDIO_S8; 741d14ffa9Sbellard 7585bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 761d14ffa9Sbellard return AUDIO_U8; 771d14ffa9Sbellard 7885bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 791d14ffa9Sbellard return AUDIO_S16LSB; 801d14ffa9Sbellard 8185bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 821d14ffa9Sbellard return AUDIO_U16LSB; 831d14ffa9Sbellard 84ed2a4a79SKővágó, Zoltán case AUDIO_FORMAT_S32: 85ed2a4a79SKővágó, Zoltán return AUDIO_S32LSB; 86ed2a4a79SKővágó, Zoltán 87ed2a4a79SKővágó, Zoltán /* no unsigned 32-bit support in SDL */ 88ed2a4a79SKővágó, Zoltán 89ed2a4a79SKővágó, Zoltán case AUDIO_FORMAT_F32: 90ed2a4a79SKővágó, Zoltán return AUDIO_F32LSB; 91ed2a4a79SKővágó, Zoltán 9285571bc7Sbellard default: 931d14ffa9Sbellard dolog ("Internal logic error: Bad audio format %d\n", fmt); 941d14ffa9Sbellard #ifdef DEBUG_AUDIO 951d14ffa9Sbellard abort (); 961d14ffa9Sbellard #endif 971d14ffa9Sbellard return AUDIO_U8; 9885571bc7Sbellard } 9985571bc7Sbellard } 10085571bc7Sbellard 10185bc5852SKővágó, Zoltán static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) 10285571bc7Sbellard { 1031d14ffa9Sbellard switch (sdlfmt) { 1041d14ffa9Sbellard case AUDIO_S8: 1054ff9786cSStefan Weil *endianness = 0; 10685bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S8; 1071d14ffa9Sbellard break; 1081d14ffa9Sbellard 1091d14ffa9Sbellard case AUDIO_U8: 1104ff9786cSStefan Weil *endianness = 0; 11185bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U8; 1121d14ffa9Sbellard break; 1131d14ffa9Sbellard 1141d14ffa9Sbellard case AUDIO_S16LSB: 1154ff9786cSStefan Weil *endianness = 0; 11685bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1171d14ffa9Sbellard break; 1181d14ffa9Sbellard 1191d14ffa9Sbellard case AUDIO_U16LSB: 1204ff9786cSStefan Weil *endianness = 0; 12185bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1221d14ffa9Sbellard break; 1231d14ffa9Sbellard 1241d14ffa9Sbellard case AUDIO_S16MSB: 1254ff9786cSStefan Weil *endianness = 1; 12685bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_S16; 1271d14ffa9Sbellard break; 1281d14ffa9Sbellard 1291d14ffa9Sbellard case AUDIO_U16MSB: 1304ff9786cSStefan Weil *endianness = 1; 13185bc5852SKővágó, Zoltán *fmt = AUDIO_FORMAT_U16; 1321d14ffa9Sbellard break; 1331d14ffa9Sbellard 134ed2a4a79SKővágó, Zoltán case AUDIO_S32LSB: 135ed2a4a79SKővágó, Zoltán *endianness = 0; 136ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_S32; 137ed2a4a79SKővágó, Zoltán break; 138ed2a4a79SKővágó, Zoltán 139ed2a4a79SKővágó, Zoltán case AUDIO_S32MSB: 140ed2a4a79SKővágó, Zoltán *endianness = 1; 141ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_S32; 142ed2a4a79SKővágó, Zoltán break; 143ed2a4a79SKővágó, Zoltán 144ed2a4a79SKővágó, Zoltán case AUDIO_F32LSB: 145ed2a4a79SKővágó, Zoltán *endianness = 0; 146ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_F32; 147ed2a4a79SKővágó, Zoltán break; 148ed2a4a79SKővágó, Zoltán 149ed2a4a79SKővágó, Zoltán case AUDIO_F32MSB: 150ed2a4a79SKővágó, Zoltán *endianness = 1; 151ed2a4a79SKővágó, Zoltán *fmt = AUDIO_FORMAT_F32; 152ed2a4a79SKővágó, Zoltán break; 153ed2a4a79SKővágó, Zoltán 15485571bc7Sbellard default: 1551d14ffa9Sbellard dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 1561d14ffa9Sbellard return -1; 15785571bc7Sbellard } 1581d14ffa9Sbellard 1591d14ffa9Sbellard return 0; 16085571bc7Sbellard } 16185571bc7Sbellard 162ce31f099SVolker Rümelin static SDL_AudioDeviceID sdl_open(SDL_AudioSpec *req, SDL_AudioSpec *obt, 163ce31f099SVolker Rümelin int rec) 16485571bc7Sbellard { 165ce31f099SVolker Rümelin SDL_AudioDeviceID devid; 166e784ba70Sths #ifndef _WIN32 167d087bb3eSmalc int err; 168e784ba70Sths sigset_t new, old; 169e784ba70Sths 170e784ba70Sths /* Make sure potential threads created by SDL don't hog signals. */ 171d087bb3eSmalc err = sigfillset (&new); 172d087bb3eSmalc if (err) { 173d087bb3eSmalc dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 174ce31f099SVolker Rümelin return 0; 175d087bb3eSmalc } 176d087bb3eSmalc err = pthread_sigmask (SIG_BLOCK, &new, &old); 177d087bb3eSmalc if (err) { 178d087bb3eSmalc dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 179ce31f099SVolker Rümelin return 0; 180d087bb3eSmalc } 181e784ba70Sths #endif 18285571bc7Sbellard 183ce31f099SVolker Rümelin devid = SDL_OpenAudioDevice(NULL, rec, req, obt, 0); 184ce31f099SVolker Rümelin if (!devid) { 185ce31f099SVolker Rümelin sdl_logerr("SDL_OpenAudioDevice for %s failed\n", 186ce31f099SVolker Rümelin rec ? "recording" : "playback"); 18785571bc7Sbellard } 188e784ba70Sths 189e784ba70Sths #ifndef _WIN32 190d087bb3eSmalc err = pthread_sigmask (SIG_SETMASK, &old, NULL); 191d087bb3eSmalc if (err) { 192d087bb3eSmalc dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 193d087bb3eSmalc strerror (errno)); 194d087bb3eSmalc /* We have failed to restore original signal mask, all bets are off, 195d087bb3eSmalc so exit the process */ 196d087bb3eSmalc exit (EXIT_FAILURE); 197d087bb3eSmalc } 198e784ba70Sths #endif 199ce31f099SVolker Rümelin return devid; 20085571bc7Sbellard } 20185571bc7Sbellard 202ce31f099SVolker Rümelin static void sdl_close_out(SDLVoiceOut *sdl) 20385571bc7Sbellard { 204ce31f099SVolker Rümelin if (sdl->initialized) { 205ce31f099SVolker Rümelin SDL_LockAudioDevice(sdl->devid); 206ce31f099SVolker Rümelin sdl->exit = 1; 207ce31f099SVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); 208ce31f099SVolker Rümelin SDL_PauseAudioDevice(sdl->devid, 1); 209ce31f099SVolker Rümelin sdl->initialized = 0; 210ce31f099SVolker Rümelin } 211ce31f099SVolker Rümelin if (sdl->devid) { 212ce31f099SVolker Rümelin SDL_CloseAudioDevice(sdl->devid); 213ce31f099SVolker Rümelin sdl->devid = 0; 21485571bc7Sbellard } 21585571bc7Sbellard } 21685571bc7Sbellard 217ce31f099SVolker Rümelin static void sdl_callback_out(void *opaque, Uint8 *buf, int len) 21885571bc7Sbellard { 2191d14ffa9Sbellard SDLVoiceOut *sdl = opaque; 2201d14ffa9Sbellard HWVoiceOut *hw = &sdl->hw; 22185571bc7Sbellard 222ce31f099SVolker Rümelin if (!sdl->exit) { 22385571bc7Sbellard 224ce31f099SVolker Rümelin /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */ 22585571bc7Sbellard 226ff718767SKővágó, Zoltán while (hw->pending_emul && len) { 22718404ff1SVolker Rümelin size_t write_len, start; 22818404ff1SVolker Rümelin 22918404ff1SVolker Rümelin start = audio_ring_posb(hw->pos_emul, hw->pending_emul, 23018404ff1SVolker Rümelin hw->size_emul); 23118404ff1SVolker Rümelin assert(start < hw->size_emul); 2329399ef16SThomas Huth 233ff718767SKővágó, Zoltán write_len = MIN(MIN(hw->pending_emul, len), 234ff718767SKővágó, Zoltán hw->size_emul - start); 235bcf19777SThomas Huth 236ff718767SKővágó, Zoltán memcpy(buf, hw->buf_emul + start, write_len); 237ff718767SKővágó, Zoltán hw->pending_emul -= write_len; 238ff718767SKővágó, Zoltán len -= write_len; 239ff718767SKővágó, Zoltán buf += write_len; 240ff718767SKővágó, Zoltán } 241bcce2ea5SVolker Rümelin } 242ff718767SKővágó, Zoltán 243ff718767SKővágó, Zoltán /* clear remaining buffer that we couldn't fill with data */ 244ff718767SKővágó, Zoltán if (len) { 245e02d178fSVolker Rümelin audio_pcm_info_clear_buf(&hw->info, buf, 246e02d178fSVolker Rümelin len / hw->info.bytes_per_frame); 247bcf19777SThomas Huth } 248ff541499Smalc } 24985571bc7Sbellard 250c2031deaSVolker Rümelin static void sdl_close_in(SDLVoiceIn *sdl) 251c2031deaSVolker Rümelin { 252c2031deaSVolker Rümelin if (sdl->initialized) { 253c2031deaSVolker Rümelin SDL_LockAudioDevice(sdl->devid); 254c2031deaSVolker Rümelin sdl->exit = 1; 255c2031deaSVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); 256c2031deaSVolker Rümelin SDL_PauseAudioDevice(sdl->devid, 1); 257c2031deaSVolker Rümelin sdl->initialized = 0; 258c2031deaSVolker Rümelin } 259c2031deaSVolker Rümelin if (sdl->devid) { 260c2031deaSVolker Rümelin SDL_CloseAudioDevice(sdl->devid); 261c2031deaSVolker Rümelin sdl->devid = 0; 262c2031deaSVolker Rümelin } 263c2031deaSVolker Rümelin } 264c2031deaSVolker Rümelin 265c2031deaSVolker Rümelin static void sdl_callback_in(void *opaque, Uint8 *buf, int len) 266c2031deaSVolker Rümelin { 267c2031deaSVolker Rümelin SDLVoiceIn *sdl = opaque; 268c2031deaSVolker Rümelin HWVoiceIn *hw = &sdl->hw; 269c2031deaSVolker Rümelin 270c2031deaSVolker Rümelin if (sdl->exit) { 271c2031deaSVolker Rümelin return; 272c2031deaSVolker Rümelin } 273c2031deaSVolker Rümelin 274c2031deaSVolker Rümelin /* dolog("callback_in: len=%d pending=%zu\n", len, hw->pending_emul); */ 275c2031deaSVolker Rümelin 276c2031deaSVolker Rümelin while (hw->pending_emul < hw->size_emul && len) { 277c2031deaSVolker Rümelin size_t read_len = MIN(len, MIN(hw->size_emul - hw->pos_emul, 278c2031deaSVolker Rümelin hw->size_emul - hw->pending_emul)); 279c2031deaSVolker Rümelin 280c2031deaSVolker Rümelin memcpy(hw->buf_emul + hw->pos_emul, buf, read_len); 281c2031deaSVolker Rümelin 282c2031deaSVolker Rümelin hw->pending_emul += read_len; 283c2031deaSVolker Rümelin hw->pos_emul = (hw->pos_emul + read_len) % hw->size_emul; 284c2031deaSVolker Rümelin len -= read_len; 285c2031deaSVolker Rümelin buf += read_len; 286c2031deaSVolker Rümelin } 287c2031deaSVolker Rümelin } 288c2031deaSVolker Rümelin 289ce31f099SVolker Rümelin #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \ 290ff718767SKővágó, Zoltán static ret_type glue(sdl_, name)args_decl \ 291ff718767SKővágó, Zoltán { \ 292ff718767SKővágó, Zoltán ret_type ret; \ 293ce31f099SVolker Rümelin glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ 294ff718767SKővágó, Zoltán \ 295ce31f099SVolker Rümelin SDL_LockAudioDevice(sdl->devid); \ 296ff718767SKővágó, Zoltán ret = glue(audio_generic_, name)args; \ 297ce31f099SVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); \ 298ef26632eSVolker Rümelin \ 299ff718767SKővágó, Zoltán return ret; \ 300ff541499Smalc } 301ff541499Smalc 302c2031deaSVolker Rümelin #define SDL_WRAPPER_VOID_FUNC(name, args_decl, args, dir) \ 303c2031deaSVolker Rümelin static void glue(sdl_, name)args_decl \ 304c2031deaSVolker Rümelin { \ 305c2031deaSVolker Rümelin glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ 306c2031deaSVolker Rümelin \ 307c2031deaSVolker Rümelin SDL_LockAudioDevice(sdl->devid); \ 308c2031deaSVolker Rümelin glue(audio_generic_, name)args; \ 309c2031deaSVolker Rümelin SDL_UnlockAudioDevice(sdl->devid); \ 310c2031deaSVolker Rümelin } 311c2031deaSVolker Rümelin 3129833438eSVolker Rümelin SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out) 313ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), 314ce31f099SVolker Rümelin (hw, size), Out) 315fdc8c5f4SVolker Rümelin SDL_WRAPPER_FUNC(put_buffer_out, size_t, 316ce31f099SVolker Rümelin (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) 317ff718767SKővágó, Zoltán SDL_WRAPPER_FUNC(write, size_t, 318ce31f099SVolker Rümelin (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) 319c2031deaSVolker Rümelin SDL_WRAPPER_FUNC(read, size_t, (HWVoiceIn *hw, void *buf, size_t size), 320c2031deaSVolker Rümelin (hw, buf, size), In) 321c2031deaSVolker Rümelin SDL_WRAPPER_FUNC(get_buffer_in, void *, (HWVoiceIn *hw, size_t *size), 322c2031deaSVolker Rümelin (hw, size), In) 323c2031deaSVolker Rümelin SDL_WRAPPER_VOID_FUNC(put_buffer_in, (HWVoiceIn *hw, void *buf, size_t size), 324c2031deaSVolker Rümelin (hw, buf, size), In) 325ff718767SKővágó, Zoltán #undef SDL_WRAPPER_FUNC 326c2031deaSVolker Rümelin #undef SDL_WRAPPER_VOID_FUNC 3271d14ffa9Sbellard 3281d14ffa9Sbellard static void sdl_fini_out(HWVoiceOut *hw) 3291d14ffa9Sbellard { 330ce31f099SVolker Rümelin SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 3311d14ffa9Sbellard 332ce31f099SVolker Rümelin sdl_close_out(sdl); 33385571bc7Sbellard } 33485571bc7Sbellard 3355706db1dSKővágó, Zoltán static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 3365706db1dSKővágó, Zoltán void *drv_opaque) 33785571bc7Sbellard { 3381d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 33985571bc7Sbellard SDL_AudioSpec req, obt; 3404ff9786cSStefan Weil int endianness; 3411d14ffa9Sbellard int err; 34285bc5852SKővágó, Zoltán AudioFormat effective_fmt; 343ce31f099SVolker Rümelin Audiodev *dev = drv_opaque; 344ce31f099SVolker Rümelin AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out; 3451ea879e5Smalc struct audsettings obt_as; 34685571bc7Sbellard 347c0fe3827Sbellard req.freq = as->freq; 3486c557ab9SSerge Ziryukin req.format = aud_to_sdlfmt (as->fmt); 349c0fe3827Sbellard req.channels = as->nchannels; 350*7b672528SVolker Rümelin /* SDL samples are QEMU frames */ 351*7b672528SVolker Rümelin req.samples = audio_buffer_frames( 3525a0926c2SVolker Rümelin qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); 353ce31f099SVolker Rümelin req.callback = sdl_callback_out; 35485571bc7Sbellard req.userdata = sdl; 35585571bc7Sbellard 356ce31f099SVolker Rümelin sdl->dev = dev; 357ce31f099SVolker Rümelin sdl->devid = sdl_open(&req, &obt, 0); 358ce31f099SVolker Rümelin if (!sdl->devid) { 35985571bc7Sbellard return -1; 3601d14ffa9Sbellard } 36185571bc7Sbellard 3624ff9786cSStefan Weil err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 3631d14ffa9Sbellard if (err) { 364ce31f099SVolker Rümelin sdl_close_out(sdl); 3651d14ffa9Sbellard return -1; 3661d14ffa9Sbellard } 3671d14ffa9Sbellard 368c0fe3827Sbellard obt_as.freq = obt.freq; 369c0fe3827Sbellard obt_as.nchannels = obt.channels; 370c0fe3827Sbellard obt_as.fmt = effective_fmt; 3714ff9786cSStefan Weil obt_as.endianness = endianness; 372c0fe3827Sbellard 373d929eba5Sbellard audio_pcm_init_info (&hw->info, &obt_as); 3745a0926c2SVolker Rümelin hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * 3755a0926c2SVolker Rümelin obt.samples; 37685571bc7Sbellard 377ce31f099SVolker Rümelin sdl->initialized = 1; 378ce31f099SVolker Rümelin sdl->exit = 0; 37985571bc7Sbellard return 0; 38085571bc7Sbellard } 38185571bc7Sbellard 382571a8c52SKővágó, Zoltán static void sdl_enable_out(HWVoiceOut *hw, bool enable) 38385571bc7Sbellard { 384ce31f099SVolker Rümelin SDLVoiceOut *sdl = (SDLVoiceOut *)hw; 385ce31f099SVolker Rümelin 386ce31f099SVolker Rümelin SDL_PauseAudioDevice(sdl->devid, !enable); 38785571bc7Sbellard } 38885571bc7Sbellard 389c2031deaSVolker Rümelin static void sdl_fini_in(HWVoiceIn *hw) 390c2031deaSVolker Rümelin { 391c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 392c2031deaSVolker Rümelin 393c2031deaSVolker Rümelin sdl_close_in(sdl); 394c2031deaSVolker Rümelin } 395c2031deaSVolker Rümelin 396c2031deaSVolker Rümelin static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque) 397c2031deaSVolker Rümelin { 398c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 399c2031deaSVolker Rümelin SDL_AudioSpec req, obt; 400c2031deaSVolker Rümelin int endianness; 401c2031deaSVolker Rümelin int err; 402c2031deaSVolker Rümelin AudioFormat effective_fmt; 403c2031deaSVolker Rümelin Audiodev *dev = drv_opaque; 404c2031deaSVolker Rümelin AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in; 405c2031deaSVolker Rümelin struct audsettings obt_as; 406c2031deaSVolker Rümelin 407c2031deaSVolker Rümelin req.freq = as->freq; 408c2031deaSVolker Rümelin req.format = aud_to_sdlfmt(as->fmt); 409c2031deaSVolker Rümelin req.channels = as->nchannels; 410c2031deaSVolker Rümelin /* SDL samples are QEMU frames */ 411c2031deaSVolker Rümelin req.samples = audio_buffer_frames( 412c2031deaSVolker Rümelin qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); 413c2031deaSVolker Rümelin req.callback = sdl_callback_in; 414c2031deaSVolker Rümelin req.userdata = sdl; 415c2031deaSVolker Rümelin 416c2031deaSVolker Rümelin sdl->dev = dev; 417c2031deaSVolker Rümelin sdl->devid = sdl_open(&req, &obt, 1); 418c2031deaSVolker Rümelin if (!sdl->devid) { 419c2031deaSVolker Rümelin return -1; 420c2031deaSVolker Rümelin } 421c2031deaSVolker Rümelin 422c2031deaSVolker Rümelin err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 423c2031deaSVolker Rümelin if (err) { 424c2031deaSVolker Rümelin sdl_close_in(sdl); 425c2031deaSVolker Rümelin return -1; 426c2031deaSVolker Rümelin } 427c2031deaSVolker Rümelin 428c2031deaSVolker Rümelin obt_as.freq = obt.freq; 429c2031deaSVolker Rümelin obt_as.nchannels = obt.channels; 430c2031deaSVolker Rümelin obt_as.fmt = effective_fmt; 431c2031deaSVolker Rümelin obt_as.endianness = endianness; 432c2031deaSVolker Rümelin 433c2031deaSVolker Rümelin audio_pcm_init_info(&hw->info, &obt_as); 434c2031deaSVolker Rümelin hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * 435c2031deaSVolker Rümelin obt.samples; 436c2031deaSVolker Rümelin hw->size_emul = hw->samples * hw->info.bytes_per_frame; 437c2031deaSVolker Rümelin hw->buf_emul = g_malloc(hw->size_emul); 438c2031deaSVolker Rümelin hw->pos_emul = hw->pending_emul = 0; 439c2031deaSVolker Rümelin 440c2031deaSVolker Rümelin sdl->initialized = 1; 441c2031deaSVolker Rümelin sdl->exit = 0; 442c2031deaSVolker Rümelin return 0; 443c2031deaSVolker Rümelin } 444c2031deaSVolker Rümelin 445c2031deaSVolker Rümelin static void sdl_enable_in(HWVoiceIn *hw, bool enable) 446c2031deaSVolker Rümelin { 447c2031deaSVolker Rümelin SDLVoiceIn *sdl = (SDLVoiceIn *)hw; 448c2031deaSVolker Rümelin 449c2031deaSVolker Rümelin SDL_PauseAudioDevice(sdl->devid, !enable); 450c2031deaSVolker Rümelin } 451c2031deaSVolker Rümelin 45271830221SKővágó, Zoltán static void *sdl_audio_init(Audiodev *dev) 45385571bc7Sbellard { 45485571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 4551d14ffa9Sbellard sdl_logerr ("SDL failed to initialize audio subsystem\n"); 45685571bc7Sbellard return NULL; 45785571bc7Sbellard } 45885571bc7Sbellard 459ce31f099SVolker Rümelin return dev; 46085571bc7Sbellard } 46185571bc7Sbellard 46285571bc7Sbellard static void sdl_audio_fini (void *opaque) 46385571bc7Sbellard { 46485571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 46585571bc7Sbellard } 46685571bc7Sbellard 46735f4b58cSblueswir1 static struct audio_pcm_ops sdl_pcm_ops = { 4681dd3e4d1SJuan Quintela .init_out = sdl_init_out, 4691dd3e4d1SJuan Quintela .fini_out = sdl_fini_out, 470fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_write */ 471ff718767SKővágó, Zoltán .write = sdl_write, 4729833438eSVolker Rümelin /* wrapper for audio_generic_buffer_get_free */ 4739833438eSVolker Rümelin .buffer_get_free = sdl_buffer_get_free, 474fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_get_buffer_out */ 475ff718767SKővágó, Zoltán .get_buffer_out = sdl_get_buffer_out, 476fdc8c5f4SVolker Rümelin /* wrapper for audio_generic_put_buffer_out */ 477fdc8c5f4SVolker Rümelin .put_buffer_out = sdl_put_buffer_out, 478571a8c52SKővágó, Zoltán .enable_out = sdl_enable_out, 479c2031deaSVolker Rümelin .init_in = sdl_init_in, 480c2031deaSVolker Rümelin .fini_in = sdl_fini_in, 481c2031deaSVolker Rümelin /* wrapper for audio_generic_read */ 482c2031deaSVolker Rümelin .read = sdl_read, 483c2031deaSVolker Rümelin /* wrapper for audio_generic_get_buffer_in */ 484c2031deaSVolker Rümelin .get_buffer_in = sdl_get_buffer_in, 485c2031deaSVolker Rümelin /* wrapper for audio_generic_put_buffer_in */ 486c2031deaSVolker Rümelin .put_buffer_in = sdl_put_buffer_in, 487c2031deaSVolker Rümelin .enable_in = sdl_enable_in, 4881d14ffa9Sbellard }; 4891d14ffa9Sbellard 490d3893a39SGerd Hoffmann static struct audio_driver sdl_audio_driver = { 491bee37f32SJuan Quintela .name = "sdl", 492bee37f32SJuan Quintela .descr = "SDL http://www.libsdl.org", 493bee37f32SJuan Quintela .init = sdl_audio_init, 494bee37f32SJuan Quintela .fini = sdl_audio_fini, 495bee37f32SJuan Quintela .pcm_ops = &sdl_pcm_ops, 496bee37f32SJuan Quintela .can_be_default = 1, 497bd37ede4SVolker Rümelin .max_voices_out = INT_MAX, 498bd37ede4SVolker Rümelin .max_voices_in = INT_MAX, 499bee37f32SJuan Quintela .voice_size_out = sizeof(SDLVoiceOut), 500c2031deaSVolker Rümelin .voice_size_in = sizeof(SDLVoiceIn), 50185571bc7Sbellard }; 502d3893a39SGerd Hoffmann 503d3893a39SGerd Hoffmann static void register_audio_sdl(void) 504d3893a39SGerd Hoffmann { 505d3893a39SGerd Hoffmann audio_driver_register(&sdl_audio_driver); 506d3893a39SGerd Hoffmann } 507d3893a39SGerd Hoffmann type_init(register_audio_sdl); 508