]> git.proxmox.com Git - qemu.git/blob - audio/esdaudio.c
Use C99 initializers for audio_option
[qemu.git] / audio / esdaudio.c
1 /*
2 * QEMU ESD audio driver
3 *
4 * Copyright (c) 2006 Frederick Reeve (brushed up by 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 <esd.h>
25 #include "qemu-common.h"
26 #include "audio.h"
27 #include <signal.h>
28
29 #define AUDIO_CAP "esd"
30 #include "audio_int.h"
31 #include "audio_pt_int.h"
32
33 typedef struct {
34 HWVoiceOut hw;
35 int done;
36 int live;
37 int decr;
38 int rpos;
39 void *pcm_buf;
40 int fd;
41 struct audio_pt pt;
42 } ESDVoiceOut;
43
44 typedef struct {
45 HWVoiceIn hw;
46 int done;
47 int dead;
48 int incr;
49 int wpos;
50 void *pcm_buf;
51 int fd;
52 struct audio_pt pt;
53 } ESDVoiceIn;
54
55 static struct {
56 int samples;
57 int divisor;
58 char *dac_host;
59 char *adc_host;
60 } conf = {
61 1024,
62 2,
63 NULL,
64 NULL
65 };
66
67 static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
68 {
69 va_list ap;
70
71 va_start (ap, fmt);
72 AUD_vlog (AUDIO_CAP, fmt, ap);
73 va_end (ap);
74
75 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
76 }
77
78 /* playback */
79 static void *qesd_thread_out (void *arg)
80 {
81 ESDVoiceOut *esd = arg;
82 HWVoiceOut *hw = &esd->hw;
83 int threshold;
84
85 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
86
87 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
88 return NULL;
89 }
90
91 for (;;) {
92 int decr, to_mix, rpos;
93
94 for (;;) {
95 if (esd->done) {
96 goto exit;
97 }
98
99 if (esd->live > threshold) {
100 break;
101 }
102
103 if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
104 goto exit;
105 }
106 }
107
108 decr = to_mix = esd->live;
109 rpos = hw->rpos;
110
111 if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
112 return NULL;
113 }
114
115 while (to_mix) {
116 ssize_t written;
117 int chunk = audio_MIN (to_mix, hw->samples - rpos);
118 struct st_sample *src = hw->mix_buf + rpos;
119
120 hw->clip (esd->pcm_buf, src, chunk);
121
122 again:
123 written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
124 if (written == -1) {
125 if (errno == EINTR || errno == EAGAIN) {
126 goto again;
127 }
128 qesd_logerr (errno, "write failed\n");
129 return NULL;
130 }
131
132 if (written != chunk << hw->info.shift) {
133 int wsamples = written >> hw->info.shift;
134 int wbytes = wsamples << hw->info.shift;
135 if (wbytes != written) {
136 dolog ("warning: Misaligned write %d (requested %d), "
137 "alignment %d\n",
138 wbytes, written, hw->info.align + 1);
139 }
140 to_mix -= wsamples;
141 rpos = (rpos + wsamples) % hw->samples;
142 break;
143 }
144
145 rpos = (rpos + chunk) % hw->samples;
146 to_mix -= chunk;
147 }
148
149 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
150 return NULL;
151 }
152
153 esd->rpos = rpos;
154 esd->live -= decr;
155 esd->decr += decr;
156 }
157
158 exit:
159 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
160 return NULL;
161 }
162
163 static int qesd_run_out (HWVoiceOut *hw)
164 {
165 int live, decr;
166 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
167
168 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
169 return 0;
170 }
171
172 live = audio_pcm_hw_get_live_out (hw);
173 decr = audio_MIN (live, esd->decr);
174 esd->decr -= decr;
175 esd->live = live - decr;
176 hw->rpos = esd->rpos;
177 if (esd->live > 0) {
178 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
179 }
180 else {
181 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
182 }
183 return decr;
184 }
185
186 static int qesd_write (SWVoiceOut *sw, void *buf, int len)
187 {
188 return audio_pcm_sw_write (sw, buf, len);
189 }
190
191 static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
192 {
193 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
194 struct audsettings obt_as = *as;
195 int esdfmt = ESD_STREAM | ESD_PLAY;
196 int err;
197 sigset_t set, old_set;
198
199 sigfillset (&set);
200
201 esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
202 switch (as->fmt) {
203 case AUD_FMT_S8:
204 case AUD_FMT_U8:
205 esdfmt |= ESD_BITS8;
206 obt_as.fmt = AUD_FMT_U8;
207 break;
208
209 case AUD_FMT_S32:
210 case AUD_FMT_U32:
211 dolog ("Will use 16 instead of 32 bit samples\n");
212
213 case AUD_FMT_S16:
214 case AUD_FMT_U16:
215 deffmt:
216 esdfmt |= ESD_BITS16;
217 obt_as.fmt = AUD_FMT_S16;
218 break;
219
220 default:
221 dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
222 goto deffmt;
223
224 }
225 obt_as.endianness = AUDIO_HOST_ENDIANNESS;
226
227 audio_pcm_init_info (&hw->info, &obt_as);
228
229 hw->samples = conf.samples;
230 esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
231 if (!esd->pcm_buf) {
232 dolog ("Could not allocate buffer (%d bytes)\n",
233 hw->samples << hw->info.shift);
234 return -1;
235 }
236
237 esd->fd = -1;
238 err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
239 if (err) {
240 qesd_logerr (err, "pthread_sigmask failed\n");
241 goto fail1;
242 }
243
244 esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
245 if (esd->fd < 0) {
246 qesd_logerr (errno, "esd_play_stream failed\n");
247 goto fail2;
248 }
249
250 if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
251 goto fail3;
252 }
253
254 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
255 if (err) {
256 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
257 }
258
259 return 0;
260
261 fail3:
262 if (close (esd->fd)) {
263 qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
264 AUDIO_FUNC, esd->fd);
265 }
266 esd->fd = -1;
267
268 fail2:
269 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
270 if (err) {
271 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
272 }
273
274 fail1:
275 qemu_free (esd->pcm_buf);
276 esd->pcm_buf = NULL;
277 return -1;
278 }
279
280 static void qesd_fini_out (HWVoiceOut *hw)
281 {
282 void *ret;
283 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
284
285 audio_pt_lock (&esd->pt, AUDIO_FUNC);
286 esd->done = 1;
287 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
288 audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
289
290 if (esd->fd >= 0) {
291 if (close (esd->fd)) {
292 qesd_logerr (errno, "failed to close esd socket\n");
293 }
294 esd->fd = -1;
295 }
296
297 audio_pt_fini (&esd->pt, AUDIO_FUNC);
298
299 qemu_free (esd->pcm_buf);
300 esd->pcm_buf = NULL;
301 }
302
303 static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
304 {
305 (void) hw;
306 (void) cmd;
307 return 0;
308 }
309
310 /* capture */
311 static void *qesd_thread_in (void *arg)
312 {
313 ESDVoiceIn *esd = arg;
314 HWVoiceIn *hw = &esd->hw;
315 int threshold;
316
317 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
318
319 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
320 return NULL;
321 }
322
323 for (;;) {
324 int incr, to_grab, wpos;
325
326 for (;;) {
327 if (esd->done) {
328 goto exit;
329 }
330
331 if (esd->dead > threshold) {
332 break;
333 }
334
335 if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
336 goto exit;
337 }
338 }
339
340 incr = to_grab = esd->dead;
341 wpos = hw->wpos;
342
343 if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
344 return NULL;
345 }
346
347 while (to_grab) {
348 ssize_t nread;
349 int chunk = audio_MIN (to_grab, hw->samples - wpos);
350 void *buf = advance (esd->pcm_buf, wpos);
351
352 again:
353 nread = read (esd->fd, buf, chunk << hw->info.shift);
354 if (nread == -1) {
355 if (errno == EINTR || errno == EAGAIN) {
356 goto again;
357 }
358 qesd_logerr (errno, "read failed\n");
359 return NULL;
360 }
361
362 if (nread != chunk << hw->info.shift) {
363 int rsamples = nread >> hw->info.shift;
364 int rbytes = rsamples << hw->info.shift;
365 if (rbytes != nread) {
366 dolog ("warning: Misaligned write %d (requested %d), "
367 "alignment %d\n",
368 rbytes, nread, hw->info.align + 1);
369 }
370 to_grab -= rsamples;
371 wpos = (wpos + rsamples) % hw->samples;
372 break;
373 }
374
375 hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
376 &nominal_volume);
377 wpos = (wpos + chunk) % hw->samples;
378 to_grab -= chunk;
379 }
380
381 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
382 return NULL;
383 }
384
385 esd->wpos = wpos;
386 esd->dead -= incr;
387 esd->incr += incr;
388 }
389
390 exit:
391 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
392 return NULL;
393 }
394
395 static int qesd_run_in (HWVoiceIn *hw)
396 {
397 int live, incr, dead;
398 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
399
400 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
401 return 0;
402 }
403
404 live = audio_pcm_hw_get_live_in (hw);
405 dead = hw->samples - live;
406 incr = audio_MIN (dead, esd->incr);
407 esd->incr -= incr;
408 esd->dead = dead - incr;
409 hw->wpos = esd->wpos;
410 if (esd->dead > 0) {
411 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
412 }
413 else {
414 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
415 }
416 return incr;
417 }
418
419 static int qesd_read (SWVoiceIn *sw, void *buf, int len)
420 {
421 return audio_pcm_sw_read (sw, buf, len);
422 }
423
424 static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
425 {
426 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
427 struct audsettings obt_as = *as;
428 int esdfmt = ESD_STREAM | ESD_RECORD;
429 int err;
430 sigset_t set, old_set;
431
432 sigfillset (&set);
433
434 esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
435 switch (as->fmt) {
436 case AUD_FMT_S8:
437 case AUD_FMT_U8:
438 esdfmt |= ESD_BITS8;
439 obt_as.fmt = AUD_FMT_U8;
440 break;
441
442 case AUD_FMT_S16:
443 case AUD_FMT_U16:
444 esdfmt |= ESD_BITS16;
445 obt_as.fmt = AUD_FMT_S16;
446 break;
447
448 case AUD_FMT_S32:
449 case AUD_FMT_U32:
450 dolog ("Will use 16 instead of 32 bit samples\n");
451 esdfmt |= ESD_BITS16;
452 obt_as.fmt = AUD_FMT_S16;
453 break;
454 }
455 obt_as.endianness = AUDIO_HOST_ENDIANNESS;
456
457 audio_pcm_init_info (&hw->info, &obt_as);
458
459 hw->samples = conf.samples;
460 esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
461 if (!esd->pcm_buf) {
462 dolog ("Could not allocate buffer (%d bytes)\n",
463 hw->samples << hw->info.shift);
464 return -1;
465 }
466
467 esd->fd = -1;
468
469 err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
470 if (err) {
471 qesd_logerr (err, "pthread_sigmask failed\n");
472 goto fail1;
473 }
474
475 esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
476 if (esd->fd < 0) {
477 qesd_logerr (errno, "esd_record_stream failed\n");
478 goto fail2;
479 }
480
481 if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
482 goto fail3;
483 }
484
485 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
486 if (err) {
487 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
488 }
489
490 return 0;
491
492 fail3:
493 if (close (esd->fd)) {
494 qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
495 AUDIO_FUNC, esd->fd);
496 }
497 esd->fd = -1;
498
499 fail2:
500 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
501 if (err) {
502 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
503 }
504
505 fail1:
506 qemu_free (esd->pcm_buf);
507 esd->pcm_buf = NULL;
508 return -1;
509 }
510
511 static void qesd_fini_in (HWVoiceIn *hw)
512 {
513 void *ret;
514 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
515
516 audio_pt_lock (&esd->pt, AUDIO_FUNC);
517 esd->done = 1;
518 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
519 audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
520
521 if (esd->fd >= 0) {
522 if (close (esd->fd)) {
523 qesd_logerr (errno, "failed to close esd socket\n");
524 }
525 esd->fd = -1;
526 }
527
528 audio_pt_fini (&esd->pt, AUDIO_FUNC);
529
530 qemu_free (esd->pcm_buf);
531 esd->pcm_buf = NULL;
532 }
533
534 static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
535 {
536 (void) hw;
537 (void) cmd;
538 return 0;
539 }
540
541 /* common */
542 static void *qesd_audio_init (void)
543 {
544 return &conf;
545 }
546
547 static void qesd_audio_fini (void *opaque)
548 {
549 (void) opaque;
550 ldebug ("esd_fini");
551 }
552
553 struct audio_option qesd_options[] = {
554 {.name = "SAMPLES",
555 .tag = AUD_OPT_INT,
556 .valp = &conf.samples,
557 .descr = "buffer size in samples"},
558 {.name = "DIVISOR",
559 .tag = AUD_OPT_INT,
560 .valp = &conf.divisor,
561 .descr = "threshold divisor"},
562 {.name = "DAC_HOST",
563 .tag = AUD_OPT_STR,
564 .valp = &conf.dac_host,
565 .descr = "playback host"},
566 {.name = "ADC_HOST",
567 .tag = AUD_OPT_STR,
568 .valp = &conf.adc_host,
569 .descr = "capture host"},
570 { /* End of list */ }
571 };
572
573 static struct audio_pcm_ops qesd_pcm_ops = {
574 qesd_init_out,
575 qesd_fini_out,
576 qesd_run_out,
577 qesd_write,
578 qesd_ctl_out,
579
580 qesd_init_in,
581 qesd_fini_in,
582 qesd_run_in,
583 qesd_read,
584 qesd_ctl_in,
585 };
586
587 struct audio_driver esd_audio_driver = {
588 .name = "esd",
589 .descr = "http://en.wikipedia.org/wiki/Esound",
590 .options = qesd_options,
591 .init = qesd_audio_init,
592 .fini = qesd_audio_fini,
593 .pcm_ops = &qesd_pcm_ops,
594 .can_be_default = 0,
595 .max_voices_out = INT_MAX,
596 .max_voices_in = INT_MAX,
597 .voice_size_out = sizeof (ESDVoiceOut),
598 .voice_size_in = sizeof (ESDVoiceIn)
599 };