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