|
|
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.