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