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