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