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 = { 41b8e59f18Smalc 1024, 42b8e59f18Smalc 2, 43b8e59f18Smalc NULL, 44b8e59f18Smalc NULL, 45b8e59f18Smalc NULL 46b8e59f18Smalc }; 47b8e59f18Smalc 48b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) 49b8e59f18Smalc { 50b8e59f18Smalc va_list ap; 51b8e59f18Smalc 52b8e59f18Smalc va_start (ap, fmt); 53b8e59f18Smalc AUD_vlog (AUDIO_CAP, fmt, ap); 54b8e59f18Smalc va_end (ap); 55b8e59f18Smalc 56b8e59f18Smalc AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); 57b8e59f18Smalc } 58b8e59f18Smalc 59b8e59f18Smalc static void *qpa_thread_out (void *arg) 60b8e59f18Smalc { 61b8e59f18Smalc PAVoiceOut *pa = arg; 62b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 63b8e59f18Smalc int threshold; 64b8e59f18Smalc 65b8e59f18Smalc threshold = conf.divisor ? hw->samples / conf.divisor : 0; 66b8e59f18Smalc 67b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 68b8e59f18Smalc return NULL; 69b8e59f18Smalc } 70b8e59f18Smalc 71b8e59f18Smalc for (;;) { 72b8e59f18Smalc int decr, to_mix, rpos; 73b8e59f18Smalc 74b8e59f18Smalc for (;;) { 75b8e59f18Smalc if (pa->done) { 76b8e59f18Smalc goto exit; 77b8e59f18Smalc } 78b8e59f18Smalc 79b8e59f18Smalc if (pa->live > threshold) { 80b8e59f18Smalc break; 81b8e59f18Smalc } 82b8e59f18Smalc 83b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 84b8e59f18Smalc goto exit; 85b8e59f18Smalc } 86b8e59f18Smalc } 87b8e59f18Smalc 88b8e59f18Smalc decr = to_mix = pa->live; 89b8e59f18Smalc rpos = hw->rpos; 90b8e59f18Smalc 91b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 92b8e59f18Smalc return NULL; 93b8e59f18Smalc } 94b8e59f18Smalc 95b8e59f18Smalc while (to_mix) { 96b8e59f18Smalc int error; 97b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 981ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 99b8e59f18Smalc 100b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 101b8e59f18Smalc 102b8e59f18Smalc if (pa_simple_write (pa->s, pa->pcm_buf, 103b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 104b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 105b8e59f18Smalc return NULL; 106b8e59f18Smalc } 107b8e59f18Smalc 108b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 109b8e59f18Smalc to_mix -= chunk; 110b8e59f18Smalc } 111b8e59f18Smalc 112b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 113b8e59f18Smalc return NULL; 114b8e59f18Smalc } 115b8e59f18Smalc 116b8e59f18Smalc pa->rpos = rpos; 117b8e59f18Smalc pa->live -= decr; 118b8e59f18Smalc pa->decr += decr; 119b8e59f18Smalc } 120b8e59f18Smalc 121b8e59f18Smalc exit: 122b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 123b8e59f18Smalc return NULL; 124b8e59f18Smalc } 125b8e59f18Smalc 126b8e59f18Smalc static int qpa_run_out (HWVoiceOut *hw) 127b8e59f18Smalc { 128b8e59f18Smalc int live, decr; 129b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 130b8e59f18Smalc 131b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 132b8e59f18Smalc return 0; 133b8e59f18Smalc } 134b8e59f18Smalc 135b8e59f18Smalc live = audio_pcm_hw_get_live_out (hw); 136b8e59f18Smalc decr = audio_MIN (live, pa->decr); 137b8e59f18Smalc pa->decr -= decr; 138b8e59f18Smalc pa->live = live - decr; 139b8e59f18Smalc hw->rpos = pa->rpos; 140b8e59f18Smalc if (pa->live > 0) { 141b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 142b8e59f18Smalc } 143b8e59f18Smalc else { 144b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 145b8e59f18Smalc } 146b8e59f18Smalc return decr; 147b8e59f18Smalc } 148b8e59f18Smalc 149b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 150b8e59f18Smalc { 151b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 152b8e59f18Smalc } 153b8e59f18Smalc 154b8e59f18Smalc /* capture */ 155b8e59f18Smalc static void *qpa_thread_in (void *arg) 156b8e59f18Smalc { 157b8e59f18Smalc PAVoiceIn *pa = arg; 158b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 159b8e59f18Smalc int threshold; 160b8e59f18Smalc 161b8e59f18Smalc threshold = conf.divisor ? hw->samples / conf.divisor : 0; 162b8e59f18Smalc 163b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 164b8e59f18Smalc return NULL; 165b8e59f18Smalc } 166b8e59f18Smalc 167b8e59f18Smalc for (;;) { 168b8e59f18Smalc int incr, to_grab, wpos; 169b8e59f18Smalc 170b8e59f18Smalc for (;;) { 171b8e59f18Smalc if (pa->done) { 172b8e59f18Smalc goto exit; 173b8e59f18Smalc } 174b8e59f18Smalc 175b8e59f18Smalc if (pa->dead > threshold) { 176b8e59f18Smalc break; 177b8e59f18Smalc } 178b8e59f18Smalc 179b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 180b8e59f18Smalc goto exit; 181b8e59f18Smalc } 182b8e59f18Smalc } 183b8e59f18Smalc 184b8e59f18Smalc incr = to_grab = pa->dead; 185b8e59f18Smalc wpos = hw->wpos; 186b8e59f18Smalc 187b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 188b8e59f18Smalc return NULL; 189b8e59f18Smalc } 190b8e59f18Smalc 191b8e59f18Smalc while (to_grab) { 192b8e59f18Smalc int error; 193b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 194b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 195b8e59f18Smalc 196b8e59f18Smalc if (pa_simple_read (pa->s, buf, 197b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 198b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 199b8e59f18Smalc return NULL; 200b8e59f18Smalc } 201b8e59f18Smalc 202b8e59f18Smalc hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); 203b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 204b8e59f18Smalc to_grab -= chunk; 205b8e59f18Smalc } 206b8e59f18Smalc 207b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 208b8e59f18Smalc return NULL; 209b8e59f18Smalc } 210b8e59f18Smalc 211b8e59f18Smalc pa->wpos = wpos; 212b8e59f18Smalc pa->dead -= incr; 213b8e59f18Smalc pa->incr += incr; 214b8e59f18Smalc } 215b8e59f18Smalc 216b8e59f18Smalc exit: 217b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 218b8e59f18Smalc return NULL; 219b8e59f18Smalc } 220b8e59f18Smalc 221b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 222b8e59f18Smalc { 223b8e59f18Smalc int live, incr, dead; 224b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 225b8e59f18Smalc 226b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 227b8e59f18Smalc return 0; 228b8e59f18Smalc } 229b8e59f18Smalc 230b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 231b8e59f18Smalc dead = hw->samples - live; 232b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 233b8e59f18Smalc pa->incr -= incr; 234b8e59f18Smalc pa->dead = dead - incr; 235b8e59f18Smalc hw->wpos = pa->wpos; 236b8e59f18Smalc if (pa->dead > 0) { 237b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 238b8e59f18Smalc } 239b8e59f18Smalc else { 240b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 241b8e59f18Smalc } 242b8e59f18Smalc return incr; 243b8e59f18Smalc } 244b8e59f18Smalc 245b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 246b8e59f18Smalc { 247b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 248b8e59f18Smalc } 249b8e59f18Smalc 250b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 251b8e59f18Smalc { 252b8e59f18Smalc int format; 253b8e59f18Smalc 254b8e59f18Smalc switch (afmt) { 255b8e59f18Smalc case AUD_FMT_S8: 256b8e59f18Smalc case AUD_FMT_U8: 257b8e59f18Smalc format = PA_SAMPLE_U8; 258b8e59f18Smalc break; 259b8e59f18Smalc case AUD_FMT_S16: 260b8e59f18Smalc case AUD_FMT_U16: 261b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 262b8e59f18Smalc break; 263b8e59f18Smalc case AUD_FMT_S32: 264b8e59f18Smalc case AUD_FMT_U32: 265b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 266b8e59f18Smalc break; 267b8e59f18Smalc default: 268b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 269b8e59f18Smalc format = PA_SAMPLE_U8; 270b8e59f18Smalc break; 271b8e59f18Smalc } 272b8e59f18Smalc return format; 273b8e59f18Smalc } 274b8e59f18Smalc 275b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 276b8e59f18Smalc { 277b8e59f18Smalc switch (fmt) { 278b8e59f18Smalc case PA_SAMPLE_U8: 279b8e59f18Smalc return AUD_FMT_U8; 280b8e59f18Smalc case PA_SAMPLE_S16BE: 281b8e59f18Smalc *endianness = 1; 282b8e59f18Smalc return AUD_FMT_S16; 283b8e59f18Smalc case PA_SAMPLE_S16LE: 284b8e59f18Smalc *endianness = 0; 285b8e59f18Smalc return AUD_FMT_S16; 286b8e59f18Smalc case PA_SAMPLE_S32BE: 287b8e59f18Smalc *endianness = 1; 288b8e59f18Smalc return AUD_FMT_S32; 289b8e59f18Smalc case PA_SAMPLE_S32LE: 290b8e59f18Smalc *endianness = 0; 291b8e59f18Smalc return AUD_FMT_S32; 292b8e59f18Smalc default: 293b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 294b8e59f18Smalc return AUD_FMT_U8; 295b8e59f18Smalc } 296b8e59f18Smalc } 297b8e59f18Smalc 2981ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) 299b8e59f18Smalc { 300b8e59f18Smalc int error; 301b8e59f18Smalc static pa_sample_spec ss; 3021ea879e5Smalc struct audsettings obt_as = *as; 303b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 304b8e59f18Smalc 305b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 306b8e59f18Smalc ss.channels = as->nchannels; 307b8e59f18Smalc ss.rate = as->freq; 308b8e59f18Smalc 309b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 310b8e59f18Smalc 311b8e59f18Smalc pa->s = pa_simple_new ( 312b8e59f18Smalc conf.server, 313b8e59f18Smalc "qemu", 314b8e59f18Smalc PA_STREAM_PLAYBACK, 315b8e59f18Smalc conf.sink, 316b8e59f18Smalc "pcm.playback", 317b8e59f18Smalc &ss, 318b8e59f18Smalc NULL, /* channel map */ 319b8e59f18Smalc NULL, /* buffering attributes */ 320b8e59f18Smalc &error 321b8e59f18Smalc ); 322b8e59f18Smalc if (!pa->s) { 323b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 324b8e59f18Smalc goto fail1; 325b8e59f18Smalc } 326b8e59f18Smalc 327b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 328b8e59f18Smalc hw->samples = conf.samples; 329b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 330b8e59f18Smalc if (!pa->pcm_buf) { 331b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 332b8e59f18Smalc hw->samples << hw->info.shift); 333b8e59f18Smalc goto fail2; 334b8e59f18Smalc } 335b8e59f18Smalc 336b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 337b8e59f18Smalc goto fail3; 338b8e59f18Smalc } 339b8e59f18Smalc 340b8e59f18Smalc return 0; 341b8e59f18Smalc 342b8e59f18Smalc fail3: 3435d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 344b8e59f18Smalc pa->pcm_buf = NULL; 345b8e59f18Smalc fail2: 346b8e59f18Smalc pa_simple_free (pa->s); 347b8e59f18Smalc pa->s = NULL; 348b8e59f18Smalc fail1: 349b8e59f18Smalc return -1; 350b8e59f18Smalc } 351b8e59f18Smalc 3521ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) 353b8e59f18Smalc { 354b8e59f18Smalc int error; 355b8e59f18Smalc static pa_sample_spec ss; 3561ea879e5Smalc struct audsettings obt_as = *as; 357b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 358b8e59f18Smalc 359b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 360b8e59f18Smalc ss.channels = as->nchannels; 361b8e59f18Smalc ss.rate = as->freq; 362b8e59f18Smalc 363b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 364b8e59f18Smalc 365b8e59f18Smalc pa->s = pa_simple_new ( 366b8e59f18Smalc conf.server, 367b8e59f18Smalc "qemu", 368b8e59f18Smalc PA_STREAM_RECORD, 369b8e59f18Smalc conf.source, 370b8e59f18Smalc "pcm.capture", 371b8e59f18Smalc &ss, 372b8e59f18Smalc NULL, /* channel map */ 373b8e59f18Smalc NULL, /* buffering attributes */ 374b8e59f18Smalc &error 375b8e59f18Smalc ); 376b8e59f18Smalc if (!pa->s) { 377b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 378b8e59f18Smalc goto fail1; 379b8e59f18Smalc } 380b8e59f18Smalc 381b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 382b8e59f18Smalc hw->samples = conf.samples; 383b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 384b8e59f18Smalc if (!pa->pcm_buf) { 385b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 386b8e59f18Smalc hw->samples << hw->info.shift); 387b8e59f18Smalc goto fail2; 388b8e59f18Smalc } 389b8e59f18Smalc 390b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 391b8e59f18Smalc goto fail3; 392b8e59f18Smalc } 393b8e59f18Smalc 394b8e59f18Smalc return 0; 395b8e59f18Smalc 396b8e59f18Smalc fail3: 3975d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 398b8e59f18Smalc pa->pcm_buf = NULL; 399b8e59f18Smalc fail2: 400b8e59f18Smalc pa_simple_free (pa->s); 401b8e59f18Smalc pa->s = NULL; 402b8e59f18Smalc fail1: 403b8e59f18Smalc return -1; 404b8e59f18Smalc } 405b8e59f18Smalc 406b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 407b8e59f18Smalc { 408b8e59f18Smalc void *ret; 409b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 410b8e59f18Smalc 411b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 412b8e59f18Smalc pa->done = 1; 413b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 414b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 415b8e59f18Smalc 416b8e59f18Smalc if (pa->s) { 417b8e59f18Smalc pa_simple_free (pa->s); 418b8e59f18Smalc pa->s = NULL; 419b8e59f18Smalc } 420b8e59f18Smalc 421b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 422b8e59f18Smalc qemu_free (pa->pcm_buf); 423b8e59f18Smalc pa->pcm_buf = NULL; 424b8e59f18Smalc } 425b8e59f18Smalc 426b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 427b8e59f18Smalc { 428b8e59f18Smalc void *ret; 429b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 430b8e59f18Smalc 431b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 432b8e59f18Smalc pa->done = 1; 433b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 434b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 435b8e59f18Smalc 436b8e59f18Smalc if (pa->s) { 437b8e59f18Smalc pa_simple_free (pa->s); 438b8e59f18Smalc pa->s = NULL; 439b8e59f18Smalc } 440b8e59f18Smalc 441b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 442b8e59f18Smalc qemu_free (pa->pcm_buf); 443b8e59f18Smalc pa->pcm_buf = NULL; 444b8e59f18Smalc } 445b8e59f18Smalc 446b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 447b8e59f18Smalc { 448b8e59f18Smalc (void) hw; 449b8e59f18Smalc (void) cmd; 450b8e59f18Smalc return 0; 451b8e59f18Smalc } 452b8e59f18Smalc 453b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 454b8e59f18Smalc { 455b8e59f18Smalc (void) hw; 456b8e59f18Smalc (void) cmd; 457b8e59f18Smalc return 0; 458b8e59f18Smalc } 459b8e59f18Smalc 460b8e59f18Smalc /* common */ 461b8e59f18Smalc static void *qpa_audio_init (void) 462b8e59f18Smalc { 463b8e59f18Smalc return &conf; 464b8e59f18Smalc } 465b8e59f18Smalc 466b8e59f18Smalc static void qpa_audio_fini (void *opaque) 467b8e59f18Smalc { 468b8e59f18Smalc (void) opaque; 469b8e59f18Smalc } 470b8e59f18Smalc 471b8e59f18Smalc struct audio_option qpa_options[] = { 472b8e59f18Smalc {"SAMPLES", AUD_OPT_INT, &conf.samples, 473b8e59f18Smalc "buffer size in samples", NULL, 0}, 474b8e59f18Smalc 475b8e59f18Smalc {"DIVISOR", AUD_OPT_INT, &conf.divisor, 476b8e59f18Smalc "threshold divisor", NULL, 0}, 477b8e59f18Smalc 478b8e59f18Smalc {"SERVER", AUD_OPT_STR, &conf.server, 479b8e59f18Smalc "server address", NULL, 0}, 480b8e59f18Smalc 481b8e59f18Smalc {"SINK", AUD_OPT_STR, &conf.sink, 482b8e59f18Smalc "sink device name", NULL, 0}, 483b8e59f18Smalc 484b8e59f18Smalc {"SOURCE", AUD_OPT_STR, &conf.source, 485b8e59f18Smalc "source device name", NULL, 0}, 486b8e59f18Smalc 487b8e59f18Smalc {NULL, 0, NULL, NULL, NULL, 0} 488b8e59f18Smalc }; 489b8e59f18Smalc 49035f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 491b8e59f18Smalc qpa_init_out, 492b8e59f18Smalc qpa_fini_out, 493b8e59f18Smalc qpa_run_out, 494b8e59f18Smalc qpa_write, 495b8e59f18Smalc qpa_ctl_out, 496b8e59f18Smalc qpa_init_in, 497b8e59f18Smalc qpa_fini_in, 498b8e59f18Smalc qpa_run_in, 499b8e59f18Smalc qpa_read, 500b8e59f18Smalc qpa_ctl_in 501b8e59f18Smalc }; 502b8e59f18Smalc 503b8e59f18Smalc struct audio_driver pa_audio_driver = { 504*bee37f32SJuan Quintela .name = "pa", 505*bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 506*bee37f32SJuan Quintela .options = qpa_options, 507*bee37f32SJuan Quintela .init = qpa_audio_init, 508*bee37f32SJuan Quintela .fini = qpa_audio_fini, 509*bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 510*bee37f32SJuan Quintela .can_be_default = 0, 511*bee37f32SJuan Quintela .max_voices_out = INT_MAX, 512*bee37f32SJuan Quintela .max_voices_in = INT_MAX, 513*bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 514*bee37f32SJuan Quintela .voice_size_in = sizeof (PAVoiceIn) 515b8e59f18Smalc }; 516