|
|
1.1 root 1: /* public domain */
2: #include "qemu-common.h"
3: #include "audio.h"
4:
5: #include <pulse/simple.h>
6: #include <pulse/error.h>
7:
8: #define AUDIO_CAP "pulseaudio"
9: #include "audio_int.h"
10: #include "audio_pt_int.h"
11:
12: typedef struct {
13: HWVoiceOut hw;
14: int done;
15: int live;
16: int decr;
17: int rpos;
18: pa_simple *s;
19: void *pcm_buf;
20: struct audio_pt pt;
21: } PAVoiceOut;
22:
23: typedef struct {
24: HWVoiceIn hw;
25: int done;
26: int dead;
27: int incr;
28: int wpos;
29: pa_simple *s;
30: void *pcm_buf;
31: struct audio_pt pt;
32: } PAVoiceIn;
33:
34: static struct {
35: int samples;
36: char *server;
37: char *sink;
38: char *source;
39: } conf = {
1.1.1.4 ! root 40: .samples = 4096,
1.1 root 41: };
42:
43: static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
44: {
45: va_list ap;
46:
47: va_start (ap, fmt);
48: AUD_vlog (AUDIO_CAP, fmt, ap);
49: va_end (ap);
50:
51: AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
52: }
53:
54: static void *qpa_thread_out (void *arg)
55: {
56: PAVoiceOut *pa = arg;
57: HWVoiceOut *hw = &pa->hw;
58:
59: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
60: return NULL;
61: }
62:
63: for (;;) {
64: int decr, to_mix, rpos;
65:
66: for (;;) {
67: if (pa->done) {
68: goto exit;
69: }
70:
1.1.1.4 ! root 71: if (pa->live > 0) {
1.1 root 72: break;
73: }
74:
75: if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
76: goto exit;
77: }
78: }
79:
1.1.1.4 ! root 80: decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
! 81: rpos = pa->rpos;
1.1 root 82:
83: if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
84: return NULL;
85: }
86:
87: while (to_mix) {
88: int error;
89: int chunk = audio_MIN (to_mix, hw->samples - rpos);
90: struct st_sample *src = hw->mix_buf + rpos;
91:
92: hw->clip (pa->pcm_buf, src, chunk);
93:
94: if (pa_simple_write (pa->s, pa->pcm_buf,
95: chunk << hw->info.shift, &error) < 0) {
96: qpa_logerr (error, "pa_simple_write failed\n");
97: return NULL;
98: }
99:
100: rpos = (rpos + chunk) % hw->samples;
101: to_mix -= chunk;
102: }
103:
104: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
105: return NULL;
106: }
107:
108: pa->rpos = rpos;
109: pa->live -= decr;
110: pa->decr += decr;
111: }
112:
113: exit:
114: audio_pt_unlock (&pa->pt, AUDIO_FUNC);
115: return NULL;
116: }
117:
1.1.1.3 root 118: static int qpa_run_out (HWVoiceOut *hw, int live)
1.1 root 119: {
1.1.1.3 root 120: int decr;
1.1 root 121: PAVoiceOut *pa = (PAVoiceOut *) hw;
122:
123: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
124: return 0;
125: }
126:
127: decr = audio_MIN (live, pa->decr);
128: pa->decr -= decr;
129: pa->live = live - decr;
130: hw->rpos = pa->rpos;
131: if (pa->live > 0) {
132: audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
133: }
134: else {
135: audio_pt_unlock (&pa->pt, AUDIO_FUNC);
136: }
137: return decr;
138: }
139:
140: static int qpa_write (SWVoiceOut *sw, void *buf, int len)
141: {
142: return audio_pcm_sw_write (sw, buf, len);
143: }
144:
145: /* capture */
146: static void *qpa_thread_in (void *arg)
147: {
148: PAVoiceIn *pa = arg;
149: HWVoiceIn *hw = &pa->hw;
150:
151: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
152: return NULL;
153: }
154:
155: for (;;) {
156: int incr, to_grab, wpos;
157:
158: for (;;) {
159: if (pa->done) {
160: goto exit;
161: }
162:
1.1.1.4 ! root 163: if (pa->dead > 0) {
1.1 root 164: break;
165: }
166:
167: if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
168: goto exit;
169: }
170: }
171:
1.1.1.4 ! root 172: incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
! 173: wpos = pa->wpos;
1.1 root 174:
175: if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
176: return NULL;
177: }
178:
179: while (to_grab) {
180: int error;
181: int chunk = audio_MIN (to_grab, hw->samples - wpos);
182: void *buf = advance (pa->pcm_buf, wpos);
183:
184: if (pa_simple_read (pa->s, buf,
185: chunk << hw->info.shift, &error) < 0) {
186: qpa_logerr (error, "pa_simple_read failed\n");
187: return NULL;
188: }
189:
1.1.1.4 ! root 190: hw->conv (hw->conv_buf + wpos, buf, chunk);
1.1 root 191: wpos = (wpos + chunk) % hw->samples;
192: to_grab -= chunk;
193: }
194:
195: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
196: return NULL;
197: }
198:
199: pa->wpos = wpos;
200: pa->dead -= incr;
201: pa->incr += incr;
202: }
203:
204: exit:
205: audio_pt_unlock (&pa->pt, AUDIO_FUNC);
206: return NULL;
207: }
208:
209: static int qpa_run_in (HWVoiceIn *hw)
210: {
211: int live, incr, dead;
212: PAVoiceIn *pa = (PAVoiceIn *) hw;
213:
214: if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
215: return 0;
216: }
217:
218: live = audio_pcm_hw_get_live_in (hw);
219: dead = hw->samples - live;
220: incr = audio_MIN (dead, pa->incr);
221: pa->incr -= incr;
222: pa->dead = dead - incr;
223: hw->wpos = pa->wpos;
224: if (pa->dead > 0) {
225: audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
226: }
227: else {
228: audio_pt_unlock (&pa->pt, AUDIO_FUNC);
229: }
230: return incr;
231: }
232:
233: static int qpa_read (SWVoiceIn *sw, void *buf, int len)
234: {
235: return audio_pcm_sw_read (sw, buf, len);
236: }
237:
238: static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
239: {
240: int format;
241:
242: switch (afmt) {
243: case AUD_FMT_S8:
244: case AUD_FMT_U8:
245: format = PA_SAMPLE_U8;
246: break;
247: case AUD_FMT_S16:
248: case AUD_FMT_U16:
249: format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
250: break;
251: case AUD_FMT_S32:
252: case AUD_FMT_U32:
253: format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
254: break;
255: default:
256: dolog ("Internal logic error: Bad audio format %d\n", afmt);
257: format = PA_SAMPLE_U8;
258: break;
259: }
260: return format;
261: }
262:
263: static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
264: {
265: switch (fmt) {
266: case PA_SAMPLE_U8:
267: return AUD_FMT_U8;
268: case PA_SAMPLE_S16BE:
269: *endianness = 1;
270: return AUD_FMT_S16;
271: case PA_SAMPLE_S16LE:
272: *endianness = 0;
273: return AUD_FMT_S16;
274: case PA_SAMPLE_S32BE:
275: *endianness = 1;
276: return AUD_FMT_S32;
277: case PA_SAMPLE_S32LE:
278: *endianness = 0;
279: return AUD_FMT_S32;
280: default:
281: dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
282: return AUD_FMT_U8;
283: }
284: }
285:
286: static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
287: {
288: int error;
289: static pa_sample_spec ss;
1.1.1.4 ! root 290: static pa_buffer_attr ba;
1.1 root 291: struct audsettings obt_as = *as;
292: PAVoiceOut *pa = (PAVoiceOut *) hw;
293:
294: ss.format = audfmt_to_pa (as->fmt, as->endianness);
295: ss.channels = as->nchannels;
296: ss.rate = as->freq;
297:
1.1.1.4 ! root 298: /*
! 299: * qemu audio tick runs at 250 Hz (by default), so processing
! 300: * data chunks worth 4 ms of sound should be a good fit.
! 301: */
! 302: ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
! 303: ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
! 304: ba.maxlength = -1;
! 305: ba.prebuf = -1;
! 306:
1.1 root 307: obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
308:
309: pa->s = pa_simple_new (
310: conf.server,
311: "qemu",
312: PA_STREAM_PLAYBACK,
313: conf.sink,
314: "pcm.playback",
315: &ss,
316: NULL, /* channel map */
1.1.1.4 ! root 317: &ba, /* buffering attributes */
1.1 root 318: &error
319: );
320: if (!pa->s) {
321: qpa_logerr (error, "pa_simple_new for playback failed\n");
322: goto fail1;
323: }
324:
325: audio_pcm_init_info (&hw->info, &obt_as);
326: hw->samples = conf.samples;
327: pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 ! root 328: pa->rpos = hw->rpos;
1.1 root 329: if (!pa->pcm_buf) {
330: dolog ("Could not allocate buffer (%d bytes)\n",
331: hw->samples << hw->info.shift);
332: goto fail2;
333: }
334:
335: if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
336: goto fail3;
337: }
338:
339: return 0;
340:
341: fail3:
1.1.1.2 root 342: qemu_free (pa->pcm_buf);
1.1 root 343: pa->pcm_buf = NULL;
344: fail2:
345: pa_simple_free (pa->s);
346: pa->s = NULL;
347: fail1:
348: return -1;
349: }
350:
351: static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
352: {
353: int error;
354: static pa_sample_spec ss;
355: struct audsettings obt_as = *as;
356: PAVoiceIn *pa = (PAVoiceIn *) hw;
357:
358: ss.format = audfmt_to_pa (as->fmt, as->endianness);
359: ss.channels = as->nchannels;
360: ss.rate = as->freq;
361:
362: obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
363:
364: pa->s = pa_simple_new (
365: conf.server,
366: "qemu",
367: PA_STREAM_RECORD,
368: conf.source,
369: "pcm.capture",
370: &ss,
371: NULL, /* channel map */
372: NULL, /* buffering attributes */
373: &error
374: );
375: if (!pa->s) {
376: qpa_logerr (error, "pa_simple_new for capture failed\n");
377: goto fail1;
378: }
379:
380: audio_pcm_init_info (&hw->info, &obt_as);
381: hw->samples = conf.samples;
382: pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 ! root 383: pa->wpos = hw->wpos;
1.1 root 384: if (!pa->pcm_buf) {
385: dolog ("Could not allocate buffer (%d bytes)\n",
386: hw->samples << hw->info.shift);
387: goto fail2;
388: }
389:
390: if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
391: goto fail3;
392: }
393:
394: return 0;
395:
396: fail3:
1.1.1.2 root 397: qemu_free (pa->pcm_buf);
1.1 root 398: pa->pcm_buf = NULL;
399: fail2:
400: pa_simple_free (pa->s);
401: pa->s = NULL;
402: fail1:
403: return -1;
404: }
405:
406: static void qpa_fini_out (HWVoiceOut *hw)
407: {
408: void *ret;
409: PAVoiceOut *pa = (PAVoiceOut *) hw;
410:
411: audio_pt_lock (&pa->pt, AUDIO_FUNC);
412: pa->done = 1;
413: audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
414: audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
415:
416: if (pa->s) {
417: pa_simple_free (pa->s);
418: pa->s = NULL;
419: }
420:
421: audio_pt_fini (&pa->pt, AUDIO_FUNC);
422: qemu_free (pa->pcm_buf);
423: pa->pcm_buf = NULL;
424: }
425:
426: static void qpa_fini_in (HWVoiceIn *hw)
427: {
428: void *ret;
429: PAVoiceIn *pa = (PAVoiceIn *) hw;
430:
431: audio_pt_lock (&pa->pt, AUDIO_FUNC);
432: pa->done = 1;
433: audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
434: audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
435:
436: if (pa->s) {
437: pa_simple_free (pa->s);
438: pa->s = NULL;
439: }
440:
441: audio_pt_fini (&pa->pt, AUDIO_FUNC);
442: qemu_free (pa->pcm_buf);
443: pa->pcm_buf = NULL;
444: }
445:
446: static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
447: {
448: (void) hw;
449: (void) cmd;
450: return 0;
451: }
452:
453: static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
454: {
455: (void) hw;
456: (void) cmd;
457: return 0;
458: }
459:
460: /* common */
461: static void *qpa_audio_init (void)
462: {
463: return &conf;
464: }
465:
466: static void qpa_audio_fini (void *opaque)
467: {
468: (void) opaque;
469: }
470:
471: struct audio_option qpa_options[] = {
1.1.1.3 root 472: {
473: .name = "SAMPLES",
474: .tag = AUD_OPT_INT,
475: .valp = &conf.samples,
476: .descr = "buffer size in samples"
477: },
478: {
479: .name = "SERVER",
480: .tag = AUD_OPT_STR,
481: .valp = &conf.server,
482: .descr = "server address"
483: },
484: {
485: .name = "SINK",
486: .tag = AUD_OPT_STR,
487: .valp = &conf.sink,
488: .descr = "sink device name"
489: },
490: {
491: .name = "SOURCE",
492: .tag = AUD_OPT_STR,
493: .valp = &conf.source,
494: .descr = "source device name"
495: },
496: { /* End of list */ }
1.1 root 497: };
498:
499: static struct audio_pcm_ops qpa_pcm_ops = {
1.1.1.3 root 500: .init_out = qpa_init_out,
501: .fini_out = qpa_fini_out,
502: .run_out = qpa_run_out,
503: .write = qpa_write,
504: .ctl_out = qpa_ctl_out,
505:
506: .init_in = qpa_init_in,
507: .fini_in = qpa_fini_in,
508: .run_in = qpa_run_in,
509: .read = qpa_read,
510: .ctl_in = qpa_ctl_in
1.1 root 511: };
512:
513: struct audio_driver pa_audio_driver = {
1.1.1.3 root 514: .name = "pa",
515: .descr = "http://www.pulseaudio.org/",
516: .options = qpa_options,
517: .init = qpa_audio_init,
518: .fini = qpa_audio_fini,
519: .pcm_ops = &qpa_pcm_ops,
520: .can_be_default = 1,
521: .max_voices_out = INT_MAX,
522: .max_voices_in = INT_MAX,
523: .voice_size_out = sizeof (PAVoiceOut),
524: .voice_size_in = sizeof (PAVoiceIn)
1.1 root 525: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.