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