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 #include "qemu/osdep.h" 25 #include <SDL.h> 26 #include <SDL_thread.h> 27 #include "qemu-common.h" 28 #include "audio.h" 29 30 #ifndef _WIN32 31 #ifdef __sun__ 32 #define _POSIX_PTHREAD_SEMANTICS 1 33 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 34 #include <pthread.h> 35 #endif 36 #endif 37 38 #define AUDIO_CAP "sdl" 39 #include "audio_int.h" 40 41 typedef struct SDLVoiceOut { 42 HWVoiceOut hw; 43 int live; 44 int rpos; 45 int decr; 46 } SDLVoiceOut; 47 48 static struct { 49 int nb_samples; 50 } conf = { 51 .nb_samples = 1024 52 }; 53 54 static struct SDLAudioState { 55 int exit; 56 SDL_mutex *mutex; 57 SDL_sem *sem; 58 int initialized; 59 bool driver_created; 60 } glob_sdl; 61 typedef struct SDLAudioState SDLAudioState; 62 63 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 64 { 65 va_list ap; 66 67 va_start (ap, fmt); 68 AUD_vlog (AUDIO_CAP, fmt, ap); 69 va_end (ap); 70 71 AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 72 } 73 74 static int sdl_lock (SDLAudioState *s, const char *forfn) 75 { 76 if (SDL_LockMutex (s->mutex)) { 77 sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); 78 return -1; 79 } 80 return 0; 81 } 82 83 static int sdl_unlock (SDLAudioState *s, const char *forfn) 84 { 85 if (SDL_UnlockMutex (s->mutex)) { 86 sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); 87 return -1; 88 } 89 return 0; 90 } 91 92 static int sdl_post (SDLAudioState *s, const char *forfn) 93 { 94 if (SDL_SemPost (s->sem)) { 95 sdl_logerr ("SDL_SemPost for %s failed\n", forfn); 96 return -1; 97 } 98 return 0; 99 } 100 101 static int sdl_wait (SDLAudioState *s, const char *forfn) 102 { 103 if (SDL_SemWait (s->sem)) { 104 sdl_logerr ("SDL_SemWait for %s failed\n", forfn); 105 return -1; 106 } 107 return 0; 108 } 109 110 static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) 111 { 112 if (sdl_unlock (s, forfn)) { 113 return -1; 114 } 115 116 return sdl_post (s, forfn); 117 } 118 119 static int aud_to_sdlfmt (audfmt_e fmt) 120 { 121 switch (fmt) { 122 case AUD_FMT_S8: 123 return AUDIO_S8; 124 125 case AUD_FMT_U8: 126 return AUDIO_U8; 127 128 case AUD_FMT_S16: 129 return AUDIO_S16LSB; 130 131 case AUD_FMT_U16: 132 return AUDIO_U16LSB; 133 134 default: 135 dolog ("Internal logic error: Bad audio format %d\n", fmt); 136 #ifdef DEBUG_AUDIO 137 abort (); 138 #endif 139 return AUDIO_U8; 140 } 141 } 142 143 static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) 144 { 145 switch (sdlfmt) { 146 case AUDIO_S8: 147 *endianness = 0; 148 *fmt = AUD_FMT_S8; 149 break; 150 151 case AUDIO_U8: 152 *endianness = 0; 153 *fmt = AUD_FMT_U8; 154 break; 155 156 case AUDIO_S16LSB: 157 *endianness = 0; 158 *fmt = AUD_FMT_S16; 159 break; 160 161 case AUDIO_U16LSB: 162 *endianness = 0; 163 *fmt = AUD_FMT_U16; 164 break; 165 166 case AUDIO_S16MSB: 167 *endianness = 1; 168 *fmt = AUD_FMT_S16; 169 break; 170 171 case AUDIO_U16MSB: 172 *endianness = 1; 173 *fmt = AUD_FMT_U16; 174 break; 175 176 default: 177 dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 178 return -1; 179 } 180 181 return 0; 182 } 183 184 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 185 { 186 int status; 187 #ifndef _WIN32 188 int err; 189 sigset_t new, old; 190 191 /* Make sure potential threads created by SDL don't hog signals. */ 192 err = sigfillset (&new); 193 if (err) { 194 dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 195 return -1; 196 } 197 err = pthread_sigmask (SIG_BLOCK, &new, &old); 198 if (err) { 199 dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 200 return -1; 201 } 202 #endif 203 204 status = SDL_OpenAudio (req, obt); 205 if (status) { 206 sdl_logerr ("SDL_OpenAudio failed\n"); 207 } 208 209 #ifndef _WIN32 210 err = pthread_sigmask (SIG_SETMASK, &old, NULL); 211 if (err) { 212 dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 213 strerror (errno)); 214 /* We have failed to restore original signal mask, all bets are off, 215 so exit the process */ 216 exit (EXIT_FAILURE); 217 } 218 #endif 219 return status; 220 } 221 222 static void sdl_close (SDLAudioState *s) 223 { 224 if (s->initialized) { 225 sdl_lock (s, "sdl_close"); 226 s->exit = 1; 227 sdl_unlock_and_post (s, "sdl_close"); 228 SDL_PauseAudio (1); 229 SDL_CloseAudio (); 230 s->initialized = 0; 231 } 232 } 233 234 static void sdl_callback (void *opaque, Uint8 *buf, int len) 235 { 236 SDLVoiceOut *sdl = opaque; 237 SDLAudioState *s = &glob_sdl; 238 HWVoiceOut *hw = &sdl->hw; 239 int samples = len >> hw->info.shift; 240 241 if (s->exit) { 242 return; 243 } 244 245 while (samples) { 246 int to_mix, decr; 247 248 /* dolog ("in callback samples=%d\n", samples); */ 249 sdl_wait (s, "sdl_callback"); 250 if (s->exit) { 251 return; 252 } 253 254 if (sdl_lock (s, "sdl_callback")) { 255 return; 256 } 257 258 if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { 259 dolog ("sdl->live=%d hw->samples=%d\n", 260 sdl->live, hw->samples); 261 return; 262 } 263 264 if (!sdl->live) { 265 goto again; 266 } 267 268 /* dolog ("in callback live=%d\n", live); */ 269 to_mix = audio_MIN (samples, sdl->live); 270 decr = to_mix; 271 while (to_mix) { 272 int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); 273 struct st_sample *src = hw->mix_buf + hw->rpos; 274 275 /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 276 hw->clip (buf, src, chunk); 277 sdl->rpos = (sdl->rpos + chunk) % hw->samples; 278 to_mix -= chunk; 279 buf += chunk << hw->info.shift; 280 } 281 samples -= decr; 282 sdl->live -= decr; 283 sdl->decr += decr; 284 285 again: 286 if (sdl_unlock (s, "sdl_callback")) { 287 return; 288 } 289 } 290 /* dolog ("done len=%d\n", len); */ 291 } 292 293 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 294 { 295 return audio_pcm_sw_write (sw, buf, len); 296 } 297 298 static int sdl_run_out (HWVoiceOut *hw, int live) 299 { 300 int decr; 301 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 302 SDLAudioState *s = &glob_sdl; 303 304 if (sdl_lock (s, "sdl_run_out")) { 305 return 0; 306 } 307 308 if (sdl->decr > live) { 309 ldebug ("sdl->decr %d live %d sdl->live %d\n", 310 sdl->decr, 311 live, 312 sdl->live); 313 } 314 315 decr = audio_MIN (sdl->decr, live); 316 sdl->decr -= decr; 317 318 sdl->live = live - decr; 319 hw->rpos = sdl->rpos; 320 321 if (sdl->live > 0) { 322 sdl_unlock_and_post (s, "sdl_run_out"); 323 } 324 else { 325 sdl_unlock (s, "sdl_run_out"); 326 } 327 return decr; 328 } 329 330 static void sdl_fini_out (HWVoiceOut *hw) 331 { 332 (void) hw; 333 334 sdl_close (&glob_sdl); 335 } 336 337 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 338 void *drv_opaque) 339 { 340 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 341 SDLAudioState *s = &glob_sdl; 342 SDL_AudioSpec req, obt; 343 int endianness; 344 int err; 345 audfmt_e effective_fmt; 346 struct audsettings obt_as; 347 348 req.freq = as->freq; 349 req.format = aud_to_sdlfmt (as->fmt); 350 req.channels = as->nchannels; 351 req.samples = conf.nb_samples; 352 req.callback = sdl_callback; 353 req.userdata = sdl; 354 355 if (sdl_open (&req, &obt)) { 356 return -1; 357 } 358 359 err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 360 if (err) { 361 sdl_close (s); 362 return -1; 363 } 364 365 obt_as.freq = obt.freq; 366 obt_as.nchannels = obt.channels; 367 obt_as.fmt = effective_fmt; 368 obt_as.endianness = endianness; 369 370 audio_pcm_init_info (&hw->info, &obt_as); 371 hw->samples = obt.samples; 372 373 s->initialized = 1; 374 s->exit = 0; 375 SDL_PauseAudio (0); 376 return 0; 377 } 378 379 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 380 { 381 (void) hw; 382 383 switch (cmd) { 384 case VOICE_ENABLE: 385 SDL_PauseAudio (0); 386 break; 387 388 case VOICE_DISABLE: 389 SDL_PauseAudio (1); 390 break; 391 } 392 return 0; 393 } 394 395 static void *sdl_audio_init (void) 396 { 397 SDLAudioState *s = &glob_sdl; 398 if (s->driver_created) { 399 sdl_logerr("Can't create multiple sdl backends\n"); 400 return NULL; 401 } 402 403 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 404 sdl_logerr ("SDL failed to initialize audio subsystem\n"); 405 return NULL; 406 } 407 408 s->mutex = SDL_CreateMutex (); 409 if (!s->mutex) { 410 sdl_logerr ("Failed to create SDL mutex\n"); 411 SDL_QuitSubSystem (SDL_INIT_AUDIO); 412 return NULL; 413 } 414 415 s->sem = SDL_CreateSemaphore (0); 416 if (!s->sem) { 417 sdl_logerr ("Failed to create SDL semaphore\n"); 418 SDL_DestroyMutex (s->mutex); 419 SDL_QuitSubSystem (SDL_INIT_AUDIO); 420 return NULL; 421 } 422 423 s->driver_created = true; 424 return s; 425 } 426 427 static void sdl_audio_fini (void *opaque) 428 { 429 SDLAudioState *s = opaque; 430 sdl_close (s); 431 SDL_DestroySemaphore (s->sem); 432 SDL_DestroyMutex (s->mutex); 433 SDL_QuitSubSystem (SDL_INIT_AUDIO); 434 s->driver_created = false; 435 } 436 437 static struct audio_option sdl_options[] = { 438 { 439 .name = "SAMPLES", 440 .tag = AUD_OPT_INT, 441 .valp = &conf.nb_samples, 442 .descr = "Size of SDL buffer in samples" 443 }, 444 { /* End of list */ } 445 }; 446 447 static struct audio_pcm_ops sdl_pcm_ops = { 448 .init_out = sdl_init_out, 449 .fini_out = sdl_fini_out, 450 .run_out = sdl_run_out, 451 .write = sdl_write_out, 452 .ctl_out = sdl_ctl_out, 453 }; 454 455 struct audio_driver sdl_audio_driver = { 456 .name = "sdl", 457 .descr = "SDL http://www.libsdl.org", 458 .options = sdl_options, 459 .init = sdl_audio_init, 460 .fini = sdl_audio_fini, 461 .pcm_ops = &sdl_pcm_ops, 462 .can_be_default = 1, 463 .max_voices_out = 1, 464 .max_voices_in = 0, 465 .voice_size_out = sizeof (SDLVoiceOut), 466 .voice_size_in = 0 467 }; 468