|
|
1.1 root 1: /* public domain */
2: #include "qemu-common.h"
3: #include "audio.h"
4:
1.1.1.6 ! root 5: #include <pulse/pulseaudio.h>
1.1 root 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;
1.1.1.6 ! root 17: pa_stream *stream;
1.1 root 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;
1.1.1.6 ! root 28: pa_stream *stream;
1.1 root 29: void *pcm_buf;
30: struct audio_pt pt;
1.1.1.6 ! root 31: const void *read_data;
! 32: size_t read_index, read_length;
1.1 root 33: } PAVoiceIn;
34:
1.1.1.6 ! root 35: typedef struct {
1.1 root 36: int samples;
37: char *server;
38: char *sink;
39: char *source;
1.1.1.6 ! root 40: pa_threaded_mainloop *mainloop;
! 41: pa_context *context;
! 42: } paaudio;
! 43:
! 44: static paaudio glob_paaudio = {
1.1.1.4 root 45: .samples = 4096,
1.1 root 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:
1.1.1.6 ! root 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:
1.1 root 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:
1.1.1.4 root 216: if (pa->live > 0) {
1.1 root 217: break;
218: }
219:
220: if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
221: goto exit;
222: }
223: }
224:
1.1.1.6 ! root 225: decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
1.1.1.4 root 226: rpos = pa->rpos;
1.1 root 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:
1.1.1.6 ! root 239: if (qpa_simple_write (pa, pa->pcm_buf,
! 240: chunk << hw->info.shift, &error) < 0) {
1.1 root 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:
1.1.1.3 root 263: static int qpa_run_out (HWVoiceOut *hw, int live)
1.1 root 264: {
1.1.1.3 root 265: int decr;
1.1 root 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:
1.1.1.4 root 308: if (pa->dead > 0) {
1.1 root 309: break;
310: }
311:
312: if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
313: goto exit;
314: }
315: }
316:
1.1.1.6 ! root 317: incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
1.1.1.4 root 318: wpos = pa->wpos;
1.1 root 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:
1.1.1.6 ! root 329: if (qpa_simple_read (pa, buf,
! 330: chunk << hw->info.shift, &error) < 0) {
1.1 root 331: qpa_logerr (error, "pa_simple_read failed\n");
332: return NULL;
333: }
334:
1.1.1.4 root 335: hw->conv (hw->conv_buf + wpos, buf, chunk);
1.1 root 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:
1.1.1.6 ! root 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:
1.1 root 537: static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
538: {
539: int error;
540: static pa_sample_spec ss;
1.1.1.4 root 541: static pa_buffer_attr ba;
1.1 root 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:
1.1.1.4 root 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:
1.1 root 558: obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
559:
1.1.1.6 ! root 560: pa->stream = qpa_simple_new (
! 561: glob_paaudio.server,
1.1 root 562: "qemu",
563: PA_STREAM_PLAYBACK,
1.1.1.6 ! root 564: glob_paaudio.sink,
1.1 root 565: "pcm.playback",
566: &ss,
567: NULL, /* channel map */
1.1.1.4 root 568: &ba, /* buffering attributes */
1.1 root 569: &error
570: );
1.1.1.6 ! root 571: if (!pa->stream) {
1.1 root 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);
1.1.1.6 ! root 577: hw->samples = glob_paaudio.samples;
1.1 root 578: pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 root 579: pa->rpos = hw->rpos;
1.1 root 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:
1.1.1.5 root 593: g_free (pa->pcm_buf);
1.1 root 594: pa->pcm_buf = NULL;
595: fail2:
1.1.1.6 ! root 596: if (pa->stream) {
! 597: pa_stream_unref (pa->stream);
! 598: pa->stream = NULL;
! 599: }
1.1 root 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:
1.1.1.6 ! root 617: pa->stream = qpa_simple_new (
! 618: glob_paaudio.server,
1.1 root 619: "qemu",
620: PA_STREAM_RECORD,
1.1.1.6 ! root 621: glob_paaudio.source,
1.1 root 622: "pcm.capture",
623: &ss,
624: NULL, /* channel map */
625: NULL, /* buffering attributes */
626: &error
627: );
1.1.1.6 ! root 628: if (!pa->stream) {
1.1 root 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);
1.1.1.6 ! root 634: hw->samples = glob_paaudio.samples;
1.1 root 635: pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 root 636: pa->wpos = hw->wpos;
1.1 root 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:
1.1.1.5 root 650: g_free (pa->pcm_buf);
1.1 root 651: pa->pcm_buf = NULL;
652: fail2:
1.1.1.6 ! root 653: if (pa->stream) {
! 654: pa_stream_unref (pa->stream);
! 655: pa->stream = NULL;
! 656: }
1.1 root 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:
1.1.1.6 ! root 671: if (pa->stream) {
! 672: pa_stream_unref (pa->stream);
! 673: pa->stream = NULL;
1.1 root 674: }
675:
676: audio_pt_fini (&pa->pt, AUDIO_FUNC);
1.1.1.5 root 677: g_free (pa->pcm_buf);
1.1 root 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:
1.1.1.6 ! root 691: if (pa->stream) {
! 692: pa_stream_unref (pa->stream);
! 693: pa->stream = NULL;
1.1 root 694: }
695:
696: audio_pt_fini (&pa->pt, AUDIO_FUNC);
1.1.1.5 root 697: g_free (pa->pcm_buf);
1.1 root 698: pa->pcm_buf = NULL;
699: }
700:
701: static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
702: {
1.1.1.6 ! root 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: }
1.1 root 750: return 0;
751: }
752:
753: static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
754: {
1.1.1.6 ! root 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: }
1.1 root 804: return 0;
805: }
806:
807: /* common */
808: static void *qpa_audio_init (void)
809: {
1.1.1.6 ! root 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;
1.1 root 864: }
865:
866: static void qpa_audio_fini (void *opaque)
867: {
1.1.1.6 ! root 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;
1.1 root 885: }
886:
887: struct audio_option qpa_options[] = {
1.1.1.3 root 888: {
889: .name = "SAMPLES",
890: .tag = AUD_OPT_INT,
1.1.1.6 ! root 891: .valp = &glob_paaudio.samples,
1.1.1.3 root 892: .descr = "buffer size in samples"
893: },
894: {
895: .name = "SERVER",
896: .tag = AUD_OPT_STR,
1.1.1.6 ! root 897: .valp = &glob_paaudio.server,
1.1.1.3 root 898: .descr = "server address"
899: },
900: {
901: .name = "SINK",
902: .tag = AUD_OPT_STR,
1.1.1.6 ! root 903: .valp = &glob_paaudio.sink,
1.1.1.3 root 904: .descr = "sink device name"
905: },
906: {
907: .name = "SOURCE",
908: .tag = AUD_OPT_STR,
1.1.1.6 ! root 909: .valp = &glob_paaudio.source,
1.1.1.3 root 910: .descr = "source device name"
911: },
912: { /* End of list */ }
1.1 root 913: };
914:
915: static struct audio_pcm_ops qpa_pcm_ops = {
1.1.1.3 root 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
1.1 root 927: };
928:
929: struct audio_driver pa_audio_driver = {
1.1.1.3 root 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),
1.1.1.6 ! root 940: .voice_size_in = sizeof (PAVoiceIn),
! 941: .ctl_caps = VOICE_VOLUME_CAP
1.1 root 942: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.