root / audio / paaudio.c @ eedfac6f
History | View | Annotate | Download (23.6 kB)
1 |
/* public domain */
|
---|---|
2 |
#include "qemu-common.h" |
3 |
#include "audio.h" |
4 |
|
5 |
#include <pulse/pulseaudio.h> |
6 |
|
7 |
#define AUDIO_CAP "pulseaudio" |
8 |
#include "audio_int.h" |
9 |
#include "audio_pt_int.h" |
10 |
|
11 |
typedef struct { |
12 |
HWVoiceOut hw; |
13 |
int done;
|
14 |
int live;
|
15 |
int decr;
|
16 |
int rpos;
|
17 |
pa_stream *stream; |
18 |
void *pcm_buf;
|
19 |
struct audio_pt pt;
|
20 |
} PAVoiceOut; |
21 |
|
22 |
typedef struct { |
23 |
HWVoiceIn hw; |
24 |
int done;
|
25 |
int dead;
|
26 |
int incr;
|
27 |
int wpos;
|
28 |
pa_stream *stream; |
29 |
void *pcm_buf;
|
30 |
struct audio_pt pt;
|
31 |
const void *read_data; |
32 |
size_t read_index, read_length; |
33 |
} PAVoiceIn; |
34 |
|
35 |
typedef struct { |
36 |
int samples;
|
37 |
char *server;
|
38 |
char *sink;
|
39 |
char *source;
|
40 |
pa_threaded_mainloop *mainloop; |
41 |
pa_context *context; |
42 |
} paaudio; |
43 |
|
44 |
static paaudio glob_paaudio = {
|
45 |
.samples = 4096,
|
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 |
#ifndef PA_CONTEXT_IS_GOOD
|
60 |
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) |
61 |
{ |
62 |
return
|
63 |
x == PA_CONTEXT_CONNECTING || |
64 |
x == PA_CONTEXT_AUTHORIZING || |
65 |
x == PA_CONTEXT_SETTING_NAME || |
66 |
x == PA_CONTEXT_READY; |
67 |
} |
68 |
#endif
|
69 |
|
70 |
#ifndef PA_STREAM_IS_GOOD
|
71 |
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) |
72 |
{ |
73 |
return
|
74 |
x == PA_STREAM_CREATING || |
75 |
x == PA_STREAM_READY; |
76 |
} |
77 |
#endif
|
78 |
|
79 |
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
|
80 |
do { \
|
81 |
if (!(expression)) { \
|
82 |
if (rerror) { \
|
83 |
*(rerror) = pa_context_errno ((c)->context); \ |
84 |
} \ |
85 |
goto label; \
|
86 |
} \ |
87 |
} while (0); |
88 |
|
89 |
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
|
90 |
do { \
|
91 |
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
|
92 |
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ |
93 |
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
|
94 |
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ |
95 |
if (rerror) { \
|
96 |
*(rerror) = pa_context_errno ((c)->context); \ |
97 |
} \ |
98 |
} else { \
|
99 |
if (rerror) { \
|
100 |
*(rerror) = PA_ERR_BADSTATE; \ |
101 |
} \ |
102 |
} \ |
103 |
goto label; \
|
104 |
} \ |
105 |
} while (0); |
106 |
|
107 |
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) |
108 |
{ |
109 |
paaudio *g = &glob_paaudio; |
110 |
|
111 |
pa_threaded_mainloop_lock (g->mainloop); |
112 |
|
113 |
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |
114 |
|
115 |
while (length > 0) { |
116 |
size_t l; |
117 |
|
118 |
while (!p->read_data) {
|
119 |
int r;
|
120 |
|
121 |
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); |
122 |
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
|
123 |
|
124 |
if (!p->read_data) {
|
125 |
pa_threaded_mainloop_wait (g->mainloop); |
126 |
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |
127 |
} else {
|
128 |
p->read_index = 0;
|
129 |
} |
130 |
} |
131 |
|
132 |
l = p->read_length < length ? p->read_length : length; |
133 |
memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
|
134 |
|
135 |
data = (uint8_t *) data + l; |
136 |
length -= l; |
137 |
|
138 |
p->read_index += l; |
139 |
p->read_length -= l; |
140 |
|
141 |
if (!p->read_length) {
|
142 |
int r;
|
143 |
|
144 |
r = pa_stream_drop (p->stream); |
145 |
p->read_data = NULL;
|
146 |
p->read_length = 0;
|
147 |
p->read_index = 0;
|
148 |
|
149 |
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
|
150 |
} |
151 |
} |
152 |
|
153 |
pa_threaded_mainloop_unlock (g->mainloop); |
154 |
return 0; |
155 |
|
156 |
unlock_and_fail:
|
157 |
pa_threaded_mainloop_unlock (g->mainloop); |
158 |
return -1; |
159 |
} |
160 |
|
161 |
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) |
162 |
{ |
163 |
paaudio *g = &glob_paaudio; |
164 |
|
165 |
pa_threaded_mainloop_lock (g->mainloop); |
166 |
|
167 |
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |
168 |
|
169 |
while (length > 0) { |
170 |
size_t l; |
171 |
int r;
|
172 |
|
173 |
while (!(l = pa_stream_writable_size (p->stream))) {
|
174 |
pa_threaded_mainloop_wait (g->mainloop); |
175 |
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |
176 |
} |
177 |
|
178 |
CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
|
179 |
|
180 |
if (l > length) {
|
181 |
l = length; |
182 |
} |
183 |
|
184 |
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); |
185 |
CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
|
186 |
|
187 |
data = (const uint8_t *) data + l;
|
188 |
length -= l; |
189 |
} |
190 |
|
191 |
pa_threaded_mainloop_unlock (g->mainloop); |
192 |
return 0; |
193 |
|
194 |
unlock_and_fail:
|
195 |
pa_threaded_mainloop_unlock (g->mainloop); |
196 |
return -1; |
197 |
} |
198 |
|
199 |
static void *qpa_thread_out (void *arg) |
200 |
{ |
201 |
PAVoiceOut *pa = arg; |
202 |
HWVoiceOut *hw = &pa->hw; |
203 |
|
204 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
205 |
return NULL; |
206 |
} |
207 |
|
208 |
for (;;) {
|
209 |
int decr, to_mix, rpos;
|
210 |
|
211 |
for (;;) {
|
212 |
if (pa->done) {
|
213 |
goto exit;
|
214 |
} |
215 |
|
216 |
if (pa->live > 0) { |
217 |
break;
|
218 |
} |
219 |
|
220 |
if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
|
221 |
goto exit;
|
222 |
} |
223 |
} |
224 |
|
225 |
decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
|
226 |
rpos = pa->rpos; |
227 |
|
228 |
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
229 |
return NULL; |
230 |
} |
231 |
|
232 |
while (to_mix) {
|
233 |
int error;
|
234 |
int chunk = audio_MIN (to_mix, hw->samples - rpos);
|
235 |
struct st_sample *src = hw->mix_buf + rpos;
|
236 |
|
237 |
hw->clip (pa->pcm_buf, src, chunk); |
238 |
|
239 |
if (qpa_simple_write (pa, pa->pcm_buf,
|
240 |
chunk << hw->info.shift, &error) < 0) {
|
241 |
qpa_logerr (error, "pa_simple_write failed\n");
|
242 |
return NULL; |
243 |
} |
244 |
|
245 |
rpos = (rpos + chunk) % hw->samples; |
246 |
to_mix -= chunk; |
247 |
} |
248 |
|
249 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
250 |
return NULL; |
251 |
} |
252 |
|
253 |
pa->rpos = rpos; |
254 |
pa->live -= decr; |
255 |
pa->decr += decr; |
256 |
} |
257 |
|
258 |
exit:
|
259 |
audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
260 |
return NULL; |
261 |
} |
262 |
|
263 |
static int qpa_run_out (HWVoiceOut *hw, int live) |
264 |
{ |
265 |
int decr;
|
266 |
PAVoiceOut *pa = (PAVoiceOut *) hw; |
267 |
|
268 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
269 |
return 0; |
270 |
} |
271 |
|
272 |
decr = audio_MIN (live, pa->decr); |
273 |
pa->decr -= decr; |
274 |
pa->live = live - decr; |
275 |
hw->rpos = pa->rpos; |
276 |
if (pa->live > 0) { |
277 |
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
278 |
} |
279 |
else {
|
280 |
audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
281 |
} |
282 |
return decr;
|
283 |
} |
284 |
|
285 |
static int qpa_write (SWVoiceOut *sw, void *buf, int len) |
286 |
{ |
287 |
return audio_pcm_sw_write (sw, buf, len);
|
288 |
} |
289 |
|
290 |
/* capture */
|
291 |
static void *qpa_thread_in (void *arg) |
292 |
{ |
293 |
PAVoiceIn *pa = arg; |
294 |
HWVoiceIn *hw = &pa->hw; |
295 |
|
296 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
297 |
return NULL; |
298 |
} |
299 |
|
300 |
for (;;) {
|
301 |
int incr, to_grab, wpos;
|
302 |
|
303 |
for (;;) {
|
304 |
if (pa->done) {
|
305 |
goto exit;
|
306 |
} |
307 |
|
308 |
if (pa->dead > 0) { |
309 |
break;
|
310 |
} |
311 |
|
312 |
if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
|
313 |
goto exit;
|
314 |
} |
315 |
} |
316 |
|
317 |
incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
|
318 |
wpos = pa->wpos; |
319 |
|
320 |
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
321 |
return NULL; |
322 |
} |
323 |
|
324 |
while (to_grab) {
|
325 |
int error;
|
326 |
int chunk = audio_MIN (to_grab, hw->samples - wpos);
|
327 |
void *buf = advance (pa->pcm_buf, wpos);
|
328 |
|
329 |
if (qpa_simple_read (pa, buf,
|
330 |
chunk << hw->info.shift, &error) < 0) {
|
331 |
qpa_logerr (error, "pa_simple_read failed\n");
|
332 |
return NULL; |
333 |
} |
334 |
|
335 |
hw->conv (hw->conv_buf + wpos, buf, chunk); |
336 |
wpos = (wpos + chunk) % hw->samples; |
337 |
to_grab -= chunk; |
338 |
} |
339 |
|
340 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
341 |
return NULL; |
342 |
} |
343 |
|
344 |
pa->wpos = wpos; |
345 |
pa->dead -= incr; |
346 |
pa->incr += incr; |
347 |
} |
348 |
|
349 |
exit:
|
350 |
audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
351 |
return NULL; |
352 |
} |
353 |
|
354 |
static int qpa_run_in (HWVoiceIn *hw) |
355 |
{ |
356 |
int live, incr, dead;
|
357 |
PAVoiceIn *pa = (PAVoiceIn *) hw; |
358 |
|
359 |
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
360 |
return 0; |
361 |
} |
362 |
|
363 |
live = audio_pcm_hw_get_live_in (hw); |
364 |
dead = hw->samples - live; |
365 |
incr = audio_MIN (dead, pa->incr); |
366 |
pa->incr -= incr; |
367 |
pa->dead = dead - incr; |
368 |
hw->wpos = pa->wpos; |
369 |
if (pa->dead > 0) { |
370 |
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
371 |
} |
372 |
else {
|
373 |
audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
374 |
} |
375 |
return incr;
|
376 |
} |
377 |
|
378 |
static int qpa_read (SWVoiceIn *sw, void *buf, int len) |
379 |
{ |
380 |
return audio_pcm_sw_read (sw, buf, len);
|
381 |
} |
382 |
|
383 |
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) |
384 |
{ |
385 |
int format;
|
386 |
|
387 |
switch (afmt) {
|
388 |
case AUD_FMT_S8:
|
389 |
case AUD_FMT_U8:
|
390 |
format = PA_SAMPLE_U8; |
391 |
break;
|
392 |
case AUD_FMT_S16:
|
393 |
case AUD_FMT_U16:
|
394 |
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; |
395 |
break;
|
396 |
case AUD_FMT_S32:
|
397 |
case AUD_FMT_U32:
|
398 |
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; |
399 |
break;
|
400 |
default:
|
401 |
dolog ("Internal logic error: Bad audio format %d\n", afmt);
|
402 |
format = PA_SAMPLE_U8; |
403 |
break;
|
404 |
} |
405 |
return format;
|
406 |
} |
407 |
|
408 |
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) |
409 |
{ |
410 |
switch (fmt) {
|
411 |
case PA_SAMPLE_U8:
|
412 |
return AUD_FMT_U8;
|
413 |
case PA_SAMPLE_S16BE:
|
414 |
*endianness = 1;
|
415 |
return AUD_FMT_S16;
|
416 |
case PA_SAMPLE_S16LE:
|
417 |
*endianness = 0;
|
418 |
return AUD_FMT_S16;
|
419 |
case PA_SAMPLE_S32BE:
|
420 |
*endianness = 1;
|
421 |
return AUD_FMT_S32;
|
422 |
case PA_SAMPLE_S32LE:
|
423 |
*endianness = 0;
|
424 |
return AUD_FMT_S32;
|
425 |
default:
|
426 |
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
427 |
return AUD_FMT_U8;
|
428 |
} |
429 |
} |
430 |
|
431 |
static void context_state_cb (pa_context *c, void *userdata) |
432 |
{ |
433 |
paaudio *g = &glob_paaudio; |
434 |
|
435 |
switch (pa_context_get_state(c)) {
|
436 |
case PA_CONTEXT_READY:
|
437 |
case PA_CONTEXT_TERMINATED:
|
438 |
case PA_CONTEXT_FAILED:
|
439 |
pa_threaded_mainloop_signal (g->mainloop, 0);
|
440 |
break;
|
441 |
|
442 |
case PA_CONTEXT_UNCONNECTED:
|
443 |
case PA_CONTEXT_CONNECTING:
|
444 |
case PA_CONTEXT_AUTHORIZING:
|
445 |
case PA_CONTEXT_SETTING_NAME:
|
446 |
break;
|
447 |
} |
448 |
} |
449 |
|
450 |
static void stream_state_cb (pa_stream *s, void * userdata) |
451 |
{ |
452 |
paaudio *g = &glob_paaudio; |
453 |
|
454 |
switch (pa_stream_get_state (s)) {
|
455 |
|
456 |
case PA_STREAM_READY:
|
457 |
case PA_STREAM_FAILED:
|
458 |
case PA_STREAM_TERMINATED:
|
459 |
pa_threaded_mainloop_signal (g->mainloop, 0);
|
460 |
break;
|
461 |
|
462 |
case PA_STREAM_UNCONNECTED:
|
463 |
case PA_STREAM_CREATING:
|
464 |
break;
|
465 |
} |
466 |
} |
467 |
|
468 |
static void stream_request_cb (pa_stream *s, size_t length, void *userdata) |
469 |
{ |
470 |
paaudio *g = &glob_paaudio; |
471 |
|
472 |
pa_threaded_mainloop_signal (g->mainloop, 0);
|
473 |
} |
474 |
|
475 |
static pa_stream *qpa_simple_new (
|
476 |
const char *server, |
477 |
const char *name, |
478 |
pa_stream_direction_t dir, |
479 |
const char *dev, |
480 |
const char *stream_name, |
481 |
const pa_sample_spec *ss,
|
482 |
const pa_channel_map *map,
|
483 |
const pa_buffer_attr *attr,
|
484 |
int *rerror)
|
485 |
{ |
486 |
paaudio *g = &glob_paaudio; |
487 |
int r;
|
488 |
pa_stream *stream; |
489 |
|
490 |
pa_threaded_mainloop_lock (g->mainloop); |
491 |
|
492 |
stream = pa_stream_new (g->context, name, ss, map); |
493 |
if (!stream) {
|
494 |
goto fail;
|
495 |
} |
496 |
|
497 |
pa_stream_set_state_callback (stream, stream_state_cb, g); |
498 |
pa_stream_set_read_callback (stream, stream_request_cb, g); |
499 |
pa_stream_set_write_callback (stream, stream_request_cb, g); |
500 |
|
501 |
if (dir == PA_STREAM_PLAYBACK) {
|
502 |
r = pa_stream_connect_playback (stream, dev, attr, |
503 |
PA_STREAM_INTERPOLATE_TIMING |
504 |
#ifdef PA_STREAM_ADJUST_LATENCY
|
505 |
|PA_STREAM_ADJUST_LATENCY |
506 |
#endif
|
507 |
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); |
508 |
} else {
|
509 |
r = pa_stream_connect_record (stream, dev, attr, |
510 |
PA_STREAM_INTERPOLATE_TIMING |
511 |
#ifdef PA_STREAM_ADJUST_LATENCY
|
512 |
|PA_STREAM_ADJUST_LATENCY |
513 |
#endif
|
514 |
|PA_STREAM_AUTO_TIMING_UPDATE); |
515 |
} |
516 |
|
517 |
if (r < 0) { |
518 |
goto fail;
|
519 |
} |
520 |
|
521 |
pa_threaded_mainloop_unlock (g->mainloop); |
522 |
|
523 |
return stream;
|
524 |
|
525 |
fail:
|
526 |
pa_threaded_mainloop_unlock (g->mainloop); |
527 |
|
528 |
if (stream) {
|
529 |
pa_stream_unref (stream); |
530 |
} |
531 |
|
532 |
*rerror = pa_context_errno (g->context); |
533 |
|
534 |
return NULL; |
535 |
} |
536 |
|
537 |
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) |
538 |
{ |
539 |
int error;
|
540 |
static pa_sample_spec ss;
|
541 |
static pa_buffer_attr ba;
|
542 |
struct audsettings obt_as = *as;
|
543 |
PAVoiceOut *pa = (PAVoiceOut *) hw; |
544 |
|
545 |
ss.format = audfmt_to_pa (as->fmt, as->endianness); |
546 |
ss.channels = as->nchannels; |
547 |
ss.rate = as->freq; |
548 |
|
549 |
/*
|
550 |
* qemu audio tick runs at 250 Hz (by default), so processing
|
551 |
* data chunks worth 4 ms of sound should be a good fit.
|
552 |
*/
|
553 |
ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); |
554 |
ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); |
555 |
ba.maxlength = -1;
|
556 |
ba.prebuf = -1;
|
557 |
|
558 |
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |
559 |
|
560 |
pa->stream = qpa_simple_new ( |
561 |
glob_paaudio.server, |
562 |
"qemu",
|
563 |
PA_STREAM_PLAYBACK, |
564 |
glob_paaudio.sink, |
565 |
"pcm.playback",
|
566 |
&ss, |
567 |
NULL, /* channel map */ |
568 |
&ba, /* buffering attributes */
|
569 |
&error |
570 |
); |
571 |
if (!pa->stream) {
|
572 |
qpa_logerr (error, "pa_simple_new for playback failed\n");
|
573 |
goto fail1;
|
574 |
} |
575 |
|
576 |
audio_pcm_init_info (&hw->info, &obt_as); |
577 |
hw->samples = glob_paaudio.samples; |
578 |
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
579 |
pa->rpos = hw->rpos; |
580 |
if (!pa->pcm_buf) {
|
581 |
dolog ("Could not allocate buffer (%d bytes)\n",
|
582 |
hw->samples << hw->info.shift); |
583 |
goto fail2;
|
584 |
} |
585 |
|
586 |
if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
|
587 |
goto fail3;
|
588 |
} |
589 |
|
590 |
return 0; |
591 |
|
592 |
fail3:
|
593 |
g_free (pa->pcm_buf); |
594 |
pa->pcm_buf = NULL;
|
595 |
fail2:
|
596 |
if (pa->stream) {
|
597 |
pa_stream_unref (pa->stream); |
598 |
pa->stream = NULL;
|
599 |
} |
600 |
fail1:
|
601 |
return -1; |
602 |
} |
603 |
|
604 |
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) |
605 |
{ |
606 |
int error;
|
607 |
static pa_sample_spec ss;
|
608 |
struct audsettings obt_as = *as;
|
609 |
PAVoiceIn *pa = (PAVoiceIn *) hw; |
610 |
|
611 |
ss.format = audfmt_to_pa (as->fmt, as->endianness); |
612 |
ss.channels = as->nchannels; |
613 |
ss.rate = as->freq; |
614 |
|
615 |
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |
616 |
|
617 |
pa->stream = qpa_simple_new ( |
618 |
glob_paaudio.server, |
619 |
"qemu",
|
620 |
PA_STREAM_RECORD, |
621 |
glob_paaudio.source, |
622 |
"pcm.capture",
|
623 |
&ss, |
624 |
NULL, /* channel map */ |
625 |
NULL, /* buffering attributes */ |
626 |
&error |
627 |
); |
628 |
if (!pa->stream) {
|
629 |
qpa_logerr (error, "pa_simple_new for capture failed\n");
|
630 |
goto fail1;
|
631 |
} |
632 |
|
633 |
audio_pcm_init_info (&hw->info, &obt_as); |
634 |
hw->samples = glob_paaudio.samples; |
635 |
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
636 |
pa->wpos = hw->wpos; |
637 |
if (!pa->pcm_buf) {
|
638 |
dolog ("Could not allocate buffer (%d bytes)\n",
|
639 |
hw->samples << hw->info.shift); |
640 |
goto fail2;
|
641 |
} |
642 |
|
643 |
if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
|
644 |
goto fail3;
|
645 |
} |
646 |
|
647 |
return 0; |
648 |
|
649 |
fail3:
|
650 |
g_free (pa->pcm_buf); |
651 |
pa->pcm_buf = NULL;
|
652 |
fail2:
|
653 |
if (pa->stream) {
|
654 |
pa_stream_unref (pa->stream); |
655 |
pa->stream = NULL;
|
656 |
} |
657 |
fail1:
|
658 |
return -1; |
659 |
} |
660 |
|
661 |
static void qpa_fini_out (HWVoiceOut *hw) |
662 |
{ |
663 |
void *ret;
|
664 |
PAVoiceOut *pa = (PAVoiceOut *) hw; |
665 |
|
666 |
audio_pt_lock (&pa->pt, AUDIO_FUNC); |
667 |
pa->done = 1;
|
668 |
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
669 |
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); |
670 |
|
671 |
if (pa->stream) {
|
672 |
pa_stream_unref (pa->stream); |
673 |
pa->stream = NULL;
|
674 |
} |
675 |
|
676 |
audio_pt_fini (&pa->pt, AUDIO_FUNC); |
677 |
g_free (pa->pcm_buf); |
678 |
pa->pcm_buf = NULL;
|
679 |
} |
680 |
|
681 |
static void qpa_fini_in (HWVoiceIn *hw) |
682 |
{ |
683 |
void *ret;
|
684 |
PAVoiceIn *pa = (PAVoiceIn *) hw; |
685 |
|
686 |
audio_pt_lock (&pa->pt, AUDIO_FUNC); |
687 |
pa->done = 1;
|
688 |
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
689 |
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); |
690 |
|
691 |
if (pa->stream) {
|
692 |
pa_stream_unref (pa->stream); |
693 |
pa->stream = NULL;
|
694 |
} |
695 |
|
696 |
audio_pt_fini (&pa->pt, AUDIO_FUNC); |
697 |
g_free (pa->pcm_buf); |
698 |
pa->pcm_buf = NULL;
|
699 |
} |
700 |
|
701 |
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
702 |
{ |
703 |
PAVoiceOut *pa = (PAVoiceOut *) hw; |
704 |
pa_operation *op; |
705 |
pa_cvolume v; |
706 |
paaudio *g = &glob_paaudio; |
707 |
|
708 |
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ |
709 |
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
|
710 |
#endif
|
711 |
|
712 |
switch (cmd) {
|
713 |
case VOICE_VOLUME:
|
714 |
{ |
715 |
SWVoiceOut *sw; |
716 |
va_list ap; |
717 |
|
718 |
va_start (ap, cmd); |
719 |
sw = va_arg (ap, SWVoiceOut *); |
720 |
va_end (ap); |
721 |
|
722 |
v.channels = 2;
|
723 |
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
724 |
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
725 |
|
726 |
pa_threaded_mainloop_lock (g->mainloop); |
727 |
|
728 |
op = pa_context_set_sink_input_volume (g->context, |
729 |
pa_stream_get_index (pa->stream), |
730 |
&v, NULL, NULL); |
731 |
if (!op)
|
732 |
qpa_logerr (pa_context_errno (g->context), |
733 |
"set_sink_input_volume() failed\n");
|
734 |
else
|
735 |
pa_operation_unref (op); |
736 |
|
737 |
op = pa_context_set_sink_input_mute (g->context, |
738 |
pa_stream_get_index (pa->stream), |
739 |
sw->vol.mute, NULL, NULL); |
740 |
if (!op) {
|
741 |
qpa_logerr (pa_context_errno (g->context), |
742 |
"set_sink_input_mute() failed\n");
|
743 |
} else {
|
744 |
pa_operation_unref (op); |
745 |
} |
746 |
|
747 |
pa_threaded_mainloop_unlock (g->mainloop); |
748 |
} |
749 |
} |
750 |
return 0; |
751 |
} |
752 |
|
753 |
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
754 |
{ |
755 |
PAVoiceIn *pa = (PAVoiceIn *) hw; |
756 |
pa_operation *op; |
757 |
pa_cvolume v; |
758 |
paaudio *g = &glob_paaudio; |
759 |
|
760 |
#ifdef PA_CHECK_VERSION
|
761 |
pa_cvolume_init (&v); |
762 |
#endif
|
763 |
|
764 |
switch (cmd) {
|
765 |
case VOICE_VOLUME:
|
766 |
{ |
767 |
SWVoiceIn *sw; |
768 |
va_list ap; |
769 |
|
770 |
va_start (ap, cmd); |
771 |
sw = va_arg (ap, SWVoiceIn *); |
772 |
va_end (ap); |
773 |
|
774 |
v.channels = 2;
|
775 |
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
776 |
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
777 |
|
778 |
pa_threaded_mainloop_lock (g->mainloop); |
779 |
|
780 |
/* FIXME: use the upcoming "set_source_output_{volume,mute}" */
|
781 |
op = pa_context_set_source_volume_by_index (g->context, |
782 |
pa_stream_get_device_index (pa->stream), |
783 |
&v, NULL, NULL); |
784 |
if (!op) {
|
785 |
qpa_logerr (pa_context_errno (g->context), |
786 |
"set_source_volume() failed\n");
|
787 |
} else {
|
788 |
pa_operation_unref(op); |
789 |
} |
790 |
|
791 |
op = pa_context_set_source_mute_by_index (g->context, |
792 |
pa_stream_get_index (pa->stream), |
793 |
sw->vol.mute, NULL, NULL); |
794 |
if (!op) {
|
795 |
qpa_logerr (pa_context_errno (g->context), |
796 |
"set_source_mute() failed\n");
|
797 |
} else {
|
798 |
pa_operation_unref (op); |
799 |
} |
800 |
|
801 |
pa_threaded_mainloop_unlock (g->mainloop); |
802 |
} |
803 |
} |
804 |
return 0; |
805 |
} |
806 |
|
807 |
/* common */
|
808 |
static void *qpa_audio_init (void) |
809 |
{ |
810 |
paaudio *g = &glob_paaudio; |
811 |
|
812 |
g->mainloop = pa_threaded_mainloop_new (); |
813 |
if (!g->mainloop) {
|
814 |
goto fail;
|
815 |
} |
816 |
|
817 |
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); |
818 |
if (!g->context) {
|
819 |
goto fail;
|
820 |
} |
821 |
|
822 |
pa_context_set_state_callback (g->context, context_state_cb, g); |
823 |
|
824 |
if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { |
825 |
qpa_logerr (pa_context_errno (g->context), |
826 |
"pa_context_connect() failed\n");
|
827 |
goto fail;
|
828 |
} |
829 |
|
830 |
pa_threaded_mainloop_lock (g->mainloop); |
831 |
|
832 |
if (pa_threaded_mainloop_start (g->mainloop) < 0) { |
833 |
goto unlock_and_fail;
|
834 |
} |
835 |
|
836 |
for (;;) {
|
837 |
pa_context_state_t state; |
838 |
|
839 |
state = pa_context_get_state (g->context); |
840 |
|
841 |
if (state == PA_CONTEXT_READY) {
|
842 |
break;
|
843 |
} |
844 |
|
845 |
if (!PA_CONTEXT_IS_GOOD (state)) {
|
846 |
qpa_logerr (pa_context_errno (g->context), |
847 |
"Wrong context state\n");
|
848 |
goto unlock_and_fail;
|
849 |
} |
850 |
|
851 |
/* Wait until the context is ready */
|
852 |
pa_threaded_mainloop_wait (g->mainloop); |
853 |
} |
854 |
|
855 |
pa_threaded_mainloop_unlock (g->mainloop); |
856 |
|
857 |
return &glob_paaudio;
|
858 |
|
859 |
unlock_and_fail:
|
860 |
pa_threaded_mainloop_unlock (g->mainloop); |
861 |
fail:
|
862 |
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
|
863 |
return NULL; |
864 |
} |
865 |
|
866 |
static void qpa_audio_fini (void *opaque) |
867 |
{ |
868 |
paaudio *g = opaque; |
869 |
|
870 |
if (g->mainloop) {
|
871 |
pa_threaded_mainloop_stop (g->mainloop); |
872 |
} |
873 |
|
874 |
if (g->context) {
|
875 |
pa_context_disconnect (g->context); |
876 |
pa_context_unref (g->context); |
877 |
g->context = NULL;
|
878 |
} |
879 |
|
880 |
if (g->mainloop) {
|
881 |
pa_threaded_mainloop_free (g->mainloop); |
882 |
} |
883 |
|
884 |
g->mainloop = NULL;
|
885 |
} |
886 |
|
887 |
struct audio_option qpa_options[] = {
|
888 |
{ |
889 |
.name = "SAMPLES",
|
890 |
.tag = AUD_OPT_INT, |
891 |
.valp = &glob_paaudio.samples, |
892 |
.descr = "buffer size in samples"
|
893 |
}, |
894 |
{ |
895 |
.name = "SERVER",
|
896 |
.tag = AUD_OPT_STR, |
897 |
.valp = &glob_paaudio.server, |
898 |
.descr = "server address"
|
899 |
}, |
900 |
{ |
901 |
.name = "SINK",
|
902 |
.tag = AUD_OPT_STR, |
903 |
.valp = &glob_paaudio.sink, |
904 |
.descr = "sink device name"
|
905 |
}, |
906 |
{ |
907 |
.name = "SOURCE",
|
908 |
.tag = AUD_OPT_STR, |
909 |
.valp = &glob_paaudio.source, |
910 |
.descr = "source device name"
|
911 |
}, |
912 |
{ /* End of list */ }
|
913 |
}; |
914 |
|
915 |
static struct audio_pcm_ops qpa_pcm_ops = { |
916 |
.init_out = qpa_init_out, |
917 |
.fini_out = qpa_fini_out, |
918 |
.run_out = qpa_run_out, |
919 |
.write = qpa_write, |
920 |
.ctl_out = qpa_ctl_out, |
921 |
|
922 |
.init_in = qpa_init_in, |
923 |
.fini_in = qpa_fini_in, |
924 |
.run_in = qpa_run_in, |
925 |
.read = qpa_read, |
926 |
.ctl_in = qpa_ctl_in |
927 |
}; |
928 |
|
929 |
struct audio_driver pa_audio_driver = {
|
930 |
.name = "pa",
|
931 |
.descr = "http://www.pulseaudio.org/",
|
932 |
.options = qpa_options, |
933 |
.init = qpa_audio_init, |
934 |
.fini = qpa_audio_fini, |
935 |
.pcm_ops = &qpa_pcm_ops, |
936 |
.can_be_default = 1,
|
937 |
.max_voices_out = INT_MAX, |
938 |
.max_voices_in = INT_MAX, |
939 |
.voice_size_out = sizeof (PAVoiceOut),
|
940 |
.voice_size_in = sizeof (PAVoiceIn),
|
941 |
.ctl_caps = VOICE_VOLUME_CAP |
942 |
}; |