1b8e59f18Smalc /* public domain */ 2b8e59f18Smalc #include "qemu-common.h" 3b8e59f18Smalc #include "audio.h" 4b8e59f18Smalc 5b8e59f18Smalc #include <pulse/simple.h> 6b8e59f18Smalc #include <pulse/error.h> 7b8e59f18Smalc 8b8e59f18Smalc #define AUDIO_CAP "pulseaudio" 9b8e59f18Smalc #include "audio_int.h" 10b8e59f18Smalc #include "audio_pt_int.h" 11b8e59f18Smalc 12b8e59f18Smalc typedef struct { 13b8e59f18Smalc HWVoiceOut hw; 14b8e59f18Smalc int done; 15b8e59f18Smalc int live; 16b8e59f18Smalc int decr; 17b8e59f18Smalc int rpos; 18b8e59f18Smalc pa_simple *s; 19b8e59f18Smalc void *pcm_buf; 20b8e59f18Smalc struct audio_pt pt; 21b8e59f18Smalc } PAVoiceOut; 22b8e59f18Smalc 23b8e59f18Smalc typedef struct { 24b8e59f18Smalc HWVoiceIn hw; 25b8e59f18Smalc int done; 26b8e59f18Smalc int dead; 27b8e59f18Smalc int incr; 28b8e59f18Smalc int wpos; 29b8e59f18Smalc pa_simple *s; 30b8e59f18Smalc void *pcm_buf; 31b8e59f18Smalc struct audio_pt pt; 32b8e59f18Smalc } PAVoiceIn; 33b8e59f18Smalc 34b8e59f18Smalc static struct { 35b8e59f18Smalc int samples; 36b8e59f18Smalc int divisor; 37b8e59f18Smalc char *server; 38b8e59f18Smalc char *sink; 39b8e59f18Smalc char *source; 40b8e59f18Smalc } conf = { 411a40d5e2SJuan Quintela .samples = 1024, 421a40d5e2SJuan Quintela .divisor = 2, 43b8e59f18Smalc }; 44b8e59f18Smalc 45b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) 46b8e59f18Smalc { 47b8e59f18Smalc va_list ap; 48b8e59f18Smalc 49b8e59f18Smalc va_start (ap, fmt); 50b8e59f18Smalc AUD_vlog (AUDIO_CAP, fmt, ap); 51b8e59f18Smalc va_end (ap); 52b8e59f18Smalc 53b8e59f18Smalc AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); 54b8e59f18Smalc } 55b8e59f18Smalc 56b8e59f18Smalc static void *qpa_thread_out (void *arg) 57b8e59f18Smalc { 58b8e59f18Smalc PAVoiceOut *pa = arg; 59b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 60b8e59f18Smalc int threshold; 61b8e59f18Smalc 62b8e59f18Smalc threshold = conf.divisor ? hw->samples / conf.divisor : 0; 63b8e59f18Smalc 64b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 65b8e59f18Smalc return NULL; 66b8e59f18Smalc } 67b8e59f18Smalc 68b8e59f18Smalc for (;;) { 69b8e59f18Smalc int decr, to_mix, rpos; 70b8e59f18Smalc 71b8e59f18Smalc for (;;) { 72b8e59f18Smalc if (pa->done) { 73b8e59f18Smalc goto exit; 74b8e59f18Smalc } 75b8e59f18Smalc 76b8e59f18Smalc if (pa->live > threshold) { 77b8e59f18Smalc break; 78b8e59f18Smalc } 79b8e59f18Smalc 80b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 81b8e59f18Smalc goto exit; 82b8e59f18Smalc } 83b8e59f18Smalc } 84b8e59f18Smalc 85b8e59f18Smalc decr = to_mix = pa->live; 86b8e59f18Smalc rpos = hw->rpos; 87b8e59f18Smalc 88b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 89b8e59f18Smalc return NULL; 90b8e59f18Smalc } 91b8e59f18Smalc 92b8e59f18Smalc while (to_mix) { 93b8e59f18Smalc int error; 94b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 951ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 96b8e59f18Smalc 97b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 98b8e59f18Smalc 99b8e59f18Smalc if (pa_simple_write (pa->s, pa->pcm_buf, 100b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 101b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 102b8e59f18Smalc return NULL; 103b8e59f18Smalc } 104b8e59f18Smalc 105b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 106b8e59f18Smalc to_mix -= chunk; 107b8e59f18Smalc } 108b8e59f18Smalc 109b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 110b8e59f18Smalc return NULL; 111b8e59f18Smalc } 112b8e59f18Smalc 113fd5723b3SWu Fengguang pa->live = 0; 114b8e59f18Smalc pa->rpos = rpos; 115b8e59f18Smalc pa->decr += decr; 116b8e59f18Smalc } 117b8e59f18Smalc 118b8e59f18Smalc exit: 119b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 120b8e59f18Smalc return NULL; 121b8e59f18Smalc } 122b8e59f18Smalc 123bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 124b8e59f18Smalc { 125bdff253cSmalc int decr; 126b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 127b8e59f18Smalc 128b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 129b8e59f18Smalc return 0; 130b8e59f18Smalc } 131b8e59f18Smalc 132b8e59f18Smalc decr = audio_MIN (live, pa->decr); 133b8e59f18Smalc pa->decr -= decr; 134b8e59f18Smalc pa->live = live - decr; 135b8e59f18Smalc hw->rpos = pa->rpos; 136b8e59f18Smalc if (pa->live > 0) { 137b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 138b8e59f18Smalc } 139b8e59f18Smalc else { 140b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 141b8e59f18Smalc } 142b8e59f18Smalc return decr; 143b8e59f18Smalc } 144b8e59f18Smalc 145b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 146b8e59f18Smalc { 147b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 148b8e59f18Smalc } 149b8e59f18Smalc 150b8e59f18Smalc /* capture */ 151b8e59f18Smalc static void *qpa_thread_in (void *arg) 152b8e59f18Smalc { 153b8e59f18Smalc PAVoiceIn *pa = arg; 154b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 155b8e59f18Smalc int threshold; 156b8e59f18Smalc 157b8e59f18Smalc threshold = conf.divisor ? hw->samples / conf.divisor : 0; 158b8e59f18Smalc 159b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 160b8e59f18Smalc return NULL; 161b8e59f18Smalc } 162b8e59f18Smalc 163b8e59f18Smalc for (;;) { 164b8e59f18Smalc int incr, to_grab, wpos; 165b8e59f18Smalc 166b8e59f18Smalc for (;;) { 167b8e59f18Smalc if (pa->done) { 168b8e59f18Smalc goto exit; 169b8e59f18Smalc } 170b8e59f18Smalc 171b8e59f18Smalc if (pa->dead > threshold) { 172b8e59f18Smalc break; 173b8e59f18Smalc } 174b8e59f18Smalc 175b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 176b8e59f18Smalc goto exit; 177b8e59f18Smalc } 178b8e59f18Smalc } 179b8e59f18Smalc 180b8e59f18Smalc incr = to_grab = pa->dead; 181b8e59f18Smalc wpos = hw->wpos; 182b8e59f18Smalc 183b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 184b8e59f18Smalc return NULL; 185b8e59f18Smalc } 186b8e59f18Smalc 187b8e59f18Smalc while (to_grab) { 188b8e59f18Smalc int error; 189b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 190b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 191b8e59f18Smalc 192b8e59f18Smalc if (pa_simple_read (pa->s, buf, 193b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 194b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 195b8e59f18Smalc return NULL; 196b8e59f18Smalc } 197b8e59f18Smalc 198*00e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 199b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 200b8e59f18Smalc to_grab -= chunk; 201b8e59f18Smalc } 202b8e59f18Smalc 203b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 204b8e59f18Smalc return NULL; 205b8e59f18Smalc } 206b8e59f18Smalc 207b8e59f18Smalc pa->wpos = wpos; 208b8e59f18Smalc pa->dead -= incr; 209b8e59f18Smalc pa->incr += incr; 210b8e59f18Smalc } 211b8e59f18Smalc 212b8e59f18Smalc exit: 213b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 214b8e59f18Smalc return NULL; 215b8e59f18Smalc } 216b8e59f18Smalc 217b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 218b8e59f18Smalc { 219b8e59f18Smalc int live, incr, dead; 220b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 221b8e59f18Smalc 222b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 223b8e59f18Smalc return 0; 224b8e59f18Smalc } 225b8e59f18Smalc 226b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 227b8e59f18Smalc dead = hw->samples - live; 228b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 229b8e59f18Smalc pa->incr -= incr; 230b8e59f18Smalc pa->dead = dead - incr; 231b8e59f18Smalc hw->wpos = pa->wpos; 232b8e59f18Smalc if (pa->dead > 0) { 233b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 234b8e59f18Smalc } 235b8e59f18Smalc else { 236b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 237b8e59f18Smalc } 238b8e59f18Smalc return incr; 239b8e59f18Smalc } 240b8e59f18Smalc 241b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 242b8e59f18Smalc { 243b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 244b8e59f18Smalc } 245b8e59f18Smalc 246b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 247b8e59f18Smalc { 248b8e59f18Smalc int format; 249b8e59f18Smalc 250b8e59f18Smalc switch (afmt) { 251b8e59f18Smalc case AUD_FMT_S8: 252b8e59f18Smalc case AUD_FMT_U8: 253b8e59f18Smalc format = PA_SAMPLE_U8; 254b8e59f18Smalc break; 255b8e59f18Smalc case AUD_FMT_S16: 256b8e59f18Smalc case AUD_FMT_U16: 257b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 258b8e59f18Smalc break; 259b8e59f18Smalc case AUD_FMT_S32: 260b8e59f18Smalc case AUD_FMT_U32: 261b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 262b8e59f18Smalc break; 263b8e59f18Smalc default: 264b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 265b8e59f18Smalc format = PA_SAMPLE_U8; 266b8e59f18Smalc break; 267b8e59f18Smalc } 268b8e59f18Smalc return format; 269b8e59f18Smalc } 270b8e59f18Smalc 271b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 272b8e59f18Smalc { 273b8e59f18Smalc switch (fmt) { 274b8e59f18Smalc case PA_SAMPLE_U8: 275b8e59f18Smalc return AUD_FMT_U8; 276b8e59f18Smalc case PA_SAMPLE_S16BE: 277b8e59f18Smalc *endianness = 1; 278b8e59f18Smalc return AUD_FMT_S16; 279b8e59f18Smalc case PA_SAMPLE_S16LE: 280b8e59f18Smalc *endianness = 0; 281b8e59f18Smalc return AUD_FMT_S16; 282b8e59f18Smalc case PA_SAMPLE_S32BE: 283b8e59f18Smalc *endianness = 1; 284b8e59f18Smalc return AUD_FMT_S32; 285b8e59f18Smalc case PA_SAMPLE_S32LE: 286b8e59f18Smalc *endianness = 0; 287b8e59f18Smalc return AUD_FMT_S32; 288b8e59f18Smalc default: 289b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 290b8e59f18Smalc return AUD_FMT_U8; 291b8e59f18Smalc } 292b8e59f18Smalc } 293b8e59f18Smalc 2941ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) 295b8e59f18Smalc { 296b8e59f18Smalc int error; 297b8e59f18Smalc static pa_sample_spec ss; 2981ea879e5Smalc struct audsettings obt_as = *as; 299b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 300b8e59f18Smalc 301b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 302b8e59f18Smalc ss.channels = as->nchannels; 303b8e59f18Smalc ss.rate = as->freq; 304b8e59f18Smalc 305b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 306b8e59f18Smalc 307b8e59f18Smalc pa->s = pa_simple_new ( 308b8e59f18Smalc conf.server, 309b8e59f18Smalc "qemu", 310b8e59f18Smalc PA_STREAM_PLAYBACK, 311b8e59f18Smalc conf.sink, 312b8e59f18Smalc "pcm.playback", 313b8e59f18Smalc &ss, 314b8e59f18Smalc NULL, /* channel map */ 315b8e59f18Smalc NULL, /* buffering attributes */ 316b8e59f18Smalc &error 317b8e59f18Smalc ); 318b8e59f18Smalc if (!pa->s) { 319b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 320b8e59f18Smalc goto fail1; 321b8e59f18Smalc } 322b8e59f18Smalc 323b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 324b8e59f18Smalc hw->samples = conf.samples; 325b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 326b8e59f18Smalc if (!pa->pcm_buf) { 327b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 328b8e59f18Smalc hw->samples << hw->info.shift); 329b8e59f18Smalc goto fail2; 330b8e59f18Smalc } 331b8e59f18Smalc 332b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 333b8e59f18Smalc goto fail3; 334b8e59f18Smalc } 335b8e59f18Smalc 336b8e59f18Smalc return 0; 337b8e59f18Smalc 338b8e59f18Smalc fail3: 3395d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 340b8e59f18Smalc pa->pcm_buf = NULL; 341b8e59f18Smalc fail2: 342b8e59f18Smalc pa_simple_free (pa->s); 343b8e59f18Smalc pa->s = NULL; 344b8e59f18Smalc fail1: 345b8e59f18Smalc return -1; 346b8e59f18Smalc } 347b8e59f18Smalc 3481ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) 349b8e59f18Smalc { 350b8e59f18Smalc int error; 351b8e59f18Smalc static pa_sample_spec ss; 3521ea879e5Smalc struct audsettings obt_as = *as; 353b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 354b8e59f18Smalc 355b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 356b8e59f18Smalc ss.channels = as->nchannels; 357b8e59f18Smalc ss.rate = as->freq; 358b8e59f18Smalc 359b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 360b8e59f18Smalc 361b8e59f18Smalc pa->s = pa_simple_new ( 362b8e59f18Smalc conf.server, 363b8e59f18Smalc "qemu", 364b8e59f18Smalc PA_STREAM_RECORD, 365b8e59f18Smalc conf.source, 366b8e59f18Smalc "pcm.capture", 367b8e59f18Smalc &ss, 368b8e59f18Smalc NULL, /* channel map */ 369b8e59f18Smalc NULL, /* buffering attributes */ 370b8e59f18Smalc &error 371b8e59f18Smalc ); 372b8e59f18Smalc if (!pa->s) { 373b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 374b8e59f18Smalc goto fail1; 375b8e59f18Smalc } 376b8e59f18Smalc 377b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 378b8e59f18Smalc hw->samples = conf.samples; 379b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 380b8e59f18Smalc if (!pa->pcm_buf) { 381b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 382b8e59f18Smalc hw->samples << hw->info.shift); 383b8e59f18Smalc goto fail2; 384b8e59f18Smalc } 385b8e59f18Smalc 386b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 387b8e59f18Smalc goto fail3; 388b8e59f18Smalc } 389b8e59f18Smalc 390b8e59f18Smalc return 0; 391b8e59f18Smalc 392b8e59f18Smalc fail3: 3935d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 394b8e59f18Smalc pa->pcm_buf = NULL; 395b8e59f18Smalc fail2: 396b8e59f18Smalc pa_simple_free (pa->s); 397b8e59f18Smalc pa->s = NULL; 398b8e59f18Smalc fail1: 399b8e59f18Smalc return -1; 400b8e59f18Smalc } 401b8e59f18Smalc 402b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 403b8e59f18Smalc { 404b8e59f18Smalc void *ret; 405b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 406b8e59f18Smalc 407b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 408b8e59f18Smalc pa->done = 1; 409b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 410b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 411b8e59f18Smalc 412b8e59f18Smalc if (pa->s) { 413b8e59f18Smalc pa_simple_free (pa->s); 414b8e59f18Smalc pa->s = NULL; 415b8e59f18Smalc } 416b8e59f18Smalc 417b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 418b8e59f18Smalc qemu_free (pa->pcm_buf); 419b8e59f18Smalc pa->pcm_buf = NULL; 420b8e59f18Smalc } 421b8e59f18Smalc 422b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 423b8e59f18Smalc { 424b8e59f18Smalc void *ret; 425b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 426b8e59f18Smalc 427b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 428b8e59f18Smalc pa->done = 1; 429b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 430b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 431b8e59f18Smalc 432b8e59f18Smalc if (pa->s) { 433b8e59f18Smalc pa_simple_free (pa->s); 434b8e59f18Smalc pa->s = NULL; 435b8e59f18Smalc } 436b8e59f18Smalc 437b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 438b8e59f18Smalc qemu_free (pa->pcm_buf); 439b8e59f18Smalc pa->pcm_buf = NULL; 440b8e59f18Smalc } 441b8e59f18Smalc 442b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 443b8e59f18Smalc { 444b8e59f18Smalc (void) hw; 445b8e59f18Smalc (void) cmd; 446b8e59f18Smalc return 0; 447b8e59f18Smalc } 448b8e59f18Smalc 449b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 450b8e59f18Smalc { 451b8e59f18Smalc (void) hw; 452b8e59f18Smalc (void) cmd; 453b8e59f18Smalc return 0; 454b8e59f18Smalc } 455b8e59f18Smalc 456b8e59f18Smalc /* common */ 457b8e59f18Smalc static void *qpa_audio_init (void) 458b8e59f18Smalc { 459b8e59f18Smalc return &conf; 460b8e59f18Smalc } 461b8e59f18Smalc 462b8e59f18Smalc static void qpa_audio_fini (void *opaque) 463b8e59f18Smalc { 464b8e59f18Smalc (void) opaque; 465b8e59f18Smalc } 466b8e59f18Smalc 467b8e59f18Smalc struct audio_option qpa_options[] = { 46898f9f48cSmalc { 46998f9f48cSmalc .name = "SAMPLES", 4702700efa3SJuan Quintela .tag = AUD_OPT_INT, 4712700efa3SJuan Quintela .valp = &conf.samples, 47298f9f48cSmalc .descr = "buffer size in samples" 47398f9f48cSmalc }, 47498f9f48cSmalc { 47598f9f48cSmalc .name = "DIVISOR", 4762700efa3SJuan Quintela .tag = AUD_OPT_INT, 4772700efa3SJuan Quintela .valp = &conf.divisor, 47898f9f48cSmalc .descr = "threshold divisor" 47998f9f48cSmalc }, 48098f9f48cSmalc { 48198f9f48cSmalc .name = "SERVER", 4822700efa3SJuan Quintela .tag = AUD_OPT_STR, 4832700efa3SJuan Quintela .valp = &conf.server, 48498f9f48cSmalc .descr = "server address" 48598f9f48cSmalc }, 48698f9f48cSmalc { 48798f9f48cSmalc .name = "SINK", 4882700efa3SJuan Quintela .tag = AUD_OPT_STR, 4892700efa3SJuan Quintela .valp = &conf.sink, 49098f9f48cSmalc .descr = "sink device name" 49198f9f48cSmalc }, 49298f9f48cSmalc { 49398f9f48cSmalc .name = "SOURCE", 4942700efa3SJuan Quintela .tag = AUD_OPT_STR, 4952700efa3SJuan Quintela .valp = &conf.source, 49698f9f48cSmalc .descr = "source device name" 49798f9f48cSmalc }, 4982700efa3SJuan Quintela { /* End of list */ } 499b8e59f18Smalc }; 500b8e59f18Smalc 50135f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 5021dd3e4d1SJuan Quintela .init_out = qpa_init_out, 5031dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 5041dd3e4d1SJuan Quintela .run_out = qpa_run_out, 5051dd3e4d1SJuan Quintela .write = qpa_write, 5061dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 5071dd3e4d1SJuan Quintela 5081dd3e4d1SJuan Quintela .init_in = qpa_init_in, 5091dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 5101dd3e4d1SJuan Quintela .run_in = qpa_run_in, 5111dd3e4d1SJuan Quintela .read = qpa_read, 5121dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 513b8e59f18Smalc }; 514b8e59f18Smalc 515b8e59f18Smalc struct audio_driver pa_audio_driver = { 516bee37f32SJuan Quintela .name = "pa", 517bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 518bee37f32SJuan Quintela .options = qpa_options, 519bee37f32SJuan Quintela .init = qpa_audio_init, 520bee37f32SJuan Quintela .fini = qpa_audio_fini, 521bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 5221a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 523bee37f32SJuan Quintela .max_voices_out = INT_MAX, 524bee37f32SJuan Quintela .max_voices_in = INT_MAX, 525bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 526bee37f32SJuan Quintela .voice_size_in = sizeof (PAVoiceIn) 527b8e59f18Smalc }; 528