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