|
|
1.1 root 1: /* 1.1.1.2 ! root 2: * QEMU SDL audio driver ! 3: * ! 4: * Copyright (c) 2004-2005 Vassili Karpov (malc) ! 5: * 1.1 root 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 <SDL.h> 25: #include <SDL_thread.h> 26: #include "vl.h" 27: 1.1.1.2 ! root 28: #define AUDIO_CAP "sdl" ! 29: #include "audio_int.h" 1.1 root 30: 1.1.1.2 ! root 31: typedef struct SDLVoiceOut { ! 32: HWVoiceOut hw; ! 33: int live; ! 34: int rpos; ! 35: int decr; ! 36: } SDLVoiceOut; 1.1 root 37: 38: static struct { 39: int nb_samples; 40: } conf = { 41: 1024 42: }; 43: 44: struct SDLAudioState { 45: int exit; 46: SDL_mutex *mutex; 47: SDL_sem *sem; 48: int initialized; 49: } glob_sdl; 50: typedef struct SDLAudioState SDLAudioState; 51: 1.1.1.2 ! root 52: static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 1.1 root 53: { 1.1.1.2 ! root 54: va_list ap; ! 55: ! 56: va_start (ap, fmt); ! 57: AUD_vlog (AUDIO_CAP, fmt, ap); ! 58: va_end (ap); ! 59: ! 60: AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 1.1 root 61: } 62: 1.1.1.2 ! root 63: static int sdl_lock (SDLAudioState *s, const char *forfn) 1.1 root 64: { 65: if (SDL_LockMutex (s->mutex)) { 1.1.1.2 ! root 66: sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); 1.1 root 67: return -1; 68: } 69: return 0; 70: } 71: 1.1.1.2 ! root 72: static int sdl_unlock (SDLAudioState *s, const char *forfn) 1.1 root 73: { 74: if (SDL_UnlockMutex (s->mutex)) { 1.1.1.2 ! root 75: sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); 1.1 root 76: return -1; 77: } 78: return 0; 79: } 80: 1.1.1.2 ! root 81: static int sdl_post (SDLAudioState *s, const char *forfn) 1.1 root 82: { 83: if (SDL_SemPost (s->sem)) { 1.1.1.2 ! root 84: sdl_logerr ("SDL_SemPost for %s failed\n", forfn); 1.1 root 85: return -1; 86: } 87: return 0; 88: } 89: 1.1.1.2 ! root 90: static int sdl_wait (SDLAudioState *s, const char *forfn) 1.1 root 91: { 92: if (SDL_SemWait (s->sem)) { 1.1.1.2 ! root 93: sdl_logerr ("SDL_SemWait for %s failed\n", forfn); 1.1 root 94: return -1; 95: } 96: return 0; 97: } 98: 1.1.1.2 ! root 99: static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) 1.1 root 100: { 1.1.1.2 ! root 101: if (sdl_unlock (s, forfn)) { 1.1 root 102: return -1; 1.1.1.2 ! root 103: } 1.1 root 104: 1.1.1.2 ! root 105: return sdl_post (s, forfn); 1.1 root 106: } 107: 1.1.1.2 ! root 108: static int aud_to_sdlfmt (audfmt_e fmt, int *shift) 1.1 root 109: { 110: switch (fmt) { 1.1.1.2 ! root 111: case AUD_FMT_S8: ! 112: *shift = 0; ! 113: return AUDIO_S8; ! 114: ! 115: case AUD_FMT_U8: ! 116: *shift = 0; ! 117: return AUDIO_U8; ! 118: ! 119: case AUD_FMT_S16: ! 120: *shift = 1; ! 121: return AUDIO_S16LSB; ! 122: ! 123: case AUD_FMT_U16: ! 124: *shift = 1; ! 125: return AUDIO_U16LSB; ! 126: 1.1 root 127: default: 1.1.1.2 ! root 128: dolog ("Internal logic error: Bad audio format %d\n", fmt); ! 129: #ifdef DEBUG_AUDIO ! 130: abort (); ! 131: #endif ! 132: return AUDIO_U8; 1.1 root 133: } 134: } 135: 1.1.1.2 ! root 136: static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess) 1.1 root 137: { 1.1.1.2 ! root 138: switch (sdlfmt) { ! 139: case AUDIO_S8: ! 140: *endianess = 0; ! 141: *fmt = AUD_FMT_S8; ! 142: break; ! 143: ! 144: case AUDIO_U8: ! 145: *endianess = 0; ! 146: *fmt = AUD_FMT_U8; ! 147: break; ! 148: ! 149: case AUDIO_S16LSB: ! 150: *endianess = 0; ! 151: *fmt = AUD_FMT_S16; ! 152: break; ! 153: ! 154: case AUDIO_U16LSB: ! 155: *endianess = 0; ! 156: *fmt = AUD_FMT_U16; ! 157: break; ! 158: ! 159: case AUDIO_S16MSB: ! 160: *endianess = 1; ! 161: *fmt = AUD_FMT_S16; ! 162: break; ! 163: ! 164: case AUDIO_U16MSB: ! 165: *endianess = 1; ! 166: *fmt = AUD_FMT_U16; ! 167: break; ! 168: 1.1 root 169: default: 1.1.1.2 ! root 170: dolog ("Unrecognized SDL audio format %d\n", sdlfmt); ! 171: return -1; 1.1 root 172: } 1.1.1.2 ! root 173: ! 174: return 0; 1.1 root 175: } 176: 177: static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 178: { 179: int status; 180: 181: status = SDL_OpenAudio (req, obt); 182: if (status) { 1.1.1.2 ! root 183: sdl_logerr ("SDL_OpenAudio failed\n"); 1.1 root 184: } 185: return status; 186: } 187: 188: static void sdl_close (SDLAudioState *s) 189: { 190: if (s->initialized) { 1.1.1.2 ! root 191: sdl_lock (s, "sdl_close"); 1.1 root 192: s->exit = 1; 1.1.1.2 ! root 193: sdl_unlock_and_post (s, "sdl_close"); 1.1 root 194: SDL_PauseAudio (1); 195: SDL_CloseAudio (); 196: s->initialized = 0; 197: } 198: } 199: 200: static void sdl_callback (void *opaque, Uint8 *buf, int len) 201: { 1.1.1.2 ! root 202: SDLVoiceOut *sdl = opaque; 1.1 root 203: SDLAudioState *s = &glob_sdl; 1.1.1.2 ! root 204: HWVoiceOut *hw = &sdl->hw; ! 205: int samples = len >> hw->info.shift; 1.1 root 206: 207: if (s->exit) { 208: return; 209: } 210: 211: while (samples) { 1.1.1.2 ! root 212: int to_mix, decr; 1.1 root 213: 214: /* dolog ("in callback samples=%d\n", samples); */ 1.1.1.2 ! root 215: sdl_wait (s, "sdl_callback"); 1.1 root 216: if (s->exit) { 217: return; 218: } 219: 1.1.1.2 ! root 220: if (sdl_lock (s, "sdl_callback")) { ! 221: return; ! 222: } ! 223: ! 224: if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { ! 225: dolog ("sdl->live=%d hw->samples=%d\n", ! 226: sdl->live, hw->samples); ! 227: return; ! 228: } ! 229: ! 230: if (!sdl->live) { 1.1 root 231: goto again; 1.1.1.2 ! root 232: } 1.1 root 233: 234: /* dolog ("in callback live=%d\n", live); */ 1.1.1.2 ! root 235: to_mix = audio_MIN (samples, sdl->live); 1.1 root 236: decr = to_mix; 237: while (to_mix) { 238: int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); 239: st_sample_t *src = hw->mix_buf + hw->rpos; 240: 241: /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 242: hw->clip (buf, src, chunk); 1.1.1.2 ! root 243: mixeng_clear (src, chunk); ! 244: sdl->rpos = (sdl->rpos + chunk) % hw->samples; 1.1 root 245: to_mix -= chunk; 1.1.1.2 ! root 246: buf += chunk << hw->info.shift; 1.1 root 247: } 248: samples -= decr; 1.1.1.2 ! root 249: sdl->live -= decr; ! 250: sdl->decr += decr; 1.1 root 251: 252: again: 1.1.1.2 ! root 253: if (sdl_unlock (s, "sdl_callback")) { ! 254: return; ! 255: } 1.1 root 256: } 257: /* dolog ("done len=%d\n", len); */ 258: } 259: 1.1.1.2 ! root 260: static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 1.1 root 261: { 1.1.1.2 ! root 262: return audio_pcm_sw_write (sw, buf, len); 1.1 root 263: } 264: 1.1.1.2 ! root 265: static int sdl_run_out (HWVoiceOut *hw) 1.1 root 266: { 1.1.1.2 ! root 267: int decr, live; ! 268: SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 1.1 root 269: SDLAudioState *s = &glob_sdl; 270: 1.1.1.2 ! root 271: if (sdl_lock (s, "sdl_callback")) { ! 272: return 0; ! 273: } 1.1 root 274: 1.1.1.2 ! root 275: live = audio_pcm_hw_get_live_out (hw); ! 276: ! 277: if (sdl->decr > live) { ! 278: ldebug ("sdl->decr %d live %d sdl->live %d\n", ! 279: sdl->decr, ! 280: live, ! 281: sdl->live); 1.1 root 282: } 283: 1.1.1.2 ! root 284: decr = audio_MIN (sdl->decr, live); ! 285: sdl->decr -= decr; ! 286: ! 287: sdl->live = live - decr; ! 288: hw->rpos = sdl->rpos; ! 289: ! 290: if (sdl->live > 0) { ! 291: sdl_unlock_and_post (s, "sdl_callback"); ! 292: } ! 293: else { ! 294: sdl_unlock (s, "sdl_callback"); ! 295: } ! 296: return decr; ! 297: } 1.1 root 298: 1.1.1.2 ! root 299: static void sdl_fini_out (HWVoiceOut *hw) ! 300: { ! 301: (void) hw; ! 302: ! 303: sdl_close (&glob_sdl); ! 304: } ! 305: ! 306: static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as) ! 307: { ! 308: SDLVoiceOut *sdl = (SDLVoiceOut *) hw; ! 309: SDLAudioState *s = &glob_sdl; ! 310: SDL_AudioSpec req, obt; ! 311: int shift; ! 312: int endianess; ! 313: int err; ! 314: audfmt_e effective_fmt; ! 315: audsettings_t obt_as; ! 316: ! 317: shift <<= as->nchannels == 2; ! 318: ! 319: req.freq = as->freq; ! 320: req.format = aud_to_sdlfmt (as->fmt, &shift); ! 321: req.channels = as->nchannels; ! 322: req.samples = conf.nb_samples; 1.1 root 323: req.callback = sdl_callback; 324: req.userdata = sdl; 325: 1.1.1.2 ! root 326: if (sdl_open (&req, &obt)) { ! 327: return -1; ! 328: } ! 329: ! 330: err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); ! 331: if (err) { ! 332: sdl_close (s); 1.1 root 333: return -1; 1.1.1.2 ! root 334: } 1.1 root 335: 1.1.1.2 ! root 336: obt_as.freq = obt.freq; ! 337: obt_as.nchannels = obt.channels; ! 338: obt_as.fmt = effective_fmt; ! 339: ! 340: audio_pcm_init_info ( ! 341: &hw->info, ! 342: &obt_as, ! 343: audio_need_to_swap_endian (endianess) ! 344: ); ! 345: hw->samples = obt.samples; 1.1 root 346: 347: s->initialized = 1; 348: s->exit = 0; 349: SDL_PauseAudio (0); 350: return 0; 351: } 352: 1.1.1.2 ! root 353: static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 1.1 root 354: { 355: (void) hw; 356: 357: switch (cmd) { 358: case VOICE_ENABLE: 359: SDL_PauseAudio (0); 360: break; 361: 362: case VOICE_DISABLE: 363: SDL_PauseAudio (1); 364: break; 365: } 366: return 0; 367: } 368: 369: static void *sdl_audio_init (void) 370: { 371: SDLAudioState *s = &glob_sdl; 372: 373: if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 1.1.1.2 ! root 374: sdl_logerr ("SDL failed to initialize audio subsystem\n"); 1.1 root 375: return NULL; 376: } 377: 378: s->mutex = SDL_CreateMutex (); 379: if (!s->mutex) { 1.1.1.2 ! root 380: sdl_logerr ("Failed to create SDL mutex\n"); 1.1 root 381: SDL_QuitSubSystem (SDL_INIT_AUDIO); 382: return NULL; 383: } 384: 385: s->sem = SDL_CreateSemaphore (0); 386: if (!s->sem) { 1.1.1.2 ! root 387: sdl_logerr ("Failed to create SDL semaphore\n"); 1.1 root 388: SDL_DestroyMutex (s->mutex); 389: SDL_QuitSubSystem (SDL_INIT_AUDIO); 390: return NULL; 391: } 392: 393: return s; 394: } 395: 396: static void sdl_audio_fini (void *opaque) 397: { 398: SDLAudioState *s = opaque; 399: sdl_close (s); 400: SDL_DestroySemaphore (s->sem); 401: SDL_DestroyMutex (s->mutex); 402: SDL_QuitSubSystem (SDL_INIT_AUDIO); 403: } 404: 1.1.1.2 ! root 405: static struct audio_option sdl_options[] = { ! 406: {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, ! 407: "Size of SDL buffer in samples", NULL, 0}, ! 408: {NULL, 0, NULL, NULL, NULL, 0} ! 409: }; ! 410: ! 411: static struct audio_pcm_ops sdl_pcm_ops = { ! 412: sdl_init_out, ! 413: sdl_fini_out, ! 414: sdl_run_out, ! 415: sdl_write_out, ! 416: sdl_ctl_out, ! 417: ! 418: NULL, ! 419: NULL, ! 420: NULL, ! 421: NULL, ! 422: NULL 1.1 root 423: }; 424: 1.1.1.2 ! root 425: struct audio_driver sdl_audio_driver = { ! 426: INIT_FIELD (name = ) "sdl", ! 427: INIT_FIELD (descr = ) "SDL http://www.libsdl.org", ! 428: INIT_FIELD (options = ) sdl_options, ! 429: INIT_FIELD (init = ) sdl_audio_init, ! 430: INIT_FIELD (fini = ) sdl_audio_fini, ! 431: INIT_FIELD (pcm_ops = ) &sdl_pcm_ops, ! 432: INIT_FIELD (can_be_default = ) 1, ! 433: INIT_FIELD (max_voices_out = ) 1, ! 434: INIT_FIELD (max_voices_in = ) 0, ! 435: INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut), ! 436: INIT_FIELD (voice_size_in = ) 0 1.1 root 437: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.