|
|
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 = {
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: {"SAMPLES", AUD_OPT_INT, &conf.samples,
555: "buffer size in samples", NULL, 0},
556:
557: {"DIVISOR", AUD_OPT_INT, &conf.divisor,
558: "threshold divisor", NULL, 0},
559:
560: {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
561: "playback host", NULL, 0},
562:
563: {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
564: "capture host", NULL, 0},
565:
566: {NULL, 0, NULL, NULL, NULL, 0}
567: };
568:
569: static struct audio_pcm_ops qesd_pcm_ops = {
570: qesd_init_out,
571: qesd_fini_out,
572: qesd_run_out,
573: qesd_write,
574: qesd_ctl_out,
575:
576: qesd_init_in,
577: qesd_fini_in,
578: qesd_run_in,
579: qesd_read,
580: qesd_ctl_in,
581: };
582:
583: struct audio_driver esd_audio_driver = {
584: INIT_FIELD (name = ) "esd",
585: INIT_FIELD (descr = )
586: "http://en.wikipedia.org/wiki/Esound",
587: INIT_FIELD (options = ) qesd_options,
588: INIT_FIELD (init = ) qesd_audio_init,
589: INIT_FIELD (fini = ) qesd_audio_fini,
590: INIT_FIELD (pcm_ops = ) &qesd_pcm_ops,
591: INIT_FIELD (can_be_default = ) 0,
592: INIT_FIELD (max_voices_out = ) INT_MAX,
593: INIT_FIELD (max_voices_in = ) INT_MAX,
594: INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
595: INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn)
596: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.