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