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 61b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 62b8e59f18Smalc return NULL; 63b8e59f18Smalc } 64b8e59f18Smalc 65b8e59f18Smalc for (;;) { 66b8e59f18Smalc int decr, to_mix, rpos; 67b8e59f18Smalc 68b8e59f18Smalc for (;;) { 69b8e59f18Smalc if (pa->done) { 70b8e59f18Smalc goto exit; 71b8e59f18Smalc } 72b8e59f18Smalc 736315633bSGerd Hoffmann if (pa->live > 0) { 74b8e59f18Smalc break; 75b8e59f18Smalc } 76b8e59f18Smalc 77b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 78b8e59f18Smalc goto exit; 79b8e59f18Smalc } 80b8e59f18Smalc } 81b8e59f18Smalc 826315633bSGerd Hoffmann decr = to_mix = audio_MIN (pa->live, conf.samples >> 2); 836315633bSGerd Hoffmann rpos = pa->rpos; 84b8e59f18Smalc 85b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 86b8e59f18Smalc return NULL; 87b8e59f18Smalc } 88b8e59f18Smalc 89b8e59f18Smalc while (to_mix) { 90b8e59f18Smalc int error; 91b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 921ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 93b8e59f18Smalc 94b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 95b8e59f18Smalc 96b8e59f18Smalc if (pa_simple_write (pa->s, pa->pcm_buf, 97b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 98b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 99b8e59f18Smalc return NULL; 100b8e59f18Smalc } 101b8e59f18Smalc 102b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 103b8e59f18Smalc to_mix -= chunk; 104b8e59f18Smalc } 105b8e59f18Smalc 106b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 107b8e59f18Smalc return NULL; 108b8e59f18Smalc } 109b8e59f18Smalc 110b8e59f18Smalc pa->rpos = rpos; 1116315633bSGerd Hoffmann pa->live -= decr; 112b8e59f18Smalc pa->decr += decr; 113b8e59f18Smalc } 114b8e59f18Smalc 115b8e59f18Smalc exit: 116b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 117b8e59f18Smalc return NULL; 118b8e59f18Smalc } 119b8e59f18Smalc 120bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 121b8e59f18Smalc { 122bdff253cSmalc int decr; 123b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 124b8e59f18Smalc 125b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 126b8e59f18Smalc return 0; 127b8e59f18Smalc } 128b8e59f18Smalc 129b8e59f18Smalc decr = audio_MIN (live, pa->decr); 130b8e59f18Smalc pa->decr -= decr; 131b8e59f18Smalc pa->live = live - decr; 132b8e59f18Smalc hw->rpos = pa->rpos; 133b8e59f18Smalc if (pa->live > 0) { 134b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 135b8e59f18Smalc } 136b8e59f18Smalc else { 137b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 138b8e59f18Smalc } 139b8e59f18Smalc return decr; 140b8e59f18Smalc } 141b8e59f18Smalc 142b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 143b8e59f18Smalc { 144b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 145b8e59f18Smalc } 146b8e59f18Smalc 147b8e59f18Smalc /* capture */ 148b8e59f18Smalc static void *qpa_thread_in (void *arg) 149b8e59f18Smalc { 150b8e59f18Smalc PAVoiceIn *pa = arg; 151b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 152b8e59f18Smalc 153b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 154b8e59f18Smalc return NULL; 155b8e59f18Smalc } 156b8e59f18Smalc 157b8e59f18Smalc for (;;) { 158b8e59f18Smalc int incr, to_grab, wpos; 159b8e59f18Smalc 160b8e59f18Smalc for (;;) { 161b8e59f18Smalc if (pa->done) { 162b8e59f18Smalc goto exit; 163b8e59f18Smalc } 164b8e59f18Smalc 1656315633bSGerd Hoffmann if (pa->dead > 0) { 166b8e59f18Smalc break; 167b8e59f18Smalc } 168b8e59f18Smalc 169b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 170b8e59f18Smalc goto exit; 171b8e59f18Smalc } 172b8e59f18Smalc } 173b8e59f18Smalc 1746315633bSGerd Hoffmann incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2); 1756315633bSGerd Hoffmann wpos = pa->wpos; 176b8e59f18Smalc 177b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 178b8e59f18Smalc return NULL; 179b8e59f18Smalc } 180b8e59f18Smalc 181b8e59f18Smalc while (to_grab) { 182b8e59f18Smalc int error; 183b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 184b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 185b8e59f18Smalc 186b8e59f18Smalc if (pa_simple_read (pa->s, buf, 187b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 188b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 189b8e59f18Smalc return NULL; 190b8e59f18Smalc } 191b8e59f18Smalc 19200e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 193b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 194b8e59f18Smalc to_grab -= chunk; 195b8e59f18Smalc } 196b8e59f18Smalc 197b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 198b8e59f18Smalc return NULL; 199b8e59f18Smalc } 200b8e59f18Smalc 201b8e59f18Smalc pa->wpos = wpos; 202b8e59f18Smalc pa->dead -= incr; 203b8e59f18Smalc pa->incr += incr; 204b8e59f18Smalc } 205b8e59f18Smalc 206b8e59f18Smalc exit: 207b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 208b8e59f18Smalc return NULL; 209b8e59f18Smalc } 210b8e59f18Smalc 211b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 212b8e59f18Smalc { 213b8e59f18Smalc int live, incr, dead; 214b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 215b8e59f18Smalc 216b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 217b8e59f18Smalc return 0; 218b8e59f18Smalc } 219b8e59f18Smalc 220b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 221b8e59f18Smalc dead = hw->samples - live; 222b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 223b8e59f18Smalc pa->incr -= incr; 224b8e59f18Smalc pa->dead = dead - incr; 225b8e59f18Smalc hw->wpos = pa->wpos; 226b8e59f18Smalc if (pa->dead > 0) { 227b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 228b8e59f18Smalc } 229b8e59f18Smalc else { 230b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 231b8e59f18Smalc } 232b8e59f18Smalc return incr; 233b8e59f18Smalc } 234b8e59f18Smalc 235b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 236b8e59f18Smalc { 237b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 238b8e59f18Smalc } 239b8e59f18Smalc 240b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 241b8e59f18Smalc { 242b8e59f18Smalc int format; 243b8e59f18Smalc 244b8e59f18Smalc switch (afmt) { 245b8e59f18Smalc case AUD_FMT_S8: 246b8e59f18Smalc case AUD_FMT_U8: 247b8e59f18Smalc format = PA_SAMPLE_U8; 248b8e59f18Smalc break; 249b8e59f18Smalc case AUD_FMT_S16: 250b8e59f18Smalc case AUD_FMT_U16: 251b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 252b8e59f18Smalc break; 253b8e59f18Smalc case AUD_FMT_S32: 254b8e59f18Smalc case AUD_FMT_U32: 255b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 256b8e59f18Smalc break; 257b8e59f18Smalc default: 258b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 259b8e59f18Smalc format = PA_SAMPLE_U8; 260b8e59f18Smalc break; 261b8e59f18Smalc } 262b8e59f18Smalc return format; 263b8e59f18Smalc } 264b8e59f18Smalc 265b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 266b8e59f18Smalc { 267b8e59f18Smalc switch (fmt) { 268b8e59f18Smalc case PA_SAMPLE_U8: 269b8e59f18Smalc return AUD_FMT_U8; 270b8e59f18Smalc case PA_SAMPLE_S16BE: 271b8e59f18Smalc *endianness = 1; 272b8e59f18Smalc return AUD_FMT_S16; 273b8e59f18Smalc case PA_SAMPLE_S16LE: 274b8e59f18Smalc *endianness = 0; 275b8e59f18Smalc return AUD_FMT_S16; 276b8e59f18Smalc case PA_SAMPLE_S32BE: 277b8e59f18Smalc *endianness = 1; 278b8e59f18Smalc return AUD_FMT_S32; 279b8e59f18Smalc case PA_SAMPLE_S32LE: 280b8e59f18Smalc *endianness = 0; 281b8e59f18Smalc return AUD_FMT_S32; 282b8e59f18Smalc default: 283b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 284b8e59f18Smalc return AUD_FMT_U8; 285b8e59f18Smalc } 286b8e59f18Smalc } 287b8e59f18Smalc 2881ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) 289b8e59f18Smalc { 290b8e59f18Smalc int error; 291b8e59f18Smalc static pa_sample_spec ss; 292*e6d16fa4SGerd Hoffmann static pa_buffer_attr ba; 2931ea879e5Smalc struct audsettings obt_as = *as; 294b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 295b8e59f18Smalc 296b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 297b8e59f18Smalc ss.channels = as->nchannels; 298b8e59f18Smalc ss.rate = as->freq; 299b8e59f18Smalc 300*e6d16fa4SGerd Hoffmann /* 301*e6d16fa4SGerd Hoffmann * qemu audio tick runs at 250 Hz (by default), so processing 302*e6d16fa4SGerd Hoffmann * data chunks worth 4 ms of sound should be a good fit. 303*e6d16fa4SGerd Hoffmann */ 304*e6d16fa4SGerd Hoffmann ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); 305*e6d16fa4SGerd Hoffmann ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); 306*e6d16fa4SGerd Hoffmann ba.maxlength = -1; 307*e6d16fa4SGerd Hoffmann ba.prebuf = -1; 308*e6d16fa4SGerd Hoffmann 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 */ 319*e6d16fa4SGerd Hoffmann &ba, /* 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); 3306315633bSGerd Hoffmann pa->rpos = hw->rpos; 331b8e59f18Smalc if (!pa->pcm_buf) { 332b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 333b8e59f18Smalc hw->samples << hw->info.shift); 334b8e59f18Smalc goto fail2; 335b8e59f18Smalc } 336b8e59f18Smalc 337b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 338b8e59f18Smalc goto fail3; 339b8e59f18Smalc } 340b8e59f18Smalc 341b8e59f18Smalc return 0; 342b8e59f18Smalc 343b8e59f18Smalc fail3: 3445d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 345b8e59f18Smalc pa->pcm_buf = NULL; 346b8e59f18Smalc fail2: 347b8e59f18Smalc pa_simple_free (pa->s); 348b8e59f18Smalc pa->s = NULL; 349b8e59f18Smalc fail1: 350b8e59f18Smalc return -1; 351b8e59f18Smalc } 352b8e59f18Smalc 3531ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) 354b8e59f18Smalc { 355b8e59f18Smalc int error; 356b8e59f18Smalc static pa_sample_spec ss; 3571ea879e5Smalc struct audsettings obt_as = *as; 358b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 359b8e59f18Smalc 360b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 361b8e59f18Smalc ss.channels = as->nchannels; 362b8e59f18Smalc ss.rate = as->freq; 363b8e59f18Smalc 364b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 365b8e59f18Smalc 366b8e59f18Smalc pa->s = pa_simple_new ( 367b8e59f18Smalc conf.server, 368b8e59f18Smalc "qemu", 369b8e59f18Smalc PA_STREAM_RECORD, 370b8e59f18Smalc conf.source, 371b8e59f18Smalc "pcm.capture", 372b8e59f18Smalc &ss, 373b8e59f18Smalc NULL, /* channel map */ 374b8e59f18Smalc NULL, /* buffering attributes */ 375b8e59f18Smalc &error 376b8e59f18Smalc ); 377b8e59f18Smalc if (!pa->s) { 378b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 379b8e59f18Smalc goto fail1; 380b8e59f18Smalc } 381b8e59f18Smalc 382b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 383b8e59f18Smalc hw->samples = conf.samples; 384b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 3856315633bSGerd Hoffmann pa->wpos = hw->wpos; 386b8e59f18Smalc if (!pa->pcm_buf) { 387b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 388b8e59f18Smalc hw->samples << hw->info.shift); 389b8e59f18Smalc goto fail2; 390b8e59f18Smalc } 391b8e59f18Smalc 392b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 393b8e59f18Smalc goto fail3; 394b8e59f18Smalc } 395b8e59f18Smalc 396b8e59f18Smalc return 0; 397b8e59f18Smalc 398b8e59f18Smalc fail3: 3995d928867SJean-Christophe Dubois qemu_free (pa->pcm_buf); 400b8e59f18Smalc pa->pcm_buf = NULL; 401b8e59f18Smalc fail2: 402b8e59f18Smalc pa_simple_free (pa->s); 403b8e59f18Smalc pa->s = NULL; 404b8e59f18Smalc fail1: 405b8e59f18Smalc return -1; 406b8e59f18Smalc } 407b8e59f18Smalc 408b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 409b8e59f18Smalc { 410b8e59f18Smalc void *ret; 411b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 412b8e59f18Smalc 413b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 414b8e59f18Smalc pa->done = 1; 415b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 416b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 417b8e59f18Smalc 418b8e59f18Smalc if (pa->s) { 419b8e59f18Smalc pa_simple_free (pa->s); 420b8e59f18Smalc pa->s = NULL; 421b8e59f18Smalc } 422b8e59f18Smalc 423b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 424b8e59f18Smalc qemu_free (pa->pcm_buf); 425b8e59f18Smalc pa->pcm_buf = NULL; 426b8e59f18Smalc } 427b8e59f18Smalc 428b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 429b8e59f18Smalc { 430b8e59f18Smalc void *ret; 431b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 432b8e59f18Smalc 433b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 434b8e59f18Smalc pa->done = 1; 435b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 436b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 437b8e59f18Smalc 438b8e59f18Smalc if (pa->s) { 439b8e59f18Smalc pa_simple_free (pa->s); 440b8e59f18Smalc pa->s = NULL; 441b8e59f18Smalc } 442b8e59f18Smalc 443b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 444b8e59f18Smalc qemu_free (pa->pcm_buf); 445b8e59f18Smalc pa->pcm_buf = NULL; 446b8e59f18Smalc } 447b8e59f18Smalc 448b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 449b8e59f18Smalc { 450b8e59f18Smalc (void) hw; 451b8e59f18Smalc (void) cmd; 452b8e59f18Smalc return 0; 453b8e59f18Smalc } 454b8e59f18Smalc 455b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 456b8e59f18Smalc { 457b8e59f18Smalc (void) hw; 458b8e59f18Smalc (void) cmd; 459b8e59f18Smalc return 0; 460b8e59f18Smalc } 461b8e59f18Smalc 462b8e59f18Smalc /* common */ 463b8e59f18Smalc static void *qpa_audio_init (void) 464b8e59f18Smalc { 465b8e59f18Smalc return &conf; 466b8e59f18Smalc } 467b8e59f18Smalc 468b8e59f18Smalc static void qpa_audio_fini (void *opaque) 469b8e59f18Smalc { 470b8e59f18Smalc (void) opaque; 471b8e59f18Smalc } 472b8e59f18Smalc 473b8e59f18Smalc struct audio_option qpa_options[] = { 47498f9f48cSmalc { 47598f9f48cSmalc .name = "SAMPLES", 4762700efa3SJuan Quintela .tag = AUD_OPT_INT, 4772700efa3SJuan Quintela .valp = &conf.samples, 47898f9f48cSmalc .descr = "buffer size in samples" 47998f9f48cSmalc }, 48098f9f48cSmalc { 48198f9f48cSmalc .name = "DIVISOR", 4822700efa3SJuan Quintela .tag = AUD_OPT_INT, 4832700efa3SJuan Quintela .valp = &conf.divisor, 48498f9f48cSmalc .descr = "threshold divisor" 48598f9f48cSmalc }, 48698f9f48cSmalc { 48798f9f48cSmalc .name = "SERVER", 4882700efa3SJuan Quintela .tag = AUD_OPT_STR, 4892700efa3SJuan Quintela .valp = &conf.server, 49098f9f48cSmalc .descr = "server address" 49198f9f48cSmalc }, 49298f9f48cSmalc { 49398f9f48cSmalc .name = "SINK", 4942700efa3SJuan Quintela .tag = AUD_OPT_STR, 4952700efa3SJuan Quintela .valp = &conf.sink, 49698f9f48cSmalc .descr = "sink device name" 49798f9f48cSmalc }, 49898f9f48cSmalc { 49998f9f48cSmalc .name = "SOURCE", 5002700efa3SJuan Quintela .tag = AUD_OPT_STR, 5012700efa3SJuan Quintela .valp = &conf.source, 50298f9f48cSmalc .descr = "source device name" 50398f9f48cSmalc }, 5042700efa3SJuan Quintela { /* End of list */ } 505b8e59f18Smalc }; 506b8e59f18Smalc 50735f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 5081dd3e4d1SJuan Quintela .init_out = qpa_init_out, 5091dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 5101dd3e4d1SJuan Quintela .run_out = qpa_run_out, 5111dd3e4d1SJuan Quintela .write = qpa_write, 5121dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 5131dd3e4d1SJuan Quintela 5141dd3e4d1SJuan Quintela .init_in = qpa_init_in, 5151dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 5161dd3e4d1SJuan Quintela .run_in = qpa_run_in, 5171dd3e4d1SJuan Quintela .read = qpa_read, 5181dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 519b8e59f18Smalc }; 520b8e59f18Smalc 521b8e59f18Smalc struct audio_driver pa_audio_driver = { 522bee37f32SJuan Quintela .name = "pa", 523bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 524bee37f32SJuan Quintela .options = qpa_options, 525bee37f32SJuan Quintela .init = qpa_audio_init, 526bee37f32SJuan Quintela .fini = qpa_audio_fini, 527bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 5281a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 529bee37f32SJuan Quintela .max_voices_out = INT_MAX, 530bee37f32SJuan Quintela .max_voices_in = INT_MAX, 531bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 532bee37f32SJuan Quintela .voice_size_in = sizeof (PAVoiceIn) 533b8e59f18Smalc }; 534