1b8e59f18Smalc /* public domain */ 2b8e59f18Smalc #include "qemu-common.h" 3b8e59f18Smalc #include "audio.h" 4b8e59f18Smalc 5ea9ebc2cSMarc-André Lureau #include <pulse/pulseaudio.h> 6b8e59f18Smalc 7b8e59f18Smalc #define AUDIO_CAP "pulseaudio" 8b8e59f18Smalc #include "audio_int.h" 9b8e59f18Smalc #include "audio_pt_int.h" 10b8e59f18Smalc 11b8e59f18Smalc typedef struct { 12b8e59f18Smalc HWVoiceOut hw; 13b8e59f18Smalc int done; 14b8e59f18Smalc int live; 15b8e59f18Smalc int decr; 16b8e59f18Smalc int rpos; 17ea9ebc2cSMarc-André Lureau pa_stream *stream; 18b8e59f18Smalc void *pcm_buf; 19b8e59f18Smalc struct audio_pt pt; 20b8e59f18Smalc } PAVoiceOut; 21b8e59f18Smalc 22b8e59f18Smalc typedef struct { 23b8e59f18Smalc HWVoiceIn hw; 24b8e59f18Smalc int done; 25b8e59f18Smalc int dead; 26b8e59f18Smalc int incr; 27b8e59f18Smalc int wpos; 28ea9ebc2cSMarc-André Lureau pa_stream *stream; 29b8e59f18Smalc void *pcm_buf; 30b8e59f18Smalc struct audio_pt pt; 31ea9ebc2cSMarc-André Lureau const void *read_data; 32ea9ebc2cSMarc-André Lureau size_t read_index, read_length; 33b8e59f18Smalc } PAVoiceIn; 34b8e59f18Smalc 35ea9ebc2cSMarc-André Lureau typedef struct { 36b8e59f18Smalc int samples; 37b8e59f18Smalc char *server; 38b8e59f18Smalc char *sink; 39b8e59f18Smalc char *source; 40ea9ebc2cSMarc-André Lureau pa_threaded_mainloop *mainloop; 41ea9ebc2cSMarc-André Lureau pa_context *context; 42ea9ebc2cSMarc-André Lureau } paaudio; 43ea9ebc2cSMarc-André Lureau 44ea9ebc2cSMarc-André Lureau static paaudio glob_paaudio = { 45bf1064b5SGerd Hoffmann .samples = 4096, 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 59ea9ebc2cSMarc-André Lureau #define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ 60ea9ebc2cSMarc-André Lureau do { \ 61ea9ebc2cSMarc-André Lureau if (!(expression)) { \ 62ea9ebc2cSMarc-André Lureau if (rerror) { \ 63ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 64ea9ebc2cSMarc-André Lureau } \ 65ea9ebc2cSMarc-André Lureau goto label; \ 66ea9ebc2cSMarc-André Lureau } \ 67ea9ebc2cSMarc-André Lureau } while (0); 68ea9ebc2cSMarc-André Lureau 69ea9ebc2cSMarc-André Lureau #define CHECK_DEAD_GOTO(c, stream, rerror, label) \ 70ea9ebc2cSMarc-André Lureau do { \ 71ea9ebc2cSMarc-André Lureau if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ 72ea9ebc2cSMarc-André Lureau !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ 73ea9ebc2cSMarc-André Lureau if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ 74ea9ebc2cSMarc-André Lureau ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ 75ea9ebc2cSMarc-André Lureau if (rerror) { \ 76ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 77ea9ebc2cSMarc-André Lureau } \ 78ea9ebc2cSMarc-André Lureau } else { \ 79ea9ebc2cSMarc-André Lureau if (rerror) { \ 80ea9ebc2cSMarc-André Lureau *(rerror) = PA_ERR_BADSTATE; \ 81ea9ebc2cSMarc-André Lureau } \ 82ea9ebc2cSMarc-André Lureau } \ 83ea9ebc2cSMarc-André Lureau goto label; \ 84ea9ebc2cSMarc-André Lureau } \ 85ea9ebc2cSMarc-André Lureau } while (0); 86ea9ebc2cSMarc-André Lureau 87ea9ebc2cSMarc-André Lureau static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) 88ea9ebc2cSMarc-André Lureau { 89ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 90ea9ebc2cSMarc-André Lureau 91ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 92ea9ebc2cSMarc-André Lureau 93ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 94ea9ebc2cSMarc-André Lureau 95ea9ebc2cSMarc-André Lureau while (length > 0) { 96ea9ebc2cSMarc-André Lureau size_t l; 97ea9ebc2cSMarc-André Lureau 98ea9ebc2cSMarc-André Lureau while (!p->read_data) { 99ea9ebc2cSMarc-André Lureau int r; 100ea9ebc2cSMarc-André Lureau 101ea9ebc2cSMarc-André Lureau r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); 102ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 103ea9ebc2cSMarc-André Lureau 104ea9ebc2cSMarc-André Lureau if (!p->read_data) { 105ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 106ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 107ea9ebc2cSMarc-André Lureau } else { 108ea9ebc2cSMarc-André Lureau p->read_index = 0; 109ea9ebc2cSMarc-André Lureau } 110ea9ebc2cSMarc-André Lureau } 111ea9ebc2cSMarc-André Lureau 112ea9ebc2cSMarc-André Lureau l = p->read_length < length ? p->read_length : length; 113ea9ebc2cSMarc-André Lureau memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); 114ea9ebc2cSMarc-André Lureau 115ea9ebc2cSMarc-André Lureau data = (uint8_t *) data + l; 116ea9ebc2cSMarc-André Lureau length -= l; 117ea9ebc2cSMarc-André Lureau 118ea9ebc2cSMarc-André Lureau p->read_index += l; 119ea9ebc2cSMarc-André Lureau p->read_length -= l; 120ea9ebc2cSMarc-André Lureau 121ea9ebc2cSMarc-André Lureau if (!p->read_length) { 122ea9ebc2cSMarc-André Lureau int r; 123ea9ebc2cSMarc-André Lureau 124ea9ebc2cSMarc-André Lureau r = pa_stream_drop (p->stream); 125ea9ebc2cSMarc-André Lureau p->read_data = NULL; 126ea9ebc2cSMarc-André Lureau p->read_length = 0; 127ea9ebc2cSMarc-André Lureau p->read_index = 0; 128ea9ebc2cSMarc-André Lureau 129ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 130ea9ebc2cSMarc-André Lureau } 131ea9ebc2cSMarc-André Lureau } 132ea9ebc2cSMarc-André Lureau 133ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 134ea9ebc2cSMarc-André Lureau return 0; 135ea9ebc2cSMarc-André Lureau 136ea9ebc2cSMarc-André Lureau unlock_and_fail: 137ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 138ea9ebc2cSMarc-André Lureau return -1; 139ea9ebc2cSMarc-André Lureau } 140ea9ebc2cSMarc-André Lureau 141ea9ebc2cSMarc-André Lureau static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) 142ea9ebc2cSMarc-André Lureau { 143ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 144ea9ebc2cSMarc-André Lureau 145ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 146ea9ebc2cSMarc-André Lureau 147ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 148ea9ebc2cSMarc-André Lureau 149ea9ebc2cSMarc-André Lureau while (length > 0) { 150ea9ebc2cSMarc-André Lureau size_t l; 151ea9ebc2cSMarc-André Lureau int r; 152ea9ebc2cSMarc-André Lureau 153ea9ebc2cSMarc-André Lureau while (!(l = pa_stream_writable_size (p->stream))) { 154ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 155ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 156ea9ebc2cSMarc-André Lureau } 157ea9ebc2cSMarc-André Lureau 158ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); 159ea9ebc2cSMarc-André Lureau 160ea9ebc2cSMarc-André Lureau if (l > length) { 161ea9ebc2cSMarc-André Lureau l = length; 162ea9ebc2cSMarc-André Lureau } 163ea9ebc2cSMarc-André Lureau 164ea9ebc2cSMarc-André Lureau r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); 165ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); 166ea9ebc2cSMarc-André Lureau 167ea9ebc2cSMarc-André Lureau data = (const uint8_t *) data + l; 168ea9ebc2cSMarc-André Lureau length -= l; 169ea9ebc2cSMarc-André Lureau } 170ea9ebc2cSMarc-André Lureau 171ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 172ea9ebc2cSMarc-André Lureau return 0; 173ea9ebc2cSMarc-André Lureau 174ea9ebc2cSMarc-André Lureau unlock_and_fail: 175ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 176ea9ebc2cSMarc-André Lureau return -1; 177ea9ebc2cSMarc-André Lureau } 178ea9ebc2cSMarc-André Lureau 179b8e59f18Smalc static void *qpa_thread_out (void *arg) 180b8e59f18Smalc { 181b8e59f18Smalc PAVoiceOut *pa = arg; 182b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 183b8e59f18Smalc 184b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 185b8e59f18Smalc return NULL; 186b8e59f18Smalc } 187b8e59f18Smalc 188b8e59f18Smalc for (;;) { 189b8e59f18Smalc int decr, to_mix, rpos; 190b8e59f18Smalc 191b8e59f18Smalc for (;;) { 192b8e59f18Smalc if (pa->done) { 193b8e59f18Smalc goto exit; 194b8e59f18Smalc } 195b8e59f18Smalc 1966315633bSGerd Hoffmann if (pa->live > 0) { 197b8e59f18Smalc break; 198b8e59f18Smalc } 199b8e59f18Smalc 200b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 201b8e59f18Smalc goto exit; 202b8e59f18Smalc } 203b8e59f18Smalc } 204b8e59f18Smalc 205ea9ebc2cSMarc-André Lureau decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2); 2066315633bSGerd Hoffmann rpos = pa->rpos; 207b8e59f18Smalc 208b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 209b8e59f18Smalc return NULL; 210b8e59f18Smalc } 211b8e59f18Smalc 212b8e59f18Smalc while (to_mix) { 213b8e59f18Smalc int error; 214b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 2151ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 216b8e59f18Smalc 217b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 218b8e59f18Smalc 219ea9ebc2cSMarc-André Lureau if (qpa_simple_write (pa, pa->pcm_buf, 220b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 221b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 222b8e59f18Smalc return NULL; 223b8e59f18Smalc } 224b8e59f18Smalc 225b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 226b8e59f18Smalc to_mix -= chunk; 227b8e59f18Smalc } 228b8e59f18Smalc 229b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 230b8e59f18Smalc return NULL; 231b8e59f18Smalc } 232b8e59f18Smalc 233b8e59f18Smalc pa->rpos = rpos; 2346315633bSGerd Hoffmann pa->live -= decr; 235b8e59f18Smalc pa->decr += decr; 236b8e59f18Smalc } 237b8e59f18Smalc 238b8e59f18Smalc exit: 239b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 240b8e59f18Smalc return NULL; 241b8e59f18Smalc } 242b8e59f18Smalc 243bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 244b8e59f18Smalc { 245bdff253cSmalc int decr; 246b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 247b8e59f18Smalc 248b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 249b8e59f18Smalc return 0; 250b8e59f18Smalc } 251b8e59f18Smalc 252b8e59f18Smalc decr = audio_MIN (live, pa->decr); 253b8e59f18Smalc pa->decr -= decr; 254b8e59f18Smalc pa->live = live - decr; 255b8e59f18Smalc hw->rpos = pa->rpos; 256b8e59f18Smalc if (pa->live > 0) { 257b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 258b8e59f18Smalc } 259b8e59f18Smalc else { 260b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 261b8e59f18Smalc } 262b8e59f18Smalc return decr; 263b8e59f18Smalc } 264b8e59f18Smalc 265b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 266b8e59f18Smalc { 267b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 268b8e59f18Smalc } 269b8e59f18Smalc 270b8e59f18Smalc /* capture */ 271b8e59f18Smalc static void *qpa_thread_in (void *arg) 272b8e59f18Smalc { 273b8e59f18Smalc PAVoiceIn *pa = arg; 274b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 275b8e59f18Smalc 276b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 277b8e59f18Smalc return NULL; 278b8e59f18Smalc } 279b8e59f18Smalc 280b8e59f18Smalc for (;;) { 281b8e59f18Smalc int incr, to_grab, wpos; 282b8e59f18Smalc 283b8e59f18Smalc for (;;) { 284b8e59f18Smalc if (pa->done) { 285b8e59f18Smalc goto exit; 286b8e59f18Smalc } 287b8e59f18Smalc 2886315633bSGerd Hoffmann if (pa->dead > 0) { 289b8e59f18Smalc break; 290b8e59f18Smalc } 291b8e59f18Smalc 292b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 293b8e59f18Smalc goto exit; 294b8e59f18Smalc } 295b8e59f18Smalc } 296b8e59f18Smalc 297ea9ebc2cSMarc-André Lureau incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2); 2986315633bSGerd Hoffmann wpos = pa->wpos; 299b8e59f18Smalc 300b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 301b8e59f18Smalc return NULL; 302b8e59f18Smalc } 303b8e59f18Smalc 304b8e59f18Smalc while (to_grab) { 305b8e59f18Smalc int error; 306b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 307b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 308b8e59f18Smalc 309ea9ebc2cSMarc-André Lureau if (qpa_simple_read (pa, buf, 310b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 311b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 312b8e59f18Smalc return NULL; 313b8e59f18Smalc } 314b8e59f18Smalc 31500e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 316b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 317b8e59f18Smalc to_grab -= chunk; 318b8e59f18Smalc } 319b8e59f18Smalc 320b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 321b8e59f18Smalc return NULL; 322b8e59f18Smalc } 323b8e59f18Smalc 324b8e59f18Smalc pa->wpos = wpos; 325b8e59f18Smalc pa->dead -= incr; 326b8e59f18Smalc pa->incr += incr; 327b8e59f18Smalc } 328b8e59f18Smalc 329b8e59f18Smalc exit: 330b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 331b8e59f18Smalc return NULL; 332b8e59f18Smalc } 333b8e59f18Smalc 334b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 335b8e59f18Smalc { 336b8e59f18Smalc int live, incr, dead; 337b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 338b8e59f18Smalc 339b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 340b8e59f18Smalc return 0; 341b8e59f18Smalc } 342b8e59f18Smalc 343b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 344b8e59f18Smalc dead = hw->samples - live; 345b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 346b8e59f18Smalc pa->incr -= incr; 347b8e59f18Smalc pa->dead = dead - incr; 348b8e59f18Smalc hw->wpos = pa->wpos; 349b8e59f18Smalc if (pa->dead > 0) { 350b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 351b8e59f18Smalc } 352b8e59f18Smalc else { 353b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 354b8e59f18Smalc } 355b8e59f18Smalc return incr; 356b8e59f18Smalc } 357b8e59f18Smalc 358b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 359b8e59f18Smalc { 360b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 361b8e59f18Smalc } 362b8e59f18Smalc 363b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 364b8e59f18Smalc { 365b8e59f18Smalc int format; 366b8e59f18Smalc 367b8e59f18Smalc switch (afmt) { 368b8e59f18Smalc case AUD_FMT_S8: 369b8e59f18Smalc case AUD_FMT_U8: 370b8e59f18Smalc format = PA_SAMPLE_U8; 371b8e59f18Smalc break; 372b8e59f18Smalc case AUD_FMT_S16: 373b8e59f18Smalc case AUD_FMT_U16: 374b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 375b8e59f18Smalc break; 376b8e59f18Smalc case AUD_FMT_S32: 377b8e59f18Smalc case AUD_FMT_U32: 378b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 379b8e59f18Smalc break; 380b8e59f18Smalc default: 381b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 382b8e59f18Smalc format = PA_SAMPLE_U8; 383b8e59f18Smalc break; 384b8e59f18Smalc } 385b8e59f18Smalc return format; 386b8e59f18Smalc } 387b8e59f18Smalc 388b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 389b8e59f18Smalc { 390b8e59f18Smalc switch (fmt) { 391b8e59f18Smalc case PA_SAMPLE_U8: 392b8e59f18Smalc return AUD_FMT_U8; 393b8e59f18Smalc case PA_SAMPLE_S16BE: 394b8e59f18Smalc *endianness = 1; 395b8e59f18Smalc return AUD_FMT_S16; 396b8e59f18Smalc case PA_SAMPLE_S16LE: 397b8e59f18Smalc *endianness = 0; 398b8e59f18Smalc return AUD_FMT_S16; 399b8e59f18Smalc case PA_SAMPLE_S32BE: 400b8e59f18Smalc *endianness = 1; 401b8e59f18Smalc return AUD_FMT_S32; 402b8e59f18Smalc case PA_SAMPLE_S32LE: 403b8e59f18Smalc *endianness = 0; 404b8e59f18Smalc return AUD_FMT_S32; 405b8e59f18Smalc default: 406b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 407b8e59f18Smalc return AUD_FMT_U8; 408b8e59f18Smalc } 409b8e59f18Smalc } 410b8e59f18Smalc 411ea9ebc2cSMarc-André Lureau static void context_state_cb (pa_context *c, void *userdata) 412ea9ebc2cSMarc-André Lureau { 413ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 414ea9ebc2cSMarc-André Lureau 415ea9ebc2cSMarc-André Lureau switch (pa_context_get_state(c)) { 416ea9ebc2cSMarc-André Lureau case PA_CONTEXT_READY: 417ea9ebc2cSMarc-André Lureau case PA_CONTEXT_TERMINATED: 418ea9ebc2cSMarc-André Lureau case PA_CONTEXT_FAILED: 419ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 420ea9ebc2cSMarc-André Lureau break; 421ea9ebc2cSMarc-André Lureau 422ea9ebc2cSMarc-André Lureau case PA_CONTEXT_UNCONNECTED: 423ea9ebc2cSMarc-André Lureau case PA_CONTEXT_CONNECTING: 424ea9ebc2cSMarc-André Lureau case PA_CONTEXT_AUTHORIZING: 425ea9ebc2cSMarc-André Lureau case PA_CONTEXT_SETTING_NAME: 426ea9ebc2cSMarc-André Lureau break; 427ea9ebc2cSMarc-André Lureau } 428ea9ebc2cSMarc-André Lureau } 429ea9ebc2cSMarc-André Lureau 430ea9ebc2cSMarc-André Lureau static void stream_state_cb (pa_stream *s, void * userdata) 431ea9ebc2cSMarc-André Lureau { 432ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 433ea9ebc2cSMarc-André Lureau 434ea9ebc2cSMarc-André Lureau switch (pa_stream_get_state (s)) { 435ea9ebc2cSMarc-André Lureau 436ea9ebc2cSMarc-André Lureau case PA_STREAM_READY: 437ea9ebc2cSMarc-André Lureau case PA_STREAM_FAILED: 438ea9ebc2cSMarc-André Lureau case PA_STREAM_TERMINATED: 439ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 440ea9ebc2cSMarc-André Lureau break; 441ea9ebc2cSMarc-André Lureau 442ea9ebc2cSMarc-André Lureau case PA_STREAM_UNCONNECTED: 443ea9ebc2cSMarc-André Lureau case PA_STREAM_CREATING: 444ea9ebc2cSMarc-André Lureau break; 445ea9ebc2cSMarc-André Lureau } 446ea9ebc2cSMarc-André Lureau } 447ea9ebc2cSMarc-André Lureau 448ea9ebc2cSMarc-André Lureau static void stream_request_cb (pa_stream *s, size_t length, void *userdata) 449ea9ebc2cSMarc-André Lureau { 450ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 451ea9ebc2cSMarc-André Lureau 452ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 453ea9ebc2cSMarc-André Lureau } 454ea9ebc2cSMarc-André Lureau 455ea9ebc2cSMarc-André Lureau static pa_stream *qpa_simple_new ( 456ea9ebc2cSMarc-André Lureau const char *server, 457ea9ebc2cSMarc-André Lureau const char *name, 458ea9ebc2cSMarc-André Lureau pa_stream_direction_t dir, 459ea9ebc2cSMarc-André Lureau const char *dev, 460ea9ebc2cSMarc-André Lureau const char *stream_name, 461ea9ebc2cSMarc-André Lureau const pa_sample_spec *ss, 462ea9ebc2cSMarc-André Lureau const pa_channel_map *map, 463ea9ebc2cSMarc-André Lureau const pa_buffer_attr *attr, 464ea9ebc2cSMarc-André Lureau int *rerror) 465ea9ebc2cSMarc-André Lureau { 466ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 467ea9ebc2cSMarc-André Lureau int r; 468ea9ebc2cSMarc-André Lureau pa_stream *stream; 469ea9ebc2cSMarc-André Lureau 470ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 471ea9ebc2cSMarc-André Lureau 472ea9ebc2cSMarc-André Lureau stream = pa_stream_new (g->context, name, ss, map); 473ea9ebc2cSMarc-André Lureau if (!stream) { 474ea9ebc2cSMarc-André Lureau goto fail; 475ea9ebc2cSMarc-André Lureau } 476ea9ebc2cSMarc-André Lureau 477ea9ebc2cSMarc-André Lureau pa_stream_set_state_callback (stream, stream_state_cb, g); 478ea9ebc2cSMarc-André Lureau pa_stream_set_read_callback (stream, stream_request_cb, g); 479ea9ebc2cSMarc-André Lureau pa_stream_set_write_callback (stream, stream_request_cb, g); 480ea9ebc2cSMarc-André Lureau 481ea9ebc2cSMarc-André Lureau if (dir == PA_STREAM_PLAYBACK) { 482ea9ebc2cSMarc-André Lureau r = pa_stream_connect_playback (stream, dev, attr, 483ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 484ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 485ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); 486ea9ebc2cSMarc-André Lureau } else { 487ea9ebc2cSMarc-André Lureau r = pa_stream_connect_record (stream, dev, attr, 488ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 489ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 490ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE); 491ea9ebc2cSMarc-André Lureau } 492ea9ebc2cSMarc-André Lureau 493ea9ebc2cSMarc-André Lureau if (r < 0) { 494ea9ebc2cSMarc-André Lureau goto fail; 495ea9ebc2cSMarc-André Lureau } 496ea9ebc2cSMarc-André Lureau 497ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 498ea9ebc2cSMarc-André Lureau 499ea9ebc2cSMarc-André Lureau return stream; 500ea9ebc2cSMarc-André Lureau 501ea9ebc2cSMarc-André Lureau fail: 502ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 503ea9ebc2cSMarc-André Lureau 504ea9ebc2cSMarc-André Lureau if (stream) { 505ea9ebc2cSMarc-André Lureau pa_stream_unref (stream); 506ea9ebc2cSMarc-André Lureau } 507ea9ebc2cSMarc-André Lureau 508ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 509ea9ebc2cSMarc-André Lureau "stream_new() failed\n"); 510ea9ebc2cSMarc-André Lureau 511ea9ebc2cSMarc-André Lureau return NULL; 512ea9ebc2cSMarc-André Lureau } 513ea9ebc2cSMarc-André Lureau 5141ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) 515b8e59f18Smalc { 516b8e59f18Smalc int error; 517b8e59f18Smalc static pa_sample_spec ss; 518e6d16fa4SGerd Hoffmann static pa_buffer_attr ba; 5191ea879e5Smalc struct audsettings obt_as = *as; 520b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 521b8e59f18Smalc 522b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 523b8e59f18Smalc ss.channels = as->nchannels; 524b8e59f18Smalc ss.rate = as->freq; 525b8e59f18Smalc 526e6d16fa4SGerd Hoffmann /* 527e6d16fa4SGerd Hoffmann * qemu audio tick runs at 250 Hz (by default), so processing 528e6d16fa4SGerd Hoffmann * data chunks worth 4 ms of sound should be a good fit. 529e6d16fa4SGerd Hoffmann */ 530e6d16fa4SGerd Hoffmann ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); 531e6d16fa4SGerd Hoffmann ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); 532e6d16fa4SGerd Hoffmann ba.maxlength = -1; 533e6d16fa4SGerd Hoffmann ba.prebuf = -1; 534e6d16fa4SGerd Hoffmann 535b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 536b8e59f18Smalc 537ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 538ea9ebc2cSMarc-André Lureau glob_paaudio.server, 539b8e59f18Smalc "qemu", 540b8e59f18Smalc PA_STREAM_PLAYBACK, 541ea9ebc2cSMarc-André Lureau glob_paaudio.sink, 542b8e59f18Smalc "pcm.playback", 543b8e59f18Smalc &ss, 544b8e59f18Smalc NULL, /* channel map */ 545e6d16fa4SGerd Hoffmann &ba, /* buffering attributes */ 546b8e59f18Smalc &error 547b8e59f18Smalc ); 548ea9ebc2cSMarc-André Lureau if (!pa->stream) { 549b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 550b8e59f18Smalc goto fail1; 551b8e59f18Smalc } 552b8e59f18Smalc 553b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 554ea9ebc2cSMarc-André Lureau hw->samples = glob_paaudio.samples; 555b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 5566315633bSGerd Hoffmann pa->rpos = hw->rpos; 557b8e59f18Smalc if (!pa->pcm_buf) { 558b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 559b8e59f18Smalc hw->samples << hw->info.shift); 560b8e59f18Smalc goto fail2; 561b8e59f18Smalc } 562b8e59f18Smalc 563b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 564b8e59f18Smalc goto fail3; 565b8e59f18Smalc } 566b8e59f18Smalc 567b8e59f18Smalc return 0; 568b8e59f18Smalc 569b8e59f18Smalc fail3: 5707267c094SAnthony Liguori g_free (pa->pcm_buf); 571b8e59f18Smalc pa->pcm_buf = NULL; 572b8e59f18Smalc fail2: 573ea9ebc2cSMarc-André Lureau if (pa->stream) { 574ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 575ea9ebc2cSMarc-André Lureau pa->stream = NULL; 576ea9ebc2cSMarc-André Lureau } 577b8e59f18Smalc fail1: 578b8e59f18Smalc return -1; 579b8e59f18Smalc } 580b8e59f18Smalc 5811ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) 582b8e59f18Smalc { 583b8e59f18Smalc int error; 584b8e59f18Smalc static pa_sample_spec ss; 5851ea879e5Smalc struct audsettings obt_as = *as; 586b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 587b8e59f18Smalc 588b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 589b8e59f18Smalc ss.channels = as->nchannels; 590b8e59f18Smalc ss.rate = as->freq; 591b8e59f18Smalc 592b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 593b8e59f18Smalc 594ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 595ea9ebc2cSMarc-André Lureau glob_paaudio.server, 596b8e59f18Smalc "qemu", 597b8e59f18Smalc PA_STREAM_RECORD, 598ea9ebc2cSMarc-André Lureau glob_paaudio.source, 599b8e59f18Smalc "pcm.capture", 600b8e59f18Smalc &ss, 601b8e59f18Smalc NULL, /* channel map */ 602b8e59f18Smalc NULL, /* buffering attributes */ 603b8e59f18Smalc &error 604b8e59f18Smalc ); 605ea9ebc2cSMarc-André Lureau if (!pa->stream) { 606b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 607b8e59f18Smalc goto fail1; 608b8e59f18Smalc } 609b8e59f18Smalc 610b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 611ea9ebc2cSMarc-André Lureau hw->samples = glob_paaudio.samples; 612b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 6136315633bSGerd Hoffmann pa->wpos = hw->wpos; 614b8e59f18Smalc if (!pa->pcm_buf) { 615b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 616b8e59f18Smalc hw->samples << hw->info.shift); 617b8e59f18Smalc goto fail2; 618b8e59f18Smalc } 619b8e59f18Smalc 620b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 621b8e59f18Smalc goto fail3; 622b8e59f18Smalc } 623b8e59f18Smalc 624b8e59f18Smalc return 0; 625b8e59f18Smalc 626b8e59f18Smalc fail3: 6277267c094SAnthony Liguori g_free (pa->pcm_buf); 628b8e59f18Smalc pa->pcm_buf = NULL; 629b8e59f18Smalc fail2: 630ea9ebc2cSMarc-André Lureau if (pa->stream) { 631ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 632ea9ebc2cSMarc-André Lureau pa->stream = NULL; 633ea9ebc2cSMarc-André Lureau } 634b8e59f18Smalc fail1: 635b8e59f18Smalc return -1; 636b8e59f18Smalc } 637b8e59f18Smalc 638b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 639b8e59f18Smalc { 640b8e59f18Smalc void *ret; 641b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 642b8e59f18Smalc 643b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 644b8e59f18Smalc pa->done = 1; 645b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 646b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 647b8e59f18Smalc 648ea9ebc2cSMarc-André Lureau if (pa->stream) { 649ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 650ea9ebc2cSMarc-André Lureau pa->stream = NULL; 651b8e59f18Smalc } 652b8e59f18Smalc 653b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6547267c094SAnthony Liguori g_free (pa->pcm_buf); 655b8e59f18Smalc pa->pcm_buf = NULL; 656b8e59f18Smalc } 657b8e59f18Smalc 658b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 659b8e59f18Smalc { 660b8e59f18Smalc void *ret; 661b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 662b8e59f18Smalc 663b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 664b8e59f18Smalc pa->done = 1; 665b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 666b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 667b8e59f18Smalc 668ea9ebc2cSMarc-André Lureau if (pa->stream) { 669ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 670ea9ebc2cSMarc-André Lureau pa->stream = NULL; 671b8e59f18Smalc } 672b8e59f18Smalc 673b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6747267c094SAnthony Liguori g_free (pa->pcm_buf); 675b8e59f18Smalc pa->pcm_buf = NULL; 676b8e59f18Smalc } 677b8e59f18Smalc 678b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 679b8e59f18Smalc { 680*6e7a7f3dSMarc-André Lureau PAVoiceOut *pa = (PAVoiceOut *) hw; 681*6e7a7f3dSMarc-André Lureau pa_operation *op; 682*6e7a7f3dSMarc-André Lureau pa_cvolume v; 683*6e7a7f3dSMarc-André Lureau paaudio *g = &glob_paaudio; 684*6e7a7f3dSMarc-André Lureau 685*6e7a7f3dSMarc-André Lureau pa_cvolume_init (&v); 686*6e7a7f3dSMarc-André Lureau 687*6e7a7f3dSMarc-André Lureau switch (cmd) { 688*6e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 689*6e7a7f3dSMarc-André Lureau { 690*6e7a7f3dSMarc-André Lureau SWVoiceOut *sw; 691*6e7a7f3dSMarc-André Lureau va_list ap; 692*6e7a7f3dSMarc-André Lureau 693*6e7a7f3dSMarc-André Lureau va_start (ap, cmd); 694*6e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceOut *); 695*6e7a7f3dSMarc-André Lureau va_end (ap); 696*6e7a7f3dSMarc-André Lureau 697*6e7a7f3dSMarc-André Lureau v.channels = 2; 698*6e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 699*6e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 700*6e7a7f3dSMarc-André Lureau 701*6e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 702*6e7a7f3dSMarc-André Lureau 703*6e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_volume (g->context, 704*6e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 705*6e7a7f3dSMarc-André Lureau &v, NULL, NULL); 706*6e7a7f3dSMarc-André Lureau if (!op) 707*6e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 708*6e7a7f3dSMarc-André Lureau "set_sink_input_volume() failed\n"); 709*6e7a7f3dSMarc-André Lureau else 710*6e7a7f3dSMarc-André Lureau pa_operation_unref (op); 711*6e7a7f3dSMarc-André Lureau 712*6e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_mute (g->context, 713*6e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 714*6e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 715*6e7a7f3dSMarc-André Lureau if (!op) { 716*6e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 717*6e7a7f3dSMarc-André Lureau "set_sink_input_mute() failed\n"); 718*6e7a7f3dSMarc-André Lureau } else { 719*6e7a7f3dSMarc-André Lureau pa_operation_unref (op); 720*6e7a7f3dSMarc-André Lureau } 721*6e7a7f3dSMarc-André Lureau 722*6e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 723*6e7a7f3dSMarc-André Lureau } 724*6e7a7f3dSMarc-André Lureau } 725b8e59f18Smalc return 0; 726b8e59f18Smalc } 727b8e59f18Smalc 728b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 729b8e59f18Smalc { 730*6e7a7f3dSMarc-André Lureau PAVoiceIn *pa = (PAVoiceIn *) hw; 731*6e7a7f3dSMarc-André Lureau pa_operation *op; 732*6e7a7f3dSMarc-André Lureau pa_cvolume v; 733*6e7a7f3dSMarc-André Lureau paaudio *g = &glob_paaudio; 734*6e7a7f3dSMarc-André Lureau 735*6e7a7f3dSMarc-André Lureau pa_cvolume_init (&v); 736*6e7a7f3dSMarc-André Lureau 737*6e7a7f3dSMarc-André Lureau switch (cmd) { 738*6e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 739*6e7a7f3dSMarc-André Lureau { 740*6e7a7f3dSMarc-André Lureau SWVoiceIn *sw; 741*6e7a7f3dSMarc-André Lureau va_list ap; 742*6e7a7f3dSMarc-André Lureau 743*6e7a7f3dSMarc-André Lureau va_start (ap, cmd); 744*6e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceIn *); 745*6e7a7f3dSMarc-André Lureau va_end (ap); 746*6e7a7f3dSMarc-André Lureau 747*6e7a7f3dSMarc-André Lureau v.channels = 2; 748*6e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 749*6e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 750*6e7a7f3dSMarc-André Lureau 751*6e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 752*6e7a7f3dSMarc-André Lureau 753*6e7a7f3dSMarc-André Lureau /* FIXME: use the upcoming "set_source_output_{volume,mute}" */ 754*6e7a7f3dSMarc-André Lureau op = pa_context_set_source_volume_by_index (g->context, 755*6e7a7f3dSMarc-André Lureau pa_stream_get_device_index (pa->stream), 756*6e7a7f3dSMarc-André Lureau &v, NULL, NULL); 757*6e7a7f3dSMarc-André Lureau if (!op) { 758*6e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 759*6e7a7f3dSMarc-André Lureau "set_source_volume() failed\n"); 760*6e7a7f3dSMarc-André Lureau } else { 761*6e7a7f3dSMarc-André Lureau pa_operation_unref(op); 762*6e7a7f3dSMarc-André Lureau } 763*6e7a7f3dSMarc-André Lureau 764*6e7a7f3dSMarc-André Lureau op = pa_context_set_source_mute_by_index (g->context, 765*6e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 766*6e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 767*6e7a7f3dSMarc-André Lureau if (!op) { 768*6e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 769*6e7a7f3dSMarc-André Lureau "set_source_mute() failed\n"); 770*6e7a7f3dSMarc-André Lureau } else { 771*6e7a7f3dSMarc-André Lureau pa_operation_unref (op); 772*6e7a7f3dSMarc-André Lureau } 773*6e7a7f3dSMarc-André Lureau 774*6e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 775*6e7a7f3dSMarc-André Lureau } 776*6e7a7f3dSMarc-André Lureau } 777b8e59f18Smalc return 0; 778b8e59f18Smalc } 779b8e59f18Smalc 780b8e59f18Smalc /* common */ 781b8e59f18Smalc static void *qpa_audio_init (void) 782b8e59f18Smalc { 783ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 784ea9ebc2cSMarc-André Lureau 785ea9ebc2cSMarc-André Lureau g->mainloop = pa_threaded_mainloop_new (); 786ea9ebc2cSMarc-André Lureau if (!g->mainloop) { 787ea9ebc2cSMarc-André Lureau goto fail; 788ea9ebc2cSMarc-André Lureau } 789ea9ebc2cSMarc-André Lureau 790ea9ebc2cSMarc-André Lureau g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); 791ea9ebc2cSMarc-André Lureau if (!g->context) { 792ea9ebc2cSMarc-André Lureau goto fail; 793ea9ebc2cSMarc-André Lureau } 794ea9ebc2cSMarc-André Lureau 795ea9ebc2cSMarc-André Lureau pa_context_set_state_callback (g->context, context_state_cb, g); 796ea9ebc2cSMarc-André Lureau 797ea9ebc2cSMarc-André Lureau if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { 798ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 799ea9ebc2cSMarc-André Lureau "pa_context_connect() failed\n"); 800ea9ebc2cSMarc-André Lureau goto fail; 801ea9ebc2cSMarc-André Lureau } 802ea9ebc2cSMarc-André Lureau 803ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 804ea9ebc2cSMarc-André Lureau 805ea9ebc2cSMarc-André Lureau if (pa_threaded_mainloop_start (g->mainloop) < 0) { 806ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 807ea9ebc2cSMarc-André Lureau } 808ea9ebc2cSMarc-André Lureau 809ea9ebc2cSMarc-André Lureau for (;;) { 810ea9ebc2cSMarc-André Lureau pa_context_state_t state; 811ea9ebc2cSMarc-André Lureau 812ea9ebc2cSMarc-André Lureau state = pa_context_get_state (g->context); 813ea9ebc2cSMarc-André Lureau 814ea9ebc2cSMarc-André Lureau if (state == PA_CONTEXT_READY) { 815ea9ebc2cSMarc-André Lureau break; 816ea9ebc2cSMarc-André Lureau } 817ea9ebc2cSMarc-André Lureau 818ea9ebc2cSMarc-André Lureau if (!PA_CONTEXT_IS_GOOD (state)) { 819ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 820ea9ebc2cSMarc-André Lureau "Wrong context state\n"); 821ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 822ea9ebc2cSMarc-André Lureau } 823ea9ebc2cSMarc-André Lureau 824ea9ebc2cSMarc-André Lureau /* Wait until the context is ready */ 825ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 826ea9ebc2cSMarc-André Lureau } 827ea9ebc2cSMarc-André Lureau 828ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 829ea9ebc2cSMarc-André Lureau 830ea9ebc2cSMarc-André Lureau return &glob_paaudio; 831ea9ebc2cSMarc-André Lureau 832ea9ebc2cSMarc-André Lureau unlock_and_fail: 833ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 834ea9ebc2cSMarc-André Lureau fail: 835ea9ebc2cSMarc-André Lureau AUD_log (AUDIO_CAP, "Failed to initialize PA context"); 836ea9ebc2cSMarc-André Lureau return NULL; 837b8e59f18Smalc } 838b8e59f18Smalc 839b8e59f18Smalc static void qpa_audio_fini (void *opaque) 840b8e59f18Smalc { 841ea9ebc2cSMarc-André Lureau paaudio *g = opaque; 842ea9ebc2cSMarc-André Lureau 843ea9ebc2cSMarc-André Lureau if (g->mainloop) { 844ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_stop (g->mainloop); 845ea9ebc2cSMarc-André Lureau } 846ea9ebc2cSMarc-André Lureau 847ea9ebc2cSMarc-André Lureau if (g->context) { 848ea9ebc2cSMarc-André Lureau pa_context_disconnect (g->context); 849ea9ebc2cSMarc-André Lureau pa_context_unref (g->context); 850ea9ebc2cSMarc-André Lureau g->context = NULL; 851ea9ebc2cSMarc-André Lureau } 852ea9ebc2cSMarc-André Lureau 853ea9ebc2cSMarc-André Lureau if (g->mainloop) { 854ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_free (g->mainloop); 855ea9ebc2cSMarc-André Lureau } 856ea9ebc2cSMarc-André Lureau 857ea9ebc2cSMarc-André Lureau g->mainloop = NULL; 858b8e59f18Smalc } 859b8e59f18Smalc 860b8e59f18Smalc struct audio_option qpa_options[] = { 86198f9f48cSmalc { 86298f9f48cSmalc .name = "SAMPLES", 8632700efa3SJuan Quintela .tag = AUD_OPT_INT, 864ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.samples, 86598f9f48cSmalc .descr = "buffer size in samples" 86698f9f48cSmalc }, 86798f9f48cSmalc { 86898f9f48cSmalc .name = "SERVER", 8692700efa3SJuan Quintela .tag = AUD_OPT_STR, 870ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.server, 87198f9f48cSmalc .descr = "server address" 87298f9f48cSmalc }, 87398f9f48cSmalc { 87498f9f48cSmalc .name = "SINK", 8752700efa3SJuan Quintela .tag = AUD_OPT_STR, 876ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.sink, 87798f9f48cSmalc .descr = "sink device name" 87898f9f48cSmalc }, 87998f9f48cSmalc { 88098f9f48cSmalc .name = "SOURCE", 8812700efa3SJuan Quintela .tag = AUD_OPT_STR, 882ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.source, 88398f9f48cSmalc .descr = "source device name" 88498f9f48cSmalc }, 8852700efa3SJuan Quintela { /* End of list */ } 886b8e59f18Smalc }; 887b8e59f18Smalc 88835f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 8891dd3e4d1SJuan Quintela .init_out = qpa_init_out, 8901dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 8911dd3e4d1SJuan Quintela .run_out = qpa_run_out, 8921dd3e4d1SJuan Quintela .write = qpa_write, 8931dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 8941dd3e4d1SJuan Quintela 8951dd3e4d1SJuan Quintela .init_in = qpa_init_in, 8961dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 8971dd3e4d1SJuan Quintela .run_in = qpa_run_in, 8981dd3e4d1SJuan Quintela .read = qpa_read, 8991dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 900b8e59f18Smalc }; 901b8e59f18Smalc 902b8e59f18Smalc struct audio_driver pa_audio_driver = { 903bee37f32SJuan Quintela .name = "pa", 904bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 905bee37f32SJuan Quintela .options = qpa_options, 906bee37f32SJuan Quintela .init = qpa_audio_init, 907bee37f32SJuan Quintela .fini = qpa_audio_fini, 908bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 9091a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 910bee37f32SJuan Quintela .max_voices_out = INT_MAX, 911bee37f32SJuan Quintela .max_voices_in = INT_MAX, 912bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 913*6e7a7f3dSMarc-André Lureau .voice_size_in = sizeof (PAVoiceIn), 914*6e7a7f3dSMarc-André Lureau .ctl_caps = VOICE_VOLUME_CAP 915b8e59f18Smalc }; 916