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 int live; 45 int 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 int samples = len >> hw->info.shift; 188 int to_mix, decr; 189 190 if (s->exit || !sdl->live) { 191 return; 192 } 193 194 /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ 195 196 to_mix = audio_MIN(samples, sdl->live); 197 decr = to_mix; 198 while (to_mix) { 199 int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); 200 struct st_sample *src = hw->mix_buf + hw->rpos; 201 202 /* dolog ("in callback to_mix %d, chunk %d\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=%d\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 int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 221 { 222 return audio_pcm_sw_write (sw, buf, len); 223 } 224 225 static int sdl_run_out (HWVoiceOut *hw, int live) 226 { 227 int decr; 228 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 229 230 SDL_LockAudio(); 231 232 if (sdl->decr > live) { 233 ldebug ("sdl->decr %d live %d sdl->live %d\n", 234 sdl->decr, 235 live, 236 sdl->live); 237 } 238 239 decr = audio_MIN (sdl->decr, live); 240 sdl->decr -= decr; 241 242 sdl->live = live; 243 244 SDL_UnlockAudio(); 245 246 return decr; 247 } 248 249 static void sdl_fini_out (HWVoiceOut *hw) 250 { 251 (void) hw; 252 253 sdl_close (&glob_sdl); 254 } 255 256 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, 257 void *drv_opaque) 258 { 259 SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 260 SDLAudioState *s = &glob_sdl; 261 SDL_AudioSpec req, obt; 262 int endianness; 263 int err; 264 AudioFormat effective_fmt; 265 struct audsettings obt_as; 266 267 req.freq = as->freq; 268 req.format = aud_to_sdlfmt (as->fmt); 269 req.channels = as->nchannels; 270 req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); 271 req.callback = sdl_callback; 272 req.userdata = sdl; 273 274 if (sdl_open (&req, &obt)) { 275 return -1; 276 } 277 278 err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); 279 if (err) { 280 sdl_close (s); 281 return -1; 282 } 283 284 obt_as.freq = obt.freq; 285 obt_as.nchannels = obt.channels; 286 obt_as.fmt = effective_fmt; 287 obt_as.endianness = endianness; 288 289 audio_pcm_init_info (&hw->info, &obt_as); 290 hw->samples = obt.samples; 291 292 s->initialized = 1; 293 s->exit = 0; 294 SDL_PauseAudio (0); 295 return 0; 296 } 297 298 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 299 { 300 (void) hw; 301 302 switch (cmd) { 303 case VOICE_ENABLE: 304 SDL_PauseAudio (0); 305 break; 306 307 case VOICE_DISABLE: 308 SDL_PauseAudio (1); 309 break; 310 } 311 return 0; 312 } 313 314 static void *sdl_audio_init(Audiodev *dev) 315 { 316 SDLAudioState *s = &glob_sdl; 317 if (s->driver_created) { 318 sdl_logerr("Can't create multiple sdl backends\n"); 319 return NULL; 320 } 321 322 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 323 sdl_logerr ("SDL failed to initialize audio subsystem\n"); 324 return NULL; 325 } 326 327 s->driver_created = true; 328 s->dev = dev; 329 return s; 330 } 331 332 static void sdl_audio_fini (void *opaque) 333 { 334 SDLAudioState *s = opaque; 335 sdl_close (s); 336 SDL_QuitSubSystem (SDL_INIT_AUDIO); 337 s->driver_created = false; 338 s->dev = NULL; 339 } 340 341 static struct audio_pcm_ops sdl_pcm_ops = { 342 .init_out = sdl_init_out, 343 .fini_out = sdl_fini_out, 344 .run_out = sdl_run_out, 345 .write = sdl_write_out, 346 .ctl_out = sdl_ctl_out, 347 }; 348 349 static struct audio_driver sdl_audio_driver = { 350 .name = "sdl", 351 .descr = "SDL http://www.libsdl.org", 352 .init = sdl_audio_init, 353 .fini = sdl_audio_fini, 354 .pcm_ops = &sdl_pcm_ops, 355 .can_be_default = 1, 356 .max_voices_out = 1, 357 .max_voices_in = 0, 358 .voice_size_out = sizeof (SDLVoiceOut), 359 .voice_size_in = 0 360 }; 361 362 static void register_audio_sdl(void) 363 { 364 audio_driver_register(&sdl_audio_driver); 365 } 366 type_init(register_audio_sdl); 367