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 = { 41*1a40d5e2SJuan Quintela .samples = 1024, 42*1a40d5e2SJuan 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 113b8e59f18Smalc pa->rpos = rpos; 114b8e59f18Smalc pa->live -= decr; 115b8e59f18Smalc pa->decr += decr; 116b8e59f18Smalc } 117b8e59f18Smalc 118b8e59f18Smalc exit: 119b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 120b8e59f18Smalc return NULL; 121b8e59f18Smalc } 122b8e59f18Smalc 123b8e59f18Smalc static int qpa_run_out (HWVoiceOut *hw) 124b8e59f18Smalc { 125b8e59f18Smalc int live, decr; 126b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 127b8e59f18Smalc 128b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 129b8e59f18Smalc return 0; 130b8e59f18Smalc } 131b8e59f18Smalc 132b8e59f18Smalc live = audio_pcm_hw_get_live_out (hw); 133b8e59f18Smalc decr = audio_MIN (live, pa->decr); 134b8e59f18Smalc pa->decr -= decr; 135b8e59f18Smalc pa->live = live - decr; 136b8e59f18Smalc hw->rpos = pa->rpos; 137b8e59f18Smalc if (pa->live > 0) { 138b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 139b8e59f18Smalc } 140b8e59f18Smalc else { 141b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 142b8e59f18Smalc } 143b8e59f18Smalc return decr; 144b8e59f18Smalc } 145b8e59f18Smalc 146b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 147b8e59f18Smalc { 148b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 149b8e59f18Smalc } 150b8e59f18Smalc 151b8e59f18Smalc /* capture */ 152b8e59f18Smalc static void *qpa_thread_in (void *arg) 153b8e59f18Smalc { 154b8e59f18Smalc PAVoiceIn *pa = arg; 155b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 156b8e59f18Smalc int threshold; 157b8e59f18Smalc 158b8e59f18Smalc threshold = conf.divisor ? hw->samples / conf.divisor : 0; 159b8e59f18Smalc 160b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 161b8e59f18Smalc return NULL; 162b8e59f18Smalc } 163b8e59f18Smalc 164b8e59f18Smalc for (;;) { 165b8e59f18Smalc int incr, to_grab, wpos; 166b8e59f18Smalc 167b8e59f18Smalc for (;;) { 168b8e59f18Smalc if (pa->done) { 169b8e59f18Smalc goto exit; 170b8e59f18Smalc } 171b8e59f18Smalc 172b8e59f18Smalc if (pa->dead > threshold) { 173b8e59f18Smalc break; 174b8e59f18Smalc } 175b8e59f18Smalc 176b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 177b8e59f18Smalc goto exit; 178b8e59f18Smalc } 179b8e59f18Smalc } 180b8e59f18Smalc 181b8e59f18Smalc incr = to_grab = pa->dead; 182b8e59f18Smalc wpos = hw->wpos; 183b8e59f18Smalc 184b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 185b8e59f18Smalc return NULL; 186b8e59f18Smalc } 187b8e59f18Smalc 188b8e59f18Smalc while (to_grab) { 189b8e59f18Smalc int error; 190b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 191b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 192b8e59f18Smalc 193b8e59f18Smalc if (pa_simple_read (pa->s, buf, 194b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 195b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 196b8e59f18Smalc return NULL; 197b8e59f18Smalc } 198b8e59f18Smalc 199b8e59f18Smalc hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); 200b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 201b8e59f18Smalc to_grab -= chunk; 202b8e59f18Smalc } 203b8e59f18Smalc 204b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 205b8e59f18Smalc return NULL; 206b8e59f18Smalc } 207b8e59f18Smalc 208b8e59f18Smalc pa->wpos = wpos; 209b8e59f18Smalc pa->dead -= incr; 210b8e59f18Smalc pa->incr += incr; 211b8e59f18Smalc } 212b8e59f18Smalc 213b8e59f18Smalc exit: 214b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 215b8e59f18Smalc return NULL; 216b8e59f18Smalc } 217b8e59f18Smalc 218b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 219b8e59f18Smalc { 220b8e59f18Smalc int live, incr, dead; 221b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 222b8e59f18Smalc 223b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 224b8e59f18Smalc return 0; 225b8e59f18Smalc } 226b8e59f18Smalc 227b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 228b8e59f18Smalc dead = hw->samples - live; 229b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 230b8e59f18Smalc pa->incr -= incr; 231b8e59f18Smalc pa->dead = dead - incr; 232b8e59f18Smalc hw->wpos = pa->wpos; 233b8e59f18Smalc if (pa->dead > 0) { 234b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 235b8e59f18Smalc } 236b8e59f18Smalc else { 237b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 238b8e59f18Smalc } 239b8e59f18Smalc return incr; 240b8e59f18Smalc } 241b8e59f18Smalc 242b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 243b8e59f18Smalc { 244b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 245b8e59f18Smalc } 246b8e59f18Smalc 247b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 248b8e59f18Smalc { 249b8e59f18Smalc int format; 250b8e59f18Smalc 251b8e59f18Smalc switch (afmt) { 252b8e59f18Smalc case AUD_FMT_S8: 253b8e59f18Smalc case AUD_FMT_U8: 254b8e59f18Smalc format = PA_SAMPLE_U8; 255b8e59f18Smalc break; 256b8e59f18Smalc case AUD_FMT_S16: 257b8e59f18Smalc case AUD_FMT_U16: 258b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 259b8e59f18Smalc break; 260b8e59f18Smalc case AUD_FMT_S32: 261b8e59f18Smalc case AUD_FMT_U32: 262b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 263b8e59f18Smalc break; 264b8e59f18Smalc default: 265b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 266b8e59f18Smalc format = PA_SAMPLE_U8; 267b8e59f18Smalc break; 268b8e59f18Smalc } 269b8e59f18Smalc return format; 270b8e59f18Smalc } 271b8e59f18Smalc 272b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 273b8e59f18Smalc { 274b8e59f18Smalc switch (fmt) { 275b8e59f18Smalc case PA_SAMPLE_U8: 276b8e59f18Smalc return AUD_FMT_U8; 277b8e59f18Smalc case PA_SAMPLE_S16BE: 278b8e59f18Smalc *endianness = 1; 279b8e59f18Smalc return AUD_FMT_S16; 280b8e59f18Smalc case PA_SAMPLE_S16LE: 281b8e59f18Smalc *endianness = 0; 282b8e59f18Smalc return AUD_FMT_S16; 283b8e59f18Smalc case PA_SAMPLE_S32BE: 284b8e59f18Smalc *endianness = 1; 285b8e59f18Smalc return AUD_FMT_S32; 286b8e59f18Smalc case PA_SAMPLE_S32LE: 287b8e59f18Smalc *endianness = 0; 288b8e59f18Smalc return AUD_FMT_S32; 289b8e59f18Smalc default: 290b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 291b8e59f18Smalc return AUD_FMT_U8; 292b8e59f18Smalc } 293b8e59f18Smalc } 294b8e59f18Smalc 2951ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) 296b8e59f18Smalc { 297b8e59f18Smalc int error; 298b8e59f18Smalc static pa_sample_spec ss; 2991ea879e5Smalc struct audsettings obt_as = *as; 300b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 301b8e59f18Smalc 302b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 303b8e59f18Smalc ss.channels = as->nchannels; 304b8e59f18Smalc ss.rate = as->freq; 305b8e59f18Smalc 306b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 307b8e59f18Smalc 308b8e59f18Smalc pa->s = pa_simple_new ( 309b8e59f18Smalc conf.server, 310b8e59f18Smalc "qemu", 311b8e59f18Smalc PA_STREAM_PLAYBACK, 312b8e59f18Smalc conf.sink, 313b8e59f18Smalc "pcm.playback", 314b8e59f18Smalc &ss, 315b8e59f18Smalc NULL, /* channel map */ 316b8e59f18Smalc NULL, /* buffering attributes */ 317b8e59f18Smalc &error 318b8e59f18Smalc ); 319b8e59f18Smalc if (!pa->s) { 320b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 321b8e59f18Smalc goto fail1; 322b8e59f18Smalc } 323b8e59f18Smalc 324b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 325b8e59f18Smalc hw->samples = conf.samples; 326b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 327b8e59f18Smalc if (!pa->pcm_buf) { 328b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 329b8e59f18Smalc hw->samples << hw->info.shift); 330b8e59f18Smalc goto fail2; 331b8e59f18Smalc } 332b8e59f18Smalc 333b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 334b8e59f18Smalc goto fail3; 335b8e59f18Smalc } 336b8e59f18Smalc 337b8e59f18Smalc return 0; 338b8e59f18Smalc 339b8e59f18Smalc fail3: 3405d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 341b8e59f18Smalc pa->pcm_buf = NULL; 342b8e59f18Smalc fail2: 343b8e59f18Smalc pa_simple_free (pa->s); 344b8e59f18Smalc pa->s = NULL; 345b8e59f18Smalc fail1: 346b8e59f18Smalc return -1; 347b8e59f18Smalc } 348b8e59f18Smalc 3491ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) 350b8e59f18Smalc { 351b8e59f18Smalc int error; 352b8e59f18Smalc static pa_sample_spec ss; 3531ea879e5Smalc struct audsettings obt_as = *as; 354b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 355b8e59f18Smalc 356b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 357b8e59f18Smalc ss.channels = as->nchannels; 358b8e59f18Smalc ss.rate = as->freq; 359b8e59f18Smalc 360b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 361b8e59f18Smalc 362b8e59f18Smalc pa->s = pa_simple_new ( 363b8e59f18Smalc conf.server, 364b8e59f18Smalc "qemu", 365b8e59f18Smalc PA_STREAM_RECORD, 366b8e59f18Smalc conf.source, 367b8e59f18Smalc "pcm.capture", 368b8e59f18Smalc &ss, 369b8e59f18Smalc NULL, /* channel map */ 370b8e59f18Smalc NULL, /* buffering attributes */ 371b8e59f18Smalc &error 372b8e59f18Smalc ); 373b8e59f18Smalc if (!pa->s) { 374b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 375b8e59f18Smalc goto fail1; 376b8e59f18Smalc } 377b8e59f18Smalc 378b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 379b8e59f18Smalc hw->samples = conf.samples; 380b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 381b8e59f18Smalc if (!pa->pcm_buf) { 382b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 383b8e59f18Smalc hw->samples << hw->info.shift); 384b8e59f18Smalc goto fail2; 385b8e59f18Smalc } 386b8e59f18Smalc 387b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 388b8e59f18Smalc goto fail3; 389b8e59f18Smalc } 390b8e59f18Smalc 391b8e59f18Smalc return 0; 392b8e59f18Smalc 393b8e59f18Smalc fail3: 3945d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 395b8e59f18Smalc pa->pcm_buf = NULL; 396b8e59f18Smalc fail2: 397b8e59f18Smalc pa_simple_free (pa->s); 398b8e59f18Smalc pa->s = NULL; 399b8e59f18Smalc fail1: 400b8e59f18Smalc return -1; 401b8e59f18Smalc } 402b8e59f18Smalc 403b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 404b8e59f18Smalc { 405b8e59f18Smalc void *ret; 406b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 407b8e59f18Smalc 408b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 409b8e59f18Smalc pa->done = 1; 410b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 411b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 412b8e59f18Smalc 413b8e59f18Smalc if (pa->s) { 414b8e59f18Smalc pa_simple_free (pa->s); 415b8e59f18Smalc pa->s = NULL; 416b8e59f18Smalc } 417b8e59f18Smalc 418b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 419b8e59f18Smalc qemu_free (pa->pcm_buf); 420b8e59f18Smalc pa->pcm_buf = NULL; 421b8e59f18Smalc } 422b8e59f18Smalc 423b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 424b8e59f18Smalc { 425b8e59f18Smalc void *ret; 426b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 427b8e59f18Smalc 428b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 429b8e59f18Smalc pa->done = 1; 430b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 431b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 432b8e59f18Smalc 433b8e59f18Smalc if (pa->s) { 434b8e59f18Smalc pa_simple_free (pa->s); 435b8e59f18Smalc pa->s = NULL; 436b8e59f18Smalc } 437b8e59f18Smalc 438b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 439b8e59f18Smalc qemu_free (pa->pcm_buf); 440b8e59f18Smalc pa->pcm_buf = NULL; 441b8e59f18Smalc } 442b8e59f18Smalc 443b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 444b8e59f18Smalc { 445b8e59f18Smalc (void) hw; 446b8e59f18Smalc (void) cmd; 447b8e59f18Smalc return 0; 448b8e59f18Smalc } 449b8e59f18Smalc 450b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 451b8e59f18Smalc { 452b8e59f18Smalc (void) hw; 453b8e59f18Smalc (void) cmd; 454b8e59f18Smalc return 0; 455b8e59f18Smalc } 456b8e59f18Smalc 457b8e59f18Smalc /* common */ 458b8e59f18Smalc static void *qpa_audio_init (void) 459b8e59f18Smalc { 460b8e59f18Smalc return &conf; 461b8e59f18Smalc } 462b8e59f18Smalc 463b8e59f18Smalc static void qpa_audio_fini (void *opaque) 464b8e59f18Smalc { 465b8e59f18Smalc (void) opaque; 466b8e59f18Smalc } 467b8e59f18Smalc 468b8e59f18Smalc struct audio_option qpa_options[] = { 4692700efa3SJuan Quintela {.name = "SAMPLES", 4702700efa3SJuan Quintela .tag = AUD_OPT_INT, 4712700efa3SJuan Quintela .valp = &conf.samples, 4722700efa3SJuan Quintela .descr = "buffer size in samples"}, 4732700efa3SJuan Quintela {.name = "DIVISOR", 4742700efa3SJuan Quintela .tag = AUD_OPT_INT, 4752700efa3SJuan Quintela .valp = &conf.divisor, 4762700efa3SJuan Quintela .descr = "threshold divisor"}, 4772700efa3SJuan Quintela {.name = "SERVER", 4782700efa3SJuan Quintela .tag = AUD_OPT_STR, 4792700efa3SJuan Quintela .valp = &conf.server, 4802700efa3SJuan Quintela .descr = "server address"}, 4812700efa3SJuan Quintela {.name = "SINK", 4822700efa3SJuan Quintela .tag = AUD_OPT_STR, 4832700efa3SJuan Quintela .valp = &conf.sink, 4842700efa3SJuan Quintela .descr = "sink device name"}, 4852700efa3SJuan Quintela {.name = "SOURCE", 4862700efa3SJuan Quintela .tag = AUD_OPT_STR, 4872700efa3SJuan Quintela .valp = &conf.source, 4882700efa3SJuan Quintela .descr = "source device name"}, 4892700efa3SJuan Quintela { /* End of list */ } 490b8e59f18Smalc }; 491b8e59f18Smalc 49235f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 4931dd3e4d1SJuan Quintela .init_out = qpa_init_out, 4941dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 4951dd3e4d1SJuan Quintela .run_out = qpa_run_out, 4961dd3e4d1SJuan Quintela .write = qpa_write, 4971dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 4981dd3e4d1SJuan Quintela 4991dd3e4d1SJuan Quintela .init_in = qpa_init_in, 5001dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 5011dd3e4d1SJuan Quintela .run_in = qpa_run_in, 5021dd3e4d1SJuan Quintela .read = qpa_read, 5031dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 504b8e59f18Smalc }; 505b8e59f18Smalc 506b8e59f18Smalc struct audio_driver pa_audio_driver = { 507bee37f32SJuan Quintela .name = "pa", 508bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 509bee37f32SJuan Quintela .options = qpa_options, 510bee37f32SJuan Quintela .init = qpa_audio_init, 511bee37f32SJuan Quintela .fini = qpa_audio_fini, 512bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 513bee37f32SJuan Quintela .can_be_default = 0, 514bee37f32SJuan Quintela .max_voices_out = INT_MAX, 515bee37f32SJuan Quintela .max_voices_in = INT_MAX, 516bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 517bee37f32SJuan Quintela .voice_size_in = sizeof (PAVoiceIn) 518b8e59f18Smalc }; 519