|
|
1.1 ! root 1: /* ! 2: * QEMU SDL audio output driver ! 3: * ! 4: * Copyright (c) 2004 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 <SDL.h> ! 25: #include <SDL_thread.h> ! 26: #include "vl.h" ! 27: ! 28: #include "audio/audio_int.h" ! 29: ! 30: typedef struct SDLVoice { ! 31: HWVoice hw; ! 32: } SDLVoice; ! 33: ! 34: #define dolog(...) AUD_log ("sdl", __VA_ARGS__) ! 35: #ifdef DEBUG ! 36: #define ldebug(...) dolog (__VA_ARGS__) ! 37: #else ! 38: #define ldebug(...) ! 39: #endif ! 40: ! 41: #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES" ! 42: ! 43: #define errstr() SDL_GetError () ! 44: ! 45: static struct { ! 46: int nb_samples; ! 47: } conf = { ! 48: 1024 ! 49: }; ! 50: ! 51: struct SDLAudioState { ! 52: int exit; ! 53: SDL_mutex *mutex; ! 54: SDL_sem *sem; ! 55: int initialized; ! 56: } glob_sdl; ! 57: typedef struct SDLAudioState SDLAudioState; ! 58: ! 59: static void sdl_hw_run (HWVoice *hw) ! 60: { ! 61: (void) hw; ! 62: } ! 63: ! 64: static int sdl_lock (SDLAudioState *s) ! 65: { ! 66: if (SDL_LockMutex (s->mutex)) { ! 67: dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ()); ! 68: return -1; ! 69: } ! 70: return 0; ! 71: } ! 72: ! 73: static int sdl_unlock (SDLAudioState *s) ! 74: { ! 75: if (SDL_UnlockMutex (s->mutex)) { ! 76: dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ()); ! 77: return -1; ! 78: } ! 79: return 0; ! 80: } ! 81: ! 82: static int sdl_post (SDLAudioState *s) ! 83: { ! 84: if (SDL_SemPost (s->sem)) { ! 85: dolog ("SDL_SemPost failed\nReason: %s\n", errstr ()); ! 86: return -1; ! 87: } ! 88: return 0; ! 89: } ! 90: ! 91: static int sdl_wait (SDLAudioState *s) ! 92: { ! 93: if (SDL_SemWait (s->sem)) { ! 94: dolog ("SDL_SemWait failed\nReason: %s\n", errstr ()); ! 95: return -1; ! 96: } ! 97: return 0; ! 98: } ! 99: ! 100: static int sdl_unlock_and_post (SDLAudioState *s) ! 101: { ! 102: if (sdl_unlock (s)) ! 103: return -1; ! 104: ! 105: return sdl_post (s); ! 106: } ! 107: ! 108: static int sdl_hw_write (SWVoice *sw, void *buf, int len) ! 109: { ! 110: int ret; ! 111: SDLAudioState *s = &glob_sdl; ! 112: sdl_lock (s); ! 113: ret = pcm_hw_write (sw, buf, len); ! 114: sdl_unlock_and_post (s); ! 115: return ret; ! 116: } ! 117: ! 118: static int AUD_to_sdlfmt (audfmt_e fmt, int *shift) ! 119: { ! 120: *shift = 0; ! 121: switch (fmt) { ! 122: case AUD_FMT_S8: return AUDIO_S8; ! 123: case AUD_FMT_U8: return AUDIO_U8; ! 124: case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB; ! 125: case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB; ! 126: default: ! 127: dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); ! 128: exit (EXIT_FAILURE); ! 129: } ! 130: } ! 131: ! 132: static int sdl_to_audfmt (int fmt) ! 133: { ! 134: switch (fmt) { ! 135: case AUDIO_S8: return AUD_FMT_S8; ! 136: case AUDIO_U8: return AUD_FMT_U8; ! 137: case AUDIO_S16LSB: return AUD_FMT_S16; ! 138: case AUDIO_U16LSB: return AUD_FMT_U16; ! 139: default: ! 140: dolog ("Internal logic error: Unrecognized SDL audio format %d\n" ! 141: "Aborting\n", fmt); ! 142: exit (EXIT_FAILURE); ! 143: } ! 144: } ! 145: ! 146: static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) ! 147: { ! 148: int status; ! 149: ! 150: status = SDL_OpenAudio (req, obt); ! 151: if (status) { ! 152: dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ()); ! 153: } ! 154: return status; ! 155: } ! 156: ! 157: static void sdl_close (SDLAudioState *s) ! 158: { ! 159: if (s->initialized) { ! 160: sdl_lock (s); ! 161: s->exit = 1; ! 162: sdl_unlock_and_post (s); ! 163: SDL_PauseAudio (1); ! 164: SDL_CloseAudio (); ! 165: s->initialized = 0; ! 166: } ! 167: } ! 168: ! 169: static void sdl_callback (void *opaque, Uint8 *buf, int len) ! 170: { ! 171: SDLVoice *sdl = opaque; ! 172: SDLAudioState *s = &glob_sdl; ! 173: HWVoice *hw = &sdl->hw; ! 174: int samples = len >> hw->shift; ! 175: ! 176: if (s->exit) { ! 177: return; ! 178: } ! 179: ! 180: while (samples) { ! 181: int to_mix, live, decr; ! 182: ! 183: /* dolog ("in callback samples=%d\n", samples); */ ! 184: sdl_wait (s); ! 185: if (s->exit) { ! 186: return; ! 187: } ! 188: ! 189: sdl_lock (s); ! 190: live = pcm_hw_get_live (hw); ! 191: if (live <= 0) ! 192: goto again; ! 193: ! 194: /* dolog ("in callback live=%d\n", live); */ ! 195: to_mix = audio_MIN (samples, live); ! 196: decr = to_mix; ! 197: while (to_mix) { ! 198: int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); ! 199: st_sample_t *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: memset (src, 0, chunk * sizeof (st_sample_t)); ! 204: hw->rpos = (hw->rpos + chunk) % hw->samples; ! 205: to_mix -= chunk; ! 206: buf += chunk << hw->shift; ! 207: } ! 208: samples -= decr; ! 209: pcm_hw_dec_live (hw, decr); ! 210: ! 211: again: ! 212: sdl_unlock (s); ! 213: } ! 214: /* dolog ("done len=%d\n", len); */ ! 215: } ! 216: ! 217: static void sdl_hw_fini (HWVoice *hw) ! 218: { ! 219: ldebug ("sdl_hw_fini %d fixed=%d\n", ! 220: glob_sdl.initialized, audio_state.fixed_format); ! 221: sdl_close (&glob_sdl); ! 222: } ! 223: ! 224: static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) ! 225: { ! 226: SDLVoice *sdl = (SDLVoice *) hw; ! 227: SDLAudioState *s = &glob_sdl; ! 228: SDL_AudioSpec req, obt; ! 229: int shift; ! 230: ! 231: ldebug ("sdl_hw_init %d freq=%d fixed=%d\n", ! 232: s->initialized, freq, audio_state.fixed_format); ! 233: ! 234: if (nchannels != 2) { ! 235: dolog ("Bogus channel count %d\n", nchannels); ! 236: return -1; ! 237: } ! 238: ! 239: req.freq = freq; ! 240: req.format = AUD_to_sdlfmt (fmt, &shift); ! 241: req.channels = nchannels; ! 242: req.samples = conf.nb_samples; ! 243: shift <<= nchannels == 2; ! 244: ! 245: req.callback = sdl_callback; ! 246: req.userdata = sdl; ! 247: ! 248: if (sdl_open (&req, &obt)) ! 249: return -1; ! 250: ! 251: hw->freq = obt.freq; ! 252: hw->fmt = sdl_to_audfmt (obt.format); ! 253: hw->nchannels = obt.channels; ! 254: hw->bufsize = obt.samples << shift; ! 255: ! 256: s->initialized = 1; ! 257: s->exit = 0; ! 258: SDL_PauseAudio (0); ! 259: return 0; ! 260: } ! 261: ! 262: static int sdl_hw_ctl (HWVoice *hw, int cmd, ...) ! 263: { ! 264: (void) hw; ! 265: ! 266: switch (cmd) { ! 267: case VOICE_ENABLE: ! 268: SDL_PauseAudio (0); ! 269: break; ! 270: ! 271: case VOICE_DISABLE: ! 272: SDL_PauseAudio (1); ! 273: break; ! 274: } ! 275: return 0; ! 276: } ! 277: ! 278: static void *sdl_audio_init (void) ! 279: { ! 280: SDLAudioState *s = &glob_sdl; ! 281: conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples); ! 282: ! 283: if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { ! 284: dolog ("SDL failed to initialize audio subsystem\nReason: %s\n", ! 285: errstr ()); ! 286: return NULL; ! 287: } ! 288: ! 289: s->mutex = SDL_CreateMutex (); ! 290: if (!s->mutex) { ! 291: dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ()); ! 292: SDL_QuitSubSystem (SDL_INIT_AUDIO); ! 293: return NULL; ! 294: } ! 295: ! 296: s->sem = SDL_CreateSemaphore (0); ! 297: if (!s->sem) { ! 298: dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ()); ! 299: SDL_DestroyMutex (s->mutex); ! 300: SDL_QuitSubSystem (SDL_INIT_AUDIO); ! 301: return NULL; ! 302: } ! 303: ! 304: return s; ! 305: } ! 306: ! 307: static void sdl_audio_fini (void *opaque) ! 308: { ! 309: SDLAudioState *s = opaque; ! 310: sdl_close (s); ! 311: SDL_DestroySemaphore (s->sem); ! 312: SDL_DestroyMutex (s->mutex); ! 313: SDL_QuitSubSystem (SDL_INIT_AUDIO); ! 314: } ! 315: ! 316: struct pcm_ops sdl_pcm_ops = { ! 317: sdl_hw_init, ! 318: sdl_hw_fini, ! 319: sdl_hw_run, ! 320: sdl_hw_write, ! 321: sdl_hw_ctl ! 322: }; ! 323: ! 324: struct audio_output_driver sdl_output_driver = { ! 325: "sdl", ! 326: sdl_audio_init, ! 327: sdl_audio_fini, ! 328: &sdl_pcm_ops, ! 329: 1, ! 330: 1, ! 331: sizeof (SDLVoice) ! 332: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.