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