master
c 500 lines 13.7 KB
Raw
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
25 #include "qemu/osdep.h"
26 #include <SDL.h>
27 #include <SDL_thread.h>
28 #include "qemu/module.h"
29 #include "qemu/error-report.h"
30 #include "qapi/error.h"
31 #include "qemu/audio.h"
32 #include "qom/object.h"
33
34 #ifndef _WIN32
35 #ifdef __sun__
36 #define _POSIX_PTHREAD_SEMANTICS 1
37 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
38 #include <pthread.h>
39 #endif
40 #endif
41
42 #include "audio_int.h"
43
44 #define TYPE_AUDIO_SDL "audio-sdl"
45 OBJECT_DECLARE_SIMPLE_TYPE(AudioSdl, AUDIO_SDL)
46
47 static AudioBackendClass *audio_sdl_parent_class;
48
49 struct AudioSdl {
50 AudioMixengBackend parent_obj;
51 };
52
53
54 typedef struct SDLVoiceOut {
55 HWVoiceOut hw;
56 int exit;
57 int initialized;
58 Audiodev *dev;
59 SDL_AudioDeviceID devid;
60 } SDLVoiceOut;
61
62 typedef struct SDLVoiceIn {
63 HWVoiceIn hw;
64 int exit;
65 int initialized;
66 Audiodev *dev;
67 SDL_AudioDeviceID devid;
68 } SDLVoiceIn;
69
70 static int aud_to_sdlfmt (AudioFormat fmt)
71 {
72 switch (fmt) {
73 case AUDIO_FORMAT_S8:
74 return AUDIO_S8;
75
76 case AUDIO_FORMAT_U8:
77 return AUDIO_U8;
78
79 case AUDIO_FORMAT_S16:
80 return AUDIO_S16LSB;
81
82 case AUDIO_FORMAT_U16:
83 return AUDIO_U16LSB;
84
85 case AUDIO_FORMAT_S32:
86 return AUDIO_S32LSB;
87
88 /* no unsigned 32-bit support in SDL */
89
90 case AUDIO_FORMAT_F32:
91 return AUDIO_F32LSB;
92
93 default:
94 error_report("sdl: internal logic error: bad audio format %d", fmt);
95 return AUDIO_U8;
96 }
97 }
98
99 static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, bool *big_endian)
100 {
101 switch (sdlfmt) {
102 case AUDIO_S8:
103 *big_endian = false;
104 *fmt = AUDIO_FORMAT_S8;
105 break;
106
107 case AUDIO_U8:
108 *big_endian = false;
109 *fmt = AUDIO_FORMAT_U8;
110 break;
111
112 case AUDIO_S16LSB:
113 *big_endian = false;
114 *fmt = AUDIO_FORMAT_S16;
115 break;
116
117 case AUDIO_U16LSB:
118 *big_endian = false;
119 *fmt = AUDIO_FORMAT_U16;
120 break;
121
122 case AUDIO_S16MSB:
123 *big_endian = true;
124 *fmt = AUDIO_FORMAT_S16;
125 break;
126
127 case AUDIO_U16MSB:
128 *big_endian = true;
129 *fmt = AUDIO_FORMAT_U16;
130 break;
131
132 case AUDIO_S32LSB:
133 *big_endian = false;
134 *fmt = AUDIO_FORMAT_S32;
135 break;
136
137 case AUDIO_S32MSB:
138 *big_endian = true;
139 *fmt = AUDIO_FORMAT_S32;
140 break;
141
142 case AUDIO_F32LSB:
143 *big_endian = false;
144 *fmt = AUDIO_FORMAT_F32;
145 break;
146
147 case AUDIO_F32MSB:
148 *big_endian = true;
149 *fmt = AUDIO_FORMAT_F32;
150 break;
151
152 default:
153 error_report("sdl: unrecognized audio format %d", sdlfmt);
154 return -1;
155 }
156
157 return 0;
158 }
159
160 static SDL_AudioDeviceID sdl_open(SDL_AudioSpec *req, SDL_AudioSpec *obt,
161 int rec)
162 {
163 SDL_AudioDeviceID devid;
164 #ifndef _WIN32
165 int err;
166 sigset_t new, old;
167
168 /* Make sure potential threads created by SDL don't hog signals. */
169 err = sigfillset (&new);
170 if (err) {
171 error_report("sdl: sigfillset failed: %s", strerror (errno));
172 return 0;
173 }
174 err = pthread_sigmask (SIG_BLOCK, &new, &old);
175 if (err) {
176 error_report("sdl: pthread_sigmask failed: %s", strerror (err));
177 return 0;
178 }
179 #endif
180
181 devid = SDL_OpenAudioDevice(NULL, rec, req, obt, 0);
182 if (!devid) {
183 error_report("SDL_OpenAudioDevice for %s failed: %s",
184 rec ? "recording" : "playback", SDL_GetError());
185 }
186
187 #ifndef _WIN32
188 err = pthread_sigmask (SIG_SETMASK, &old, NULL);
189 if (err) {
190 error_report("sdl: pthread_sigmask (restore) failed: %s",
191 strerror (errno));
192 /* We have failed to restore original signal mask, all bets are off,
193 so exit the process */
194 exit (EXIT_FAILURE);
195 }
196 #endif
197 return devid;
198 }
199
200 static void sdl_close_out(SDLVoiceOut *sdl)
201 {
202 if (sdl->initialized) {
203 SDL_LockAudioDevice(sdl->devid);
204 sdl->exit = 1;
205 SDL_UnlockAudioDevice(sdl->devid);
206 SDL_PauseAudioDevice(sdl->devid, 1);
207 sdl->initialized = 0;
208 }
209 if (sdl->devid) {
210 SDL_CloseAudioDevice(sdl->devid);
211 sdl->devid = 0;
212 }
213 }
214
215 static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
216 {
217 SDLVoiceOut *sdl = opaque;
218 HWVoiceOut *hw = &sdl->hw;
219
220 if (!sdl->exit) {
221
222 while (hw->pending_emul && len) {
223 size_t write_len, start;
224
225 start = audio_ring_posb(hw->pos_emul, hw->pending_emul,
226 hw->size_emul);
227 assert(start < hw->size_emul);
228
229 write_len = MIN(MIN(hw->pending_emul, len),
230 hw->size_emul - start);
231
232 memcpy(buf, hw->buf_emul + start, write_len);
233 hw->pending_emul -= write_len;
234 len -= write_len;
235 buf += write_len;
236 }
237 }
238
239 /* clear remaining buffer that we couldn't fill with data */
240 if (len) {
241 audio_pcm_info_clear_buf(&hw->info, buf,
242 len / hw->info.bytes_per_frame);
243 }
244 }
245
246 static void sdl_close_in(SDLVoiceIn *sdl)
247 {
248 if (sdl->initialized) {
249 SDL_LockAudioDevice(sdl->devid);
250 sdl->exit = 1;
251 SDL_UnlockAudioDevice(sdl->devid);
252 SDL_PauseAudioDevice(sdl->devid, 1);
253 sdl->initialized = 0;
254 }
255 if (sdl->devid) {
256 SDL_CloseAudioDevice(sdl->devid);
257 sdl->devid = 0;
258 }
259 }
260
261 static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
262 {
263 SDLVoiceIn *sdl = opaque;
264 HWVoiceIn *hw = &sdl->hw;
265
266 if (sdl->exit) {
267 return;
268 }
269
270 while (hw->pending_emul < hw->size_emul && len) {
271 size_t read_len = MIN(len, MIN(hw->size_emul - hw->pos_emul,
272 hw->size_emul - hw->pending_emul));
273
274 memcpy(hw->buf_emul + hw->pos_emul, buf, read_len);
275
276 hw->pending_emul += read_len;
277 hw->pos_emul = (hw->pos_emul + read_len) % hw->size_emul;
278 len -= read_len;
279 buf += read_len;
280 }
281 }
282
283 #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \
284 static ret_type glue(sdl_, name)args_decl \
285 { \
286 ret_type ret; \
287 glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \
288 \
289 SDL_LockAudioDevice(sdl->devid); \
290 ret = glue(audio_generic_, name)args; \
291 SDL_UnlockAudioDevice(sdl->devid); \
292 \
293 return ret; \
294 }
295
296 #define SDL_WRAPPER_VOID_FUNC(name, args_decl, args, dir) \
297 static void glue(sdl_, name)args_decl \
298 { \
299 glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \
300 \
301 SDL_LockAudioDevice(sdl->devid); \
302 glue(audio_generic_, name)args; \
303 SDL_UnlockAudioDevice(sdl->devid); \
304 }
305
306 SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out)
307 SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
308 (hw, size), Out)
309 SDL_WRAPPER_FUNC(put_buffer_out, size_t,
310 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out)
311 SDL_WRAPPER_FUNC(write, size_t,
312 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out)
313 SDL_WRAPPER_FUNC(read, size_t, (HWVoiceIn *hw, void *buf, size_t size),
314 (hw, buf, size), In)
315 SDL_WRAPPER_FUNC(get_buffer_in, void *, (HWVoiceIn *hw, size_t *size),
316 (hw, size), In)
317 SDL_WRAPPER_VOID_FUNC(put_buffer_in, (HWVoiceIn *hw, void *buf, size_t size),
318 (hw, buf, size), In)
319 #undef SDL_WRAPPER_FUNC
320 #undef SDL_WRAPPER_VOID_FUNC
321
322 static void sdl_fini_out(HWVoiceOut *hw)
323 {
324 SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
325
326 sdl_close_out(sdl);
327 }
328
329 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as)
330 {
331 SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
332 SDL_AudioSpec req, obt;
333 int err;
334 Audiodev *dev = hw->s->dev;
335 AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out;
336 struct audsettings obt_as;
337
338 req.freq = as->freq;
339 req.format = aud_to_sdlfmt (as->fmt);
340 req.channels = as->nchannels;
341 /* SDL samples are QEMU frames */
342 req.samples = audio_buffer_frames(
343 qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
344 req.callback = sdl_callback_out;
345 req.userdata = sdl;
346
347 sdl->dev = dev;
348 sdl->devid = sdl_open(&req, &obt, 0);
349 if (!sdl->devid) {
350 return -1;
351 }
352
353 err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.big_endian);
354 if (err) {
355 sdl_close_out(sdl);
356 return -1;
357 }
358
359 obt_as.freq = obt.freq;
360 obt_as.nchannels = obt.channels;
361
362 audio_pcm_init_info (&hw->info, &obt_as);
363 hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
364 obt.samples;
365
366 sdl->initialized = 1;
367 sdl->exit = 0;
368 return 0;
369 }
370
371 static void sdl_enable_out(HWVoiceOut *hw, bool enable)
372 {
373 SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
374
375 SDL_PauseAudioDevice(sdl->devid, !enable);
376 }
377
378 static void sdl_fini_in(HWVoiceIn *hw)
379 {
380 SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
381
382 sdl_close_in(sdl);
383 }
384
385 static int sdl_init_in(HWVoiceIn *hw, audsettings *as)
386 {
387 SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
388 SDL_AudioSpec req, obt;
389 int err;
390 Audiodev *dev = hw->s->dev;
391 AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in;
392 struct audsettings obt_as;
393
394 req.freq = as->freq;
395 req.format = aud_to_sdlfmt(as->fmt);
396 req.channels = as->nchannels;
397 /* SDL samples are QEMU frames */
398 req.samples = audio_buffer_frames(
399 qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
400 req.callback = sdl_callback_in;
401 req.userdata = sdl;
402
403 sdl->dev = dev;
404 sdl->devid = sdl_open(&req, &obt, 1);
405 if (!sdl->devid) {
406 return -1;
407 }
408
409 err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.big_endian);
410 if (err) {
411 sdl_close_in(sdl);
412 return -1;
413 }
414
415 obt_as.freq = obt.freq;
416 obt_as.nchannels = obt.channels;
417
418 audio_pcm_init_info(&hw->info, &obt_as);
419 hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
420 obt.samples;
421 hw->size_emul = hw->samples * hw->info.bytes_per_frame;
422 hw->buf_emul = g_malloc(hw->size_emul);
423 hw->pos_emul = hw->pending_emul = 0;
424
425 sdl->initialized = 1;
426 sdl->exit = 0;
427 return 0;
428 }
429
430 static void sdl_enable_in(HWVoiceIn *hw, bool enable)
431 {
432 SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
433
434 SDL_PauseAudioDevice(sdl->devid, !enable);
435 }
436
437 static bool audio_sdl_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
438 {
439 if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
440 error_setg(errp, "SDL failed to initialize audio subsystem");
441 qapi_free_Audiodev(dev);
442 return false;
443 }
444
445 return audio_sdl_parent_class->realize(abe, dev, errp);
446 }
447
448 static void audio_sdl_finalize(Object *obj)
449 {
450 SDL_QuitSubSystem(SDL_INIT_AUDIO);
451 }
452
453 static void audio_sdl_class_init(ObjectClass *klass, const void *data)
454 {
455 AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
456 AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
457
458 audio_sdl_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
459
460 b->realize = audio_sdl_realize;
461 k->max_voices_out = INT_MAX;
462 k->max_voices_in = INT_MAX;
463 k->voice_size_out = sizeof(SDLVoiceOut);
464 k->voice_size_in = sizeof(SDLVoiceIn);
465
466 k->init_out = sdl_init_out;
467 k->fini_out = sdl_fini_out;
468 /* wrapper for audio_generic_write */
469 k->write = sdl_write;
470 /* wrapper for audio_generic_buffer_get_free */
471 k->buffer_get_free = sdl_buffer_get_free;
472 /* wrapper for audio_generic_get_buffer_out */
473 k->get_buffer_out = sdl_get_buffer_out;
474 /* wrapper for audio_generic_put_buffer_out */
475 k->put_buffer_out = sdl_put_buffer_out;
476 k->enable_out = sdl_enable_out;
477
478 k->init_in = sdl_init_in;
479 k->fini_in = sdl_fini_in;
480 /* wrapper for audio_generic_read */
481 k->read = sdl_read;
482 /* wrapper for audio_generic_get_buffer_in */
483 k->get_buffer_in = sdl_get_buffer_in;
484 /* wrapper for audio_generic_put_buffer_in */
485 k->put_buffer_in = sdl_put_buffer_in;
486 k->enable_in = sdl_enable_in;
487 }
488
489 static const TypeInfo audio_types[] = {
490 {
491 .name = TYPE_AUDIO_SDL,
492 .parent = TYPE_AUDIO_MIXENG_BACKEND,
493 .instance_size = sizeof(AudioSdl),
494 .class_init = audio_sdl_class_init,
495 .instance_finalize = audio_sdl_finalize,
496 },
497 };
498
499 DEFINE_TYPES(audio_types)
500 module_obj(TYPE_AUDIO_SDL);