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