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