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 "qemu/osdep.h" 25 #include <SDL.h> 26 #include <SDL_thread.h> 27 #include "qemu-common.h" 28 #include "audio.h" 29 30 #ifndef _WIN32 31 #ifdef __sun__ 32 #define _POSIX_PTHREAD_SEMANTICS 1 33 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 34 #include <pthread.h> 35 #endif 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 } 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 int initialized; 56 bool driver_created; 57 } glob_sdl; 58 typedef struct SDLAudioState SDLAudioState; 59 60 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 61 { 62 va_list ap; 63 64 va_start (ap, fmt); 65 AUD_vlog (AUDIO_CAP, fmt, ap); 66 va_end (ap); 67 68 AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 69 } 70 71 static int aud_to_sdlfmt (audfmt_e fmt) 72 { 73 switch (fmt) { 74 case AUD_FMT_S8: 75 return AUDIO_S8; 76 77 case AUD_FMT_U8: 78 return AUDIO_U8; 79 80 case AUD_FMT_S16: 81 return AUDIO_S16LSB; 82 83 case AUD_FMT_U16: 84 return AUDIO_U16LSB; 85 86 default: 87 dolog ("Internal logic error: Bad audio format %d\n", fmt); 88 #ifdef DEBUG_AUDIO 89 abort (); 90 #endif 91 return AUDIO_U8; 92 } 93 } 94 95 static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) 96 { 97 switch (sdlfmt) { 98 case AUDIO_S8: 99 *endianness = 0; 100 *fmt = AUD_FMT_S8; 101 break; 102 103 case AUDIO_U8: 104 *endianness = 0; 105 *fmt = AUD_FMT_U8; 106 break; 107 108 case AUDIO_S16LSB: 109 *endianness = 0; 110 *fmt = AUD_FMT_S16; 111 break; 112 113 case AUDIO_U16LSB: 114 *endianness = 0; 115 *fmt = AUD_FMT_U16; 116 break; 117 118 case AUDIO_S16MSB: 119 *endianness = 1; 120 *fmt = AUD_FMT_S16; 121 break; 122 123 case AUDIO_U16MSB: 124 *endianness = 1; 125 *fmt = AUD_FMT_U16; 126 break; 127 128 default: 129 dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 130 return -1; 131 } 132 133 return 0; 134 } 135 136 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 137 { 138 int status; 139 #ifndef _WIN32 140 int err; 141 sigset_t new, old; 142 143 /* Make sure potential threads created by SDL don't hog signals. */ 144 err = sigfillset (&new); 145 if (err) { 146 dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 147 return -1; 148 } 149 err = pthread_sigmask (SIG_BLOCK, &new, &old); 150 if (err) { 151 dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 152 return -1; 153 } 154 #endif 155 156 status = SDL_OpenAudio (req, obt); 157 if (status) { 158 sdl_logerr ("SDL_OpenAudio failed\n"); 159 } 160 161 #ifndef _WIN32 162 err = pthread_sigmask (SIG_SETMASK, &old, NULL); 163 if (err) { 164 dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 165 strerror (errno)); 166 /* We have failed to restore original signal mask, all bets are off, 167 so exit the process */ 168 exit (EXIT_FAILURE); 169 } 170 #endif 171 return status; 172 } 173 174 static void sdl_close (SDLAudioState *s) 175 { 176 if (s->initialized) { 177 SDL_LockAudio(); 178 s->exit = 1; 179 SDL_UnlockAudio(); 180 SDL_PauseAudio (1); 181 SDL_CloseAudio (); 182 s->initialized = 0; 183 } 184 } 185 186 static void sdl_callback (void *opaque, Uint8 *buf, int len) 187 { 188 SDLVoiceOut *sdl = opaque; 189 SDLAudioState *s = &glob_sdl; 190 HWVoiceOut *hw = &sdl->hw; 191 int samples = len >> hw->info.shift; 192 int to_mix, decr; 193 194 if (s->exit || !sdl->live) { 195 return; 196 } 197 198 /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ 199 200 to_mix = audio_MIN(samples, sdl->live); 201 decr = to_mix; 202 while (to_mix) { 203 int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); 204 struct st_sample *src = hw->mix_buf + hw->rpos; 205 206 /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 207 hw->clip(buf, src, chunk); 208 hw->rpos = (hw->rpos + chunk) % hw->samples; 209 to_mix -= chunk; 210 buf += chunk << hw->info.shift; 211 } 212 samples -= decr; 213 sdl->live -= decr; 214 sdl->decr += decr; 215 216 /* dolog ("done len=%d\n", len); */ 217 218 /* SDL2 does not clear the remaining buffer for us, so do it on our own */ 219 if (samples) { 220 memset(buf, 0, samples << hw->info.shift); 221 } 222 } 223 224 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 225 { 226 return audio_pcm_sw_write (sw, buf, len); 227 } 228 229 static int sdl_run_out (HWVoiceOut *hw, int live) 230 { 231 int decr; 232 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 233 234 SDL_LockAudio(); 235 236 if (sdl->decr > live) { 237 ldebug ("sdl->decr %d live %d sdl->live %d\n", 238 sdl->decr, 239 live, 240 sdl->live); 241 } 242 243 decr = audio_MIN (sdl->decr, live); 244 sdl->decr -= decr; 245 246 sdl->live = live; 247 248 SDL_UnlockAudio(); 249 250 return decr; 251 } 252 253 static void sdl_fini_out (HWVoiceOut *hw) 254 { 255 (void) hw; 256 257 sdl_close (&glob_sdl); 258 } 259 260 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 261 void *drv_opaque) 262 { 263 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 264 SDLAudioState *s = &glob_sdl; 265 SDL_AudioSpec req, obt; 266 int endianness; 267 int err; 268 audfmt_e effective_fmt; 269 struct audsettings obt_as; 270 271 req.freq = as->freq; 272 req.format = aud_to_sdlfmt (as->fmt); 273 req.channels = as->nchannels; 274 req.samples = conf.nb_samples; 275 req.callback = sdl_callback; 276 req.userdata = sdl; 277 278 if (sdl_open (&req, &obt)) { 279 return -1; 280 } 281 282 err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 283 if (err) { 284 sdl_close (s); 285 return -1; 286 } 287 288 obt_as.freq = obt.freq; 289 obt_as.nchannels = obt.channels; 290 obt_as.fmt = effective_fmt; 291 obt_as.endianness = endianness; 292 293 audio_pcm_init_info (&hw->info, &obt_as); 294 hw->samples = obt.samples; 295 296 s->initialized = 1; 297 s->exit = 0; 298 SDL_PauseAudio (0); 299 return 0; 300 } 301 302 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 303 { 304 (void) hw; 305 306 switch (cmd) { 307 case VOICE_ENABLE: 308 SDL_PauseAudio (0); 309 break; 310 311 case VOICE_DISABLE: 312 SDL_PauseAudio (1); 313 break; 314 } 315 return 0; 316 } 317 318 static void *sdl_audio_init (void) 319 { 320 SDLAudioState *s = &glob_sdl; 321 if (s->driver_created) { 322 sdl_logerr("Can't create multiple sdl backends\n"); 323 return NULL; 324 } 325 326 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 327 sdl_logerr ("SDL failed to initialize audio subsystem\n"); 328 return NULL; 329 } 330 331 s->driver_created = true; 332 return s; 333 } 334 335 static void sdl_audio_fini (void *opaque) 336 { 337 SDLAudioState *s = opaque; 338 sdl_close (s); 339 SDL_QuitSubSystem (SDL_INIT_AUDIO); 340 s->driver_created = false; 341 } 342 343 static struct audio_option sdl_options[] = { 344 { 345 .name = "SAMPLES", 346 .tag = AUD_OPT_INT, 347 .valp = &conf.nb_samples, 348 .descr = "Size of SDL buffer in samples" 349 }, 350 { /* End of list */ } 351 }; 352 353 static struct audio_pcm_ops sdl_pcm_ops = { 354 .init_out = sdl_init_out, 355 .fini_out = sdl_fini_out, 356 .run_out = sdl_run_out, 357 .write = sdl_write_out, 358 .ctl_out = sdl_ctl_out, 359 }; 360 361 static struct audio_driver sdl_audio_driver = { 362 .name = "sdl", 363 .descr = "SDL http://www.libsdl.org", 364 .options = sdl_options, 365 .init = sdl_audio_init, 366 .fini = sdl_audio_fini, 367 .pcm_ops = &sdl_pcm_ops, 368 .can_be_default = 1, 369 .max_voices_out = 1, 370 .max_voices_in = 0, 371 .voice_size_out = sizeof (SDLVoiceOut), 372 .voice_size_in = 0 373 }; 374 375 static void register_audio_sdl(void) 376 { 377 audio_driver_register(&sdl_audio_driver); 378 } 379 type_init(register_audio_sdl); 380