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