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