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 decr; 45 int pending; 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 } 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, NULL); 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 (sdl_lock (s, "sdl_callback")) { 229 return; 230 } 231 232 if (s->exit) { 233 return; 234 } 235 236 while (samples) { 237 int to_mix, decr; 238 239 while (!sdl->pending) { 240 if (sdl_unlock (s, "sdl_callback")) { 241 return; 242 } 243 244 sdl_wait (s, "sdl_callback"); 245 if (s->exit) { 246 return; 247 } 248 249 if (sdl_lock (s, "sdl_callback")) { 250 return; 251 } 252 sdl->pending += sdl->live; 253 sdl->live = 0; 254 } 255 256 to_mix = audio_MIN (samples, sdl->pending); 257 decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0); 258 buf += decr << hw->info.shift; 259 samples -= decr; 260 sdl->decr += decr; 261 sdl->pending -= decr; 262 } 263 264 if (sdl_unlock (s, "sdl_callback")) { 265 return; 266 } 267 } 268 269 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 270 { 271 return audio_pcm_sw_write (sw, buf, len); 272 } 273 274 static int sdl_run_out (HWVoiceOut *hw, int live) 275 { 276 int decr; 277 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 278 SDLAudioState *s = &glob_sdl; 279 280 if (sdl_lock (s, "sdl_run_out")) { 281 return 0; 282 } 283 284 sdl->live = live; 285 decr = sdl->decr; 286 sdl->decr = 0; 287 288 if (sdl->live > 0) { 289 sdl_unlock_and_post (s, "sdl_run_out"); 290 } 291 else { 292 sdl_unlock (s, "sdl_run_out"); 293 } 294 return decr; 295 } 296 297 static void sdl_fini_out (HWVoiceOut *hw) 298 { 299 (void) hw; 300 301 sdl_close (&glob_sdl); 302 } 303 304 static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) 305 { 306 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 307 SDLAudioState *s = &glob_sdl; 308 SDL_AudioSpec req, obt; 309 int shift; 310 int endianess; 311 int err; 312 audfmt_e effective_fmt; 313 struct audsettings obt_as; 314 315 shift <<= as->nchannels == 2; 316 317 req.freq = as->freq; 318 req.format = aud_to_sdlfmt (as->fmt, &shift); 319 req.channels = as->nchannels; 320 req.samples = conf.nb_samples; 321 req.callback = sdl_callback; 322 req.userdata = sdl; 323 324 if (sdl_open (&req, &obt)) { 325 return -1; 326 } 327 328 err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); 329 if (err) { 330 sdl_close (s); 331 return -1; 332 } 333 334 obt_as.freq = obt.freq; 335 obt_as.nchannels = obt.channels; 336 obt_as.fmt = effective_fmt; 337 obt_as.endianness = endianess; 338 339 audio_pcm_init_info (&hw->info, &obt_as); 340 hw->samples = obt.samples; 341 342 s->initialized = 1; 343 s->exit = 0; 344 SDL_PauseAudio (0); 345 return 0; 346 } 347 348 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 349 { 350 (void) hw; 351 352 switch (cmd) { 353 case VOICE_ENABLE: 354 SDL_PauseAudio (0); 355 break; 356 357 case VOICE_DISABLE: 358 SDL_PauseAudio (1); 359 break; 360 } 361 return 0; 362 } 363 364 static void *sdl_audio_init (void) 365 { 366 SDLAudioState *s = &glob_sdl; 367 368 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 369 sdl_logerr ("SDL failed to initialize audio subsystem\n"); 370 return NULL; 371 } 372 373 s->mutex = SDL_CreateMutex (); 374 if (!s->mutex) { 375 sdl_logerr ("Failed to create SDL mutex\n"); 376 SDL_QuitSubSystem (SDL_INIT_AUDIO); 377 return NULL; 378 } 379 380 s->sem = SDL_CreateSemaphore (0); 381 if (!s->sem) { 382 sdl_logerr ("Failed to create SDL semaphore\n"); 383 SDL_DestroyMutex (s->mutex); 384 SDL_QuitSubSystem (SDL_INIT_AUDIO); 385 return NULL; 386 } 387 388 return s; 389 } 390 391 static void sdl_audio_fini (void *opaque) 392 { 393 SDLAudioState *s = opaque; 394 sdl_close (s); 395 SDL_DestroySemaphore (s->sem); 396 SDL_DestroyMutex (s->mutex); 397 SDL_QuitSubSystem (SDL_INIT_AUDIO); 398 } 399 400 static struct audio_option sdl_options[] = { 401 { 402 .name = "SAMPLES", 403 .tag = AUD_OPT_INT, 404 .valp = &conf.nb_samples, 405 .descr = "Size of SDL buffer in samples" 406 }, 407 { /* End of list */ } 408 }; 409 410 static struct audio_pcm_ops sdl_pcm_ops = { 411 .init_out = sdl_init_out, 412 .fini_out = sdl_fini_out, 413 .run_out = sdl_run_out, 414 .write = sdl_write_out, 415 .ctl_out = sdl_ctl_out, 416 }; 417 418 struct audio_driver sdl_audio_driver = { 419 .name = "sdl", 420 .descr = "SDL http://www.libsdl.org", 421 .options = sdl_options, 422 .init = sdl_audio_init, 423 .fini = sdl_audio_fini, 424 .pcm_ops = &sdl_pcm_ops, 425 .can_be_default = 1, 426 .max_voices_out = 1, 427 .max_voices_in = 0, 428 .voice_size_out = sizeof (SDLVoiceOut), 429 .voice_size_in = 0 430 }; 431