1 /* 2 * QEMU SDL audio driver 3 * 4 * Copyright (c) 2004-2005 Vassili Karpov (malc) 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include <SDL.h> 27 #include <SDL_thread.h> 28 #include "qemu/module.h" 29 #include "audio.h" 30 31 #ifndef _WIN32 32 #ifdef __sun__ 33 #define _POSIX_PTHREAD_SEMANTICS 1 34 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 35 #include <pthread.h> 36 #endif 37 #endif 38 39 #define AUDIO_CAP "sdl" 40 #include "audio_int.h" 41 42 typedef struct SDLVoiceOut { 43 HWVoiceOut hw; 44 } SDLVoiceOut; 45 46 static struct SDLAudioState { 47 int exit; 48 int initialized; 49 bool driver_created; 50 Audiodev *dev; 51 } glob_sdl; 52 typedef struct SDLAudioState SDLAudioState; 53 54 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 55 { 56 va_list ap; 57 58 va_start (ap, fmt); 59 AUD_vlog (AUDIO_CAP, fmt, ap); 60 va_end (ap); 61 62 AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 63 } 64 65 static int aud_to_sdlfmt (AudioFormat fmt) 66 { 67 switch (fmt) { 68 case AUDIO_FORMAT_S8: 69 return AUDIO_S8; 70 71 case AUDIO_FORMAT_U8: 72 return AUDIO_U8; 73 74 case AUDIO_FORMAT_S16: 75 return AUDIO_S16LSB; 76 77 case AUDIO_FORMAT_U16: 78 return AUDIO_U16LSB; 79 80 case AUDIO_FORMAT_S32: 81 return AUDIO_S32LSB; 82 83 /* no unsigned 32-bit support in SDL */ 84 85 case AUDIO_FORMAT_F32: 86 return AUDIO_F32LSB; 87 88 default: 89 dolog ("Internal logic error: Bad audio format %d\n", fmt); 90 #ifdef DEBUG_AUDIO 91 abort (); 92 #endif 93 return AUDIO_U8; 94 } 95 } 96 97 static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) 98 { 99 switch (sdlfmt) { 100 case AUDIO_S8: 101 *endianness = 0; 102 *fmt = AUDIO_FORMAT_S8; 103 break; 104 105 case AUDIO_U8: 106 *endianness = 0; 107 *fmt = AUDIO_FORMAT_U8; 108 break; 109 110 case AUDIO_S16LSB: 111 *endianness = 0; 112 *fmt = AUDIO_FORMAT_S16; 113 break; 114 115 case AUDIO_U16LSB: 116 *endianness = 0; 117 *fmt = AUDIO_FORMAT_U16; 118 break; 119 120 case AUDIO_S16MSB: 121 *endianness = 1; 122 *fmt = AUDIO_FORMAT_S16; 123 break; 124 125 case AUDIO_U16MSB: 126 *endianness = 1; 127 *fmt = AUDIO_FORMAT_U16; 128 break; 129 130 case AUDIO_S32LSB: 131 *endianness = 0; 132 *fmt = AUDIO_FORMAT_S32; 133 break; 134 135 case AUDIO_S32MSB: 136 *endianness = 1; 137 *fmt = AUDIO_FORMAT_S32; 138 break; 139 140 case AUDIO_F32LSB: 141 *endianness = 0; 142 *fmt = AUDIO_FORMAT_F32; 143 break; 144 145 case AUDIO_F32MSB: 146 *endianness = 1; 147 *fmt = AUDIO_FORMAT_F32; 148 break; 149 150 default: 151 dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 152 return -1; 153 } 154 155 return 0; 156 } 157 158 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 159 { 160 int status; 161 #ifndef _WIN32 162 int err; 163 sigset_t new, old; 164 165 /* Make sure potential threads created by SDL don't hog signals. */ 166 err = sigfillset (&new); 167 if (err) { 168 dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 169 return -1; 170 } 171 err = pthread_sigmask (SIG_BLOCK, &new, &old); 172 if (err) { 173 dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 174 return -1; 175 } 176 #endif 177 178 status = SDL_OpenAudio (req, obt); 179 if (status) { 180 sdl_logerr ("SDL_OpenAudio failed\n"); 181 } 182 183 #ifndef _WIN32 184 err = pthread_sigmask (SIG_SETMASK, &old, NULL); 185 if (err) { 186 dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 187 strerror (errno)); 188 /* We have failed to restore original signal mask, all bets are off, 189 so exit the process */ 190 exit (EXIT_FAILURE); 191 } 192 #endif 193 return status; 194 } 195 196 static void sdl_close (SDLAudioState *s) 197 { 198 if (s->initialized) { 199 SDL_LockAudio(); 200 s->exit = 1; 201 SDL_UnlockAudio(); 202 SDL_PauseAudio (1); 203 SDL_CloseAudio (); 204 s->initialized = 0; 205 } 206 } 207 208 static void sdl_callback (void *opaque, Uint8 *buf, int len) 209 { 210 SDLVoiceOut *sdl = opaque; 211 SDLAudioState *s = &glob_sdl; 212 HWVoiceOut *hw = &sdl->hw; 213 214 if (s->exit) { 215 return; 216 } 217 218 /* dolog("callback: len=%d avail=%zu\n", len, hw->pending_emul); */ 219 220 while (hw->pending_emul && len) { 221 size_t write_len; 222 ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; 223 if (start < 0) { 224 start += hw->size_emul; 225 } 226 assert(start >= 0 && start < hw->size_emul); 227 228 write_len = MIN(MIN(hw->pending_emul, len), 229 hw->size_emul - start); 230 231 memcpy(buf, hw->buf_emul + start, write_len); 232 hw->pending_emul -= write_len; 233 len -= write_len; 234 buf += write_len; 235 } 236 237 /* clear remaining buffer that we couldn't fill with data */ 238 if (len) { 239 memset(buf, 0, len); 240 } 241 } 242 243 #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args) \ 244 static ret_type glue(sdl_, name)args_decl \ 245 { \ 246 ret_type ret; \ 247 \ 248 SDL_LockAudio(); \ 249 ret = glue(audio_generic_, name)args; \ 250 SDL_UnlockAudio(); \ 251 \ 252 return ret; \ 253 } 254 255 SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), 256 (hw, size)) 257 SDL_WRAPPER_FUNC(put_buffer_out, size_t, 258 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size)) 259 SDL_WRAPPER_FUNC(write, size_t, 260 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size)) 261 #undef SDL_WRAPPER_FUNC 262 263 static void sdl_fini_out (HWVoiceOut *hw) 264 { 265 (void) hw; 266 267 sdl_close (&glob_sdl); 268 } 269 270 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 271 void *drv_opaque) 272 { 273 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 274 SDLAudioState *s = &glob_sdl; 275 SDL_AudioSpec req, obt; 276 int endianness; 277 int err; 278 AudioFormat effective_fmt; 279 AudiodevSdlPerDirectionOptions *spdo = s->dev->u.sdl.out; 280 struct audsettings obt_as; 281 282 req.freq = as->freq; 283 req.format = aud_to_sdlfmt (as->fmt); 284 req.channels = as->nchannels; 285 /* 286 * This is wrong. SDL samples are QEMU frames. The buffer size will be 287 * the requested buffer size multiplied by the number of channels. 288 */ 289 req.samples = audio_buffer_samples( 290 qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); 291 req.callback = sdl_callback; 292 req.userdata = sdl; 293 294 if (sdl_open (&req, &obt)) { 295 return -1; 296 } 297 298 err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 299 if (err) { 300 sdl_close (s); 301 return -1; 302 } 303 304 obt_as.freq = obt.freq; 305 obt_as.nchannels = obt.channels; 306 obt_as.fmt = effective_fmt; 307 obt_as.endianness = endianness; 308 309 audio_pcm_init_info (&hw->info, &obt_as); 310 hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * 311 obt.samples; 312 313 s->initialized = 1; 314 s->exit = 0; 315 SDL_PauseAudio (0); 316 return 0; 317 } 318 319 static void sdl_enable_out(HWVoiceOut *hw, bool enable) 320 { 321 SDL_PauseAudio(!enable); 322 } 323 324 static void *sdl_audio_init(Audiodev *dev) 325 { 326 SDLAudioState *s = &glob_sdl; 327 if (s->driver_created) { 328 sdl_logerr("Can't create multiple sdl backends\n"); 329 return NULL; 330 } 331 332 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 333 sdl_logerr ("SDL failed to initialize audio subsystem\n"); 334 return NULL; 335 } 336 337 s->driver_created = true; 338 s->dev = dev; 339 return s; 340 } 341 342 static void sdl_audio_fini (void *opaque) 343 { 344 SDLAudioState *s = opaque; 345 sdl_close (s); 346 SDL_QuitSubSystem (SDL_INIT_AUDIO); 347 s->driver_created = false; 348 s->dev = NULL; 349 } 350 351 static struct audio_pcm_ops sdl_pcm_ops = { 352 .init_out = sdl_init_out, 353 .fini_out = sdl_fini_out, 354 /* wrapper for audio_generic_write */ 355 .write = sdl_write, 356 /* wrapper for audio_generic_get_buffer_out */ 357 .get_buffer_out = sdl_get_buffer_out, 358 /* wrapper for audio_generic_put_buffer_out */ 359 .put_buffer_out = sdl_put_buffer_out, 360 .enable_out = sdl_enable_out, 361 }; 362 363 static struct audio_driver sdl_audio_driver = { 364 .name = "sdl", 365 .descr = "SDL http://www.libsdl.org", 366 .init = sdl_audio_init, 367 .fini = sdl_audio_fini, 368 .pcm_ops = &sdl_pcm_ops, 369 .can_be_default = 1, 370 .max_voices_out = 1, 371 .max_voices_in = 0, 372 .voice_size_out = sizeof (SDLVoiceOut), 373 .voice_size_in = 0 374 }; 375 376 static void register_audio_sdl(void) 377 { 378 audio_driver_register(&sdl_audio_driver); 379 } 380 type_init(register_audio_sdl); 381