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