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 598f473dd1SGerd Hoffmann #ifndef PA_CONTEXT_IS_GOOD 608f473dd1SGerd Hoffmann static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) 618f473dd1SGerd Hoffmann { 628f473dd1SGerd Hoffmann return 638f473dd1SGerd Hoffmann x == PA_CONTEXT_CONNECTING || 648f473dd1SGerd Hoffmann x == PA_CONTEXT_AUTHORIZING || 658f473dd1SGerd Hoffmann x == PA_CONTEXT_SETTING_NAME || 668f473dd1SGerd Hoffmann x == PA_CONTEXT_READY; 678f473dd1SGerd Hoffmann } 688f473dd1SGerd Hoffmann #endif 698f473dd1SGerd Hoffmann 708f473dd1SGerd Hoffmann #ifndef PA_STREAM_IS_GOOD 718f473dd1SGerd Hoffmann static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) 728f473dd1SGerd Hoffmann { 738f473dd1SGerd Hoffmann return 748f473dd1SGerd Hoffmann x == PA_STREAM_CREATING || 758f473dd1SGerd Hoffmann x == PA_STREAM_READY; 768f473dd1SGerd Hoffmann } 778f473dd1SGerd Hoffmann #endif 788f473dd1SGerd Hoffmann 79ea9ebc2cSMarc-André Lureau #define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ 80ea9ebc2cSMarc-André Lureau do { \ 81ea9ebc2cSMarc-André Lureau if (!(expression)) { \ 82ea9ebc2cSMarc-André Lureau if (rerror) { \ 83ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 84ea9ebc2cSMarc-André Lureau } \ 85ea9ebc2cSMarc-André Lureau goto label; \ 86ea9ebc2cSMarc-André Lureau } \ 87ea9ebc2cSMarc-André Lureau } while (0); 88ea9ebc2cSMarc-André Lureau 89ea9ebc2cSMarc-André Lureau #define CHECK_DEAD_GOTO(c, stream, rerror, label) \ 90ea9ebc2cSMarc-André Lureau do { \ 91ea9ebc2cSMarc-André Lureau if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ 92ea9ebc2cSMarc-André Lureau !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ 93ea9ebc2cSMarc-André Lureau if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ 94ea9ebc2cSMarc-André Lureau ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ 95ea9ebc2cSMarc-André Lureau if (rerror) { \ 96ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 97ea9ebc2cSMarc-André Lureau } \ 98ea9ebc2cSMarc-André Lureau } else { \ 99ea9ebc2cSMarc-André Lureau if (rerror) { \ 100ea9ebc2cSMarc-André Lureau *(rerror) = PA_ERR_BADSTATE; \ 101ea9ebc2cSMarc-André Lureau } \ 102ea9ebc2cSMarc-André Lureau } \ 103ea9ebc2cSMarc-André Lureau goto label; \ 104ea9ebc2cSMarc-André Lureau } \ 105ea9ebc2cSMarc-André Lureau } while (0); 106ea9ebc2cSMarc-André Lureau 107ea9ebc2cSMarc-André Lureau static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) 108ea9ebc2cSMarc-André Lureau { 109ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 110ea9ebc2cSMarc-André Lureau 111ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 112ea9ebc2cSMarc-André Lureau 113ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 114ea9ebc2cSMarc-André Lureau 115ea9ebc2cSMarc-André Lureau while (length > 0) { 116ea9ebc2cSMarc-André Lureau size_t l; 117ea9ebc2cSMarc-André Lureau 118ea9ebc2cSMarc-André Lureau while (!p->read_data) { 119ea9ebc2cSMarc-André Lureau int r; 120ea9ebc2cSMarc-André Lureau 121ea9ebc2cSMarc-André Lureau r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); 122ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 123ea9ebc2cSMarc-André Lureau 124ea9ebc2cSMarc-André Lureau if (!p->read_data) { 125ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 126ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 127ea9ebc2cSMarc-André Lureau } else { 128ea9ebc2cSMarc-André Lureau p->read_index = 0; 129ea9ebc2cSMarc-André Lureau } 130ea9ebc2cSMarc-André Lureau } 131ea9ebc2cSMarc-André Lureau 132ea9ebc2cSMarc-André Lureau l = p->read_length < length ? p->read_length : length; 133ea9ebc2cSMarc-André Lureau memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); 134ea9ebc2cSMarc-André Lureau 135ea9ebc2cSMarc-André Lureau data = (uint8_t *) data + l; 136ea9ebc2cSMarc-André Lureau length -= l; 137ea9ebc2cSMarc-André Lureau 138ea9ebc2cSMarc-André Lureau p->read_index += l; 139ea9ebc2cSMarc-André Lureau p->read_length -= l; 140ea9ebc2cSMarc-André Lureau 141ea9ebc2cSMarc-André Lureau if (!p->read_length) { 142ea9ebc2cSMarc-André Lureau int r; 143ea9ebc2cSMarc-André Lureau 144ea9ebc2cSMarc-André Lureau r = pa_stream_drop (p->stream); 145ea9ebc2cSMarc-André Lureau p->read_data = NULL; 146ea9ebc2cSMarc-André Lureau p->read_length = 0; 147ea9ebc2cSMarc-André Lureau p->read_index = 0; 148ea9ebc2cSMarc-André Lureau 149ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 150ea9ebc2cSMarc-André Lureau } 151ea9ebc2cSMarc-André Lureau } 152ea9ebc2cSMarc-André Lureau 153ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 154ea9ebc2cSMarc-André Lureau return 0; 155ea9ebc2cSMarc-André Lureau 156ea9ebc2cSMarc-André Lureau unlock_and_fail: 157ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 158ea9ebc2cSMarc-André Lureau return -1; 159ea9ebc2cSMarc-André Lureau } 160ea9ebc2cSMarc-André Lureau 161ea9ebc2cSMarc-André Lureau static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) 162ea9ebc2cSMarc-André Lureau { 163ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 164ea9ebc2cSMarc-André Lureau 165ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 166ea9ebc2cSMarc-André Lureau 167ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 168ea9ebc2cSMarc-André Lureau 169ea9ebc2cSMarc-André Lureau while (length > 0) { 170ea9ebc2cSMarc-André Lureau size_t l; 171ea9ebc2cSMarc-André Lureau int r; 172ea9ebc2cSMarc-André Lureau 173ea9ebc2cSMarc-André Lureau while (!(l = pa_stream_writable_size (p->stream))) { 174ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 175ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 176ea9ebc2cSMarc-André Lureau } 177ea9ebc2cSMarc-André Lureau 178ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); 179ea9ebc2cSMarc-André Lureau 180ea9ebc2cSMarc-André Lureau if (l > length) { 181ea9ebc2cSMarc-André Lureau l = length; 182ea9ebc2cSMarc-André Lureau } 183ea9ebc2cSMarc-André Lureau 184ea9ebc2cSMarc-André Lureau r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); 185ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); 186ea9ebc2cSMarc-André Lureau 187ea9ebc2cSMarc-André Lureau data = (const uint8_t *) data + l; 188ea9ebc2cSMarc-André Lureau length -= l; 189ea9ebc2cSMarc-André Lureau } 190ea9ebc2cSMarc-André Lureau 191ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 192ea9ebc2cSMarc-André Lureau return 0; 193ea9ebc2cSMarc-André Lureau 194ea9ebc2cSMarc-André Lureau unlock_and_fail: 195ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 196ea9ebc2cSMarc-André Lureau return -1; 197ea9ebc2cSMarc-André Lureau } 198ea9ebc2cSMarc-André Lureau 199b8e59f18Smalc static void *qpa_thread_out (void *arg) 200b8e59f18Smalc { 201b8e59f18Smalc PAVoiceOut *pa = arg; 202b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 203b8e59f18Smalc 204b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 205b8e59f18Smalc return NULL; 206b8e59f18Smalc } 207b8e59f18Smalc 208b8e59f18Smalc for (;;) { 209b8e59f18Smalc int decr, to_mix, rpos; 210b8e59f18Smalc 211b8e59f18Smalc for (;;) { 212b8e59f18Smalc if (pa->done) { 213b8e59f18Smalc goto exit; 214b8e59f18Smalc } 215b8e59f18Smalc 2166315633bSGerd Hoffmann if (pa->live > 0) { 217b8e59f18Smalc break; 218b8e59f18Smalc } 219b8e59f18Smalc 220b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 221b8e59f18Smalc goto exit; 222b8e59f18Smalc } 223b8e59f18Smalc } 224b8e59f18Smalc 225ea9ebc2cSMarc-André Lureau decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2); 2266315633bSGerd Hoffmann rpos = pa->rpos; 227b8e59f18Smalc 228b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 229b8e59f18Smalc return NULL; 230b8e59f18Smalc } 231b8e59f18Smalc 232b8e59f18Smalc while (to_mix) { 233b8e59f18Smalc int error; 234b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 2351ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 236b8e59f18Smalc 237b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 238b8e59f18Smalc 239ea9ebc2cSMarc-André Lureau if (qpa_simple_write (pa, pa->pcm_buf, 240b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 241b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 242b8e59f18Smalc return NULL; 243b8e59f18Smalc } 244b8e59f18Smalc 245b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 246b8e59f18Smalc to_mix -= chunk; 247b8e59f18Smalc } 248b8e59f18Smalc 249b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 250b8e59f18Smalc return NULL; 251b8e59f18Smalc } 252b8e59f18Smalc 253b8e59f18Smalc pa->rpos = rpos; 2546315633bSGerd Hoffmann pa->live -= decr; 255b8e59f18Smalc pa->decr += decr; 256b8e59f18Smalc } 257b8e59f18Smalc 258b8e59f18Smalc exit: 259b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 260b8e59f18Smalc return NULL; 261b8e59f18Smalc } 262b8e59f18Smalc 263bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 264b8e59f18Smalc { 265bdff253cSmalc int decr; 266b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 267b8e59f18Smalc 268b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 269b8e59f18Smalc return 0; 270b8e59f18Smalc } 271b8e59f18Smalc 272b8e59f18Smalc decr = audio_MIN (live, pa->decr); 273b8e59f18Smalc pa->decr -= decr; 274b8e59f18Smalc pa->live = live - decr; 275b8e59f18Smalc hw->rpos = pa->rpos; 276b8e59f18Smalc if (pa->live > 0) { 277b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 278b8e59f18Smalc } 279b8e59f18Smalc else { 280b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 281b8e59f18Smalc } 282b8e59f18Smalc return decr; 283b8e59f18Smalc } 284b8e59f18Smalc 285b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 286b8e59f18Smalc { 287b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 288b8e59f18Smalc } 289b8e59f18Smalc 290b8e59f18Smalc /* capture */ 291b8e59f18Smalc static void *qpa_thread_in (void *arg) 292b8e59f18Smalc { 293b8e59f18Smalc PAVoiceIn *pa = arg; 294b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 295b8e59f18Smalc 296b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 297b8e59f18Smalc return NULL; 298b8e59f18Smalc } 299b8e59f18Smalc 300b8e59f18Smalc for (;;) { 301b8e59f18Smalc int incr, to_grab, wpos; 302b8e59f18Smalc 303b8e59f18Smalc for (;;) { 304b8e59f18Smalc if (pa->done) { 305b8e59f18Smalc goto exit; 306b8e59f18Smalc } 307b8e59f18Smalc 3086315633bSGerd Hoffmann if (pa->dead > 0) { 309b8e59f18Smalc break; 310b8e59f18Smalc } 311b8e59f18Smalc 312b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 313b8e59f18Smalc goto exit; 314b8e59f18Smalc } 315b8e59f18Smalc } 316b8e59f18Smalc 317ea9ebc2cSMarc-André Lureau incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2); 3186315633bSGerd Hoffmann wpos = pa->wpos; 319b8e59f18Smalc 320b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 321b8e59f18Smalc return NULL; 322b8e59f18Smalc } 323b8e59f18Smalc 324b8e59f18Smalc while (to_grab) { 325b8e59f18Smalc int error; 326b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 327b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 328b8e59f18Smalc 329ea9ebc2cSMarc-André Lureau if (qpa_simple_read (pa, buf, 330b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 331b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 332b8e59f18Smalc return NULL; 333b8e59f18Smalc } 334b8e59f18Smalc 33500e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 336b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 337b8e59f18Smalc to_grab -= chunk; 338b8e59f18Smalc } 339b8e59f18Smalc 340b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 341b8e59f18Smalc return NULL; 342b8e59f18Smalc } 343b8e59f18Smalc 344b8e59f18Smalc pa->wpos = wpos; 345b8e59f18Smalc pa->dead -= incr; 346b8e59f18Smalc pa->incr += incr; 347b8e59f18Smalc } 348b8e59f18Smalc 349b8e59f18Smalc exit: 350b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 351b8e59f18Smalc return NULL; 352b8e59f18Smalc } 353b8e59f18Smalc 354b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 355b8e59f18Smalc { 356b8e59f18Smalc int live, incr, dead; 357b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 358b8e59f18Smalc 359b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 360b8e59f18Smalc return 0; 361b8e59f18Smalc } 362b8e59f18Smalc 363b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 364b8e59f18Smalc dead = hw->samples - live; 365b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 366b8e59f18Smalc pa->incr -= incr; 367b8e59f18Smalc pa->dead = dead - incr; 368b8e59f18Smalc hw->wpos = pa->wpos; 369b8e59f18Smalc if (pa->dead > 0) { 370b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 371b8e59f18Smalc } 372b8e59f18Smalc else { 373b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 374b8e59f18Smalc } 375b8e59f18Smalc return incr; 376b8e59f18Smalc } 377b8e59f18Smalc 378b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 379b8e59f18Smalc { 380b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 381b8e59f18Smalc } 382b8e59f18Smalc 383b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 384b8e59f18Smalc { 385b8e59f18Smalc int format; 386b8e59f18Smalc 387b8e59f18Smalc switch (afmt) { 388b8e59f18Smalc case AUD_FMT_S8: 389b8e59f18Smalc case AUD_FMT_U8: 390b8e59f18Smalc format = PA_SAMPLE_U8; 391b8e59f18Smalc break; 392b8e59f18Smalc case AUD_FMT_S16: 393b8e59f18Smalc case AUD_FMT_U16: 394b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 395b8e59f18Smalc break; 396b8e59f18Smalc case AUD_FMT_S32: 397b8e59f18Smalc case AUD_FMT_U32: 398b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 399b8e59f18Smalc break; 400b8e59f18Smalc default: 401b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 402b8e59f18Smalc format = PA_SAMPLE_U8; 403b8e59f18Smalc break; 404b8e59f18Smalc } 405b8e59f18Smalc return format; 406b8e59f18Smalc } 407b8e59f18Smalc 408b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 409b8e59f18Smalc { 410b8e59f18Smalc switch (fmt) { 411b8e59f18Smalc case PA_SAMPLE_U8: 412b8e59f18Smalc return AUD_FMT_U8; 413b8e59f18Smalc case PA_SAMPLE_S16BE: 414b8e59f18Smalc *endianness = 1; 415b8e59f18Smalc return AUD_FMT_S16; 416b8e59f18Smalc case PA_SAMPLE_S16LE: 417b8e59f18Smalc *endianness = 0; 418b8e59f18Smalc return AUD_FMT_S16; 419b8e59f18Smalc case PA_SAMPLE_S32BE: 420b8e59f18Smalc *endianness = 1; 421b8e59f18Smalc return AUD_FMT_S32; 422b8e59f18Smalc case PA_SAMPLE_S32LE: 423b8e59f18Smalc *endianness = 0; 424b8e59f18Smalc return AUD_FMT_S32; 425b8e59f18Smalc default: 426b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 427b8e59f18Smalc return AUD_FMT_U8; 428b8e59f18Smalc } 429b8e59f18Smalc } 430b8e59f18Smalc 431ea9ebc2cSMarc-André Lureau static void context_state_cb (pa_context *c, void *userdata) 432ea9ebc2cSMarc-André Lureau { 433ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 434ea9ebc2cSMarc-André Lureau 435ea9ebc2cSMarc-André Lureau switch (pa_context_get_state(c)) { 436ea9ebc2cSMarc-André Lureau case PA_CONTEXT_READY: 437ea9ebc2cSMarc-André Lureau case PA_CONTEXT_TERMINATED: 438ea9ebc2cSMarc-André Lureau case PA_CONTEXT_FAILED: 439ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 440ea9ebc2cSMarc-André Lureau break; 441ea9ebc2cSMarc-André Lureau 442ea9ebc2cSMarc-André Lureau case PA_CONTEXT_UNCONNECTED: 443ea9ebc2cSMarc-André Lureau case PA_CONTEXT_CONNECTING: 444ea9ebc2cSMarc-André Lureau case PA_CONTEXT_AUTHORIZING: 445ea9ebc2cSMarc-André Lureau case PA_CONTEXT_SETTING_NAME: 446ea9ebc2cSMarc-André Lureau break; 447ea9ebc2cSMarc-André Lureau } 448ea9ebc2cSMarc-André Lureau } 449ea9ebc2cSMarc-André Lureau 450ea9ebc2cSMarc-André Lureau static void stream_state_cb (pa_stream *s, void * userdata) 451ea9ebc2cSMarc-André Lureau { 452ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 453ea9ebc2cSMarc-André Lureau 454ea9ebc2cSMarc-André Lureau switch (pa_stream_get_state (s)) { 455ea9ebc2cSMarc-André Lureau 456ea9ebc2cSMarc-André Lureau case PA_STREAM_READY: 457ea9ebc2cSMarc-André Lureau case PA_STREAM_FAILED: 458ea9ebc2cSMarc-André Lureau case PA_STREAM_TERMINATED: 459ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 460ea9ebc2cSMarc-André Lureau break; 461ea9ebc2cSMarc-André Lureau 462ea9ebc2cSMarc-André Lureau case PA_STREAM_UNCONNECTED: 463ea9ebc2cSMarc-André Lureau case PA_STREAM_CREATING: 464ea9ebc2cSMarc-André Lureau break; 465ea9ebc2cSMarc-André Lureau } 466ea9ebc2cSMarc-André Lureau } 467ea9ebc2cSMarc-André Lureau 468ea9ebc2cSMarc-André Lureau static void stream_request_cb (pa_stream *s, size_t length, void *userdata) 469ea9ebc2cSMarc-André Lureau { 470ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 471ea9ebc2cSMarc-André Lureau 472ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 473ea9ebc2cSMarc-André Lureau } 474ea9ebc2cSMarc-André Lureau 475ea9ebc2cSMarc-André Lureau static pa_stream *qpa_simple_new ( 476ea9ebc2cSMarc-André Lureau const char *server, 477ea9ebc2cSMarc-André Lureau const char *name, 478ea9ebc2cSMarc-André Lureau pa_stream_direction_t dir, 479ea9ebc2cSMarc-André Lureau const char *dev, 480ea9ebc2cSMarc-André Lureau const char *stream_name, 481ea9ebc2cSMarc-André Lureau const pa_sample_spec *ss, 482ea9ebc2cSMarc-André Lureau const pa_channel_map *map, 483ea9ebc2cSMarc-André Lureau const pa_buffer_attr *attr, 484ea9ebc2cSMarc-André Lureau int *rerror) 485ea9ebc2cSMarc-André Lureau { 486ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 487ea9ebc2cSMarc-André Lureau int r; 488ea9ebc2cSMarc-André Lureau pa_stream *stream; 489ea9ebc2cSMarc-André Lureau 490ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 491ea9ebc2cSMarc-André Lureau 492ea9ebc2cSMarc-André Lureau stream = pa_stream_new (g->context, name, ss, map); 493ea9ebc2cSMarc-André Lureau if (!stream) { 494ea9ebc2cSMarc-André Lureau goto fail; 495ea9ebc2cSMarc-André Lureau } 496ea9ebc2cSMarc-André Lureau 497ea9ebc2cSMarc-André Lureau pa_stream_set_state_callback (stream, stream_state_cb, g); 498ea9ebc2cSMarc-André Lureau pa_stream_set_read_callback (stream, stream_request_cb, g); 499ea9ebc2cSMarc-André Lureau pa_stream_set_write_callback (stream, stream_request_cb, g); 500ea9ebc2cSMarc-André Lureau 501ea9ebc2cSMarc-André Lureau if (dir == PA_STREAM_PLAYBACK) { 502ea9ebc2cSMarc-André Lureau r = pa_stream_connect_playback (stream, dev, attr, 503ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5048f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 505ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5068f473dd1SGerd Hoffmann #endif 507ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); 508ea9ebc2cSMarc-André Lureau } else { 509ea9ebc2cSMarc-André Lureau r = pa_stream_connect_record (stream, dev, attr, 510ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5118f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 512ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5138f473dd1SGerd Hoffmann #endif 514ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE); 515ea9ebc2cSMarc-André Lureau } 516ea9ebc2cSMarc-André Lureau 517ea9ebc2cSMarc-André Lureau if (r < 0) { 518ea9ebc2cSMarc-André Lureau goto fail; 519ea9ebc2cSMarc-André Lureau } 520ea9ebc2cSMarc-André Lureau 521ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 522ea9ebc2cSMarc-André Lureau 523ea9ebc2cSMarc-André Lureau return stream; 524ea9ebc2cSMarc-André Lureau 525ea9ebc2cSMarc-André Lureau fail: 526ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 527ea9ebc2cSMarc-André Lureau 528ea9ebc2cSMarc-André Lureau if (stream) { 529ea9ebc2cSMarc-André Lureau pa_stream_unref (stream); 530ea9ebc2cSMarc-André Lureau } 531ea9ebc2cSMarc-André Lureau 532d6c05bbfSGerd Hoffmann *rerror = pa_context_errno (g->context); 533ea9ebc2cSMarc-André Lureau 534ea9ebc2cSMarc-André Lureau return NULL; 535ea9ebc2cSMarc-André Lureau } 536ea9ebc2cSMarc-André Lureau 537*5706db1dSKővágó, Zoltán static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, 538*5706db1dSKővágó, Zoltán void *drv_opaque) 539b8e59f18Smalc { 540b8e59f18Smalc int error; 541b8e59f18Smalc static pa_sample_spec ss; 542e6d16fa4SGerd Hoffmann static pa_buffer_attr ba; 5431ea879e5Smalc struct audsettings obt_as = *as; 544b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 545b8e59f18Smalc 546b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 547b8e59f18Smalc ss.channels = as->nchannels; 548b8e59f18Smalc ss.rate = as->freq; 549b8e59f18Smalc 550e6d16fa4SGerd Hoffmann /* 5510e8ae611SGerd Hoffmann * qemu audio tick runs at 100 Hz (by default), so processing 5520e8ae611SGerd Hoffmann * data chunks worth 10 ms of sound should be a good fit. 553e6d16fa4SGerd Hoffmann */ 5540e8ae611SGerd Hoffmann ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); 5550e8ae611SGerd Hoffmann ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); 556e6d16fa4SGerd Hoffmann ba.maxlength = -1; 557e6d16fa4SGerd Hoffmann ba.prebuf = -1; 558e6d16fa4SGerd Hoffmann 559b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 560b8e59f18Smalc 561ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 562ea9ebc2cSMarc-André Lureau glob_paaudio.server, 563b8e59f18Smalc "qemu", 564b8e59f18Smalc PA_STREAM_PLAYBACK, 565ea9ebc2cSMarc-André Lureau glob_paaudio.sink, 566b8e59f18Smalc "pcm.playback", 567b8e59f18Smalc &ss, 568b8e59f18Smalc NULL, /* channel map */ 569e6d16fa4SGerd Hoffmann &ba, /* buffering attributes */ 570b8e59f18Smalc &error 571b8e59f18Smalc ); 572ea9ebc2cSMarc-André Lureau if (!pa->stream) { 573b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 574b8e59f18Smalc goto fail1; 575b8e59f18Smalc } 576b8e59f18Smalc 577b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 578ea9ebc2cSMarc-André Lureau hw->samples = glob_paaudio.samples; 579b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 5806315633bSGerd Hoffmann pa->rpos = hw->rpos; 581b8e59f18Smalc if (!pa->pcm_buf) { 582b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 583b8e59f18Smalc hw->samples << hw->info.shift); 584b8e59f18Smalc goto fail2; 585b8e59f18Smalc } 586b8e59f18Smalc 587b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 588b8e59f18Smalc goto fail3; 589b8e59f18Smalc } 590b8e59f18Smalc 591b8e59f18Smalc return 0; 592b8e59f18Smalc 593b8e59f18Smalc fail3: 5947267c094SAnthony Liguori g_free (pa->pcm_buf); 595b8e59f18Smalc pa->pcm_buf = NULL; 596b8e59f18Smalc fail2: 597ea9ebc2cSMarc-André Lureau if (pa->stream) { 598ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 599ea9ebc2cSMarc-André Lureau pa->stream = NULL; 600ea9ebc2cSMarc-André Lureau } 601b8e59f18Smalc fail1: 602b8e59f18Smalc return -1; 603b8e59f18Smalc } 604b8e59f18Smalc 605*5706db1dSKővágó, Zoltán static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 606b8e59f18Smalc { 607b8e59f18Smalc int error; 608b8e59f18Smalc static pa_sample_spec ss; 6091ea879e5Smalc struct audsettings obt_as = *as; 610b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 611b8e59f18Smalc 612b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 613b8e59f18Smalc ss.channels = as->nchannels; 614b8e59f18Smalc ss.rate = as->freq; 615b8e59f18Smalc 616b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 617b8e59f18Smalc 618ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 619ea9ebc2cSMarc-André Lureau glob_paaudio.server, 620b8e59f18Smalc "qemu", 621b8e59f18Smalc PA_STREAM_RECORD, 622ea9ebc2cSMarc-André Lureau glob_paaudio.source, 623b8e59f18Smalc "pcm.capture", 624b8e59f18Smalc &ss, 625b8e59f18Smalc NULL, /* channel map */ 626b8e59f18Smalc NULL, /* buffering attributes */ 627b8e59f18Smalc &error 628b8e59f18Smalc ); 629ea9ebc2cSMarc-André Lureau if (!pa->stream) { 630b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 631b8e59f18Smalc goto fail1; 632b8e59f18Smalc } 633b8e59f18Smalc 634b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 635ea9ebc2cSMarc-André Lureau hw->samples = glob_paaudio.samples; 636b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 6376315633bSGerd Hoffmann pa->wpos = hw->wpos; 638b8e59f18Smalc if (!pa->pcm_buf) { 639b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 640b8e59f18Smalc hw->samples << hw->info.shift); 641b8e59f18Smalc goto fail2; 642b8e59f18Smalc } 643b8e59f18Smalc 644b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 645b8e59f18Smalc goto fail3; 646b8e59f18Smalc } 647b8e59f18Smalc 648b8e59f18Smalc return 0; 649b8e59f18Smalc 650b8e59f18Smalc fail3: 6517267c094SAnthony Liguori g_free (pa->pcm_buf); 652b8e59f18Smalc pa->pcm_buf = NULL; 653b8e59f18Smalc fail2: 654ea9ebc2cSMarc-André Lureau if (pa->stream) { 655ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 656ea9ebc2cSMarc-André Lureau pa->stream = NULL; 657ea9ebc2cSMarc-André Lureau } 658b8e59f18Smalc fail1: 659b8e59f18Smalc return -1; 660b8e59f18Smalc } 661b8e59f18Smalc 662b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 663b8e59f18Smalc { 664b8e59f18Smalc void *ret; 665b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 666b8e59f18Smalc 667b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 668b8e59f18Smalc pa->done = 1; 669b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 670b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 671b8e59f18Smalc 672ea9ebc2cSMarc-André Lureau if (pa->stream) { 673ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 674ea9ebc2cSMarc-André Lureau pa->stream = NULL; 675b8e59f18Smalc } 676b8e59f18Smalc 677b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6787267c094SAnthony Liguori g_free (pa->pcm_buf); 679b8e59f18Smalc pa->pcm_buf = NULL; 680b8e59f18Smalc } 681b8e59f18Smalc 682b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 683b8e59f18Smalc { 684b8e59f18Smalc void *ret; 685b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 686b8e59f18Smalc 687b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 688b8e59f18Smalc pa->done = 1; 689b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 690b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 691b8e59f18Smalc 692ea9ebc2cSMarc-André Lureau if (pa->stream) { 693ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 694ea9ebc2cSMarc-André Lureau pa->stream = NULL; 695b8e59f18Smalc } 696b8e59f18Smalc 697b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6987267c094SAnthony Liguori g_free (pa->pcm_buf); 699b8e59f18Smalc pa->pcm_buf = NULL; 700b8e59f18Smalc } 701b8e59f18Smalc 702b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 703b8e59f18Smalc { 7046e7a7f3dSMarc-André Lureau PAVoiceOut *pa = (PAVoiceOut *) hw; 7056e7a7f3dSMarc-André Lureau pa_operation *op; 7066e7a7f3dSMarc-André Lureau pa_cvolume v; 7076e7a7f3dSMarc-André Lureau paaudio *g = &glob_paaudio; 7086e7a7f3dSMarc-André Lureau 7098f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ 7108f473dd1SGerd Hoffmann pa_cvolume_init (&v); /* function is present in 0.9.13+ */ 7118f473dd1SGerd Hoffmann #endif 7126e7a7f3dSMarc-André Lureau 7136e7a7f3dSMarc-André Lureau switch (cmd) { 7146e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7156e7a7f3dSMarc-André Lureau { 7166e7a7f3dSMarc-André Lureau SWVoiceOut *sw; 7176e7a7f3dSMarc-André Lureau va_list ap; 7186e7a7f3dSMarc-André Lureau 7196e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7206e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceOut *); 7216e7a7f3dSMarc-André Lureau va_end (ap); 7226e7a7f3dSMarc-André Lureau 7236e7a7f3dSMarc-André Lureau v.channels = 2; 7246e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7256e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7266e7a7f3dSMarc-André Lureau 7276e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7286e7a7f3dSMarc-André Lureau 7296e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_volume (g->context, 7306e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7316e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7326e7a7f3dSMarc-André Lureau if (!op) 7336e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7346e7a7f3dSMarc-André Lureau "set_sink_input_volume() failed\n"); 7356e7a7f3dSMarc-André Lureau else 7366e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7376e7a7f3dSMarc-André Lureau 7386e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_mute (g->context, 7396e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7406e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 7416e7a7f3dSMarc-André Lureau if (!op) { 7426e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7436e7a7f3dSMarc-André Lureau "set_sink_input_mute() failed\n"); 7446e7a7f3dSMarc-André Lureau } else { 7456e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7466e7a7f3dSMarc-André Lureau } 7476e7a7f3dSMarc-André Lureau 7486e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 7496e7a7f3dSMarc-André Lureau } 7506e7a7f3dSMarc-André Lureau } 751b8e59f18Smalc return 0; 752b8e59f18Smalc } 753b8e59f18Smalc 754b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 755b8e59f18Smalc { 7566e7a7f3dSMarc-André Lureau PAVoiceIn *pa = (PAVoiceIn *) hw; 7576e7a7f3dSMarc-André Lureau pa_operation *op; 7586e7a7f3dSMarc-André Lureau pa_cvolume v; 7596e7a7f3dSMarc-André Lureau paaudio *g = &glob_paaudio; 7606e7a7f3dSMarc-André Lureau 7618f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION 7626e7a7f3dSMarc-André Lureau pa_cvolume_init (&v); 7638f473dd1SGerd Hoffmann #endif 7646e7a7f3dSMarc-André Lureau 7656e7a7f3dSMarc-André Lureau switch (cmd) { 7666e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7676e7a7f3dSMarc-André Lureau { 7686e7a7f3dSMarc-André Lureau SWVoiceIn *sw; 7696e7a7f3dSMarc-André Lureau va_list ap; 7706e7a7f3dSMarc-André Lureau 7716e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7726e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceIn *); 7736e7a7f3dSMarc-André Lureau va_end (ap); 7746e7a7f3dSMarc-André Lureau 7756e7a7f3dSMarc-André Lureau v.channels = 2; 7766e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7776e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7786e7a7f3dSMarc-André Lureau 7796e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7806e7a7f3dSMarc-André Lureau 7816e7a7f3dSMarc-André Lureau /* FIXME: use the upcoming "set_source_output_{volume,mute}" */ 7826e7a7f3dSMarc-André Lureau op = pa_context_set_source_volume_by_index (g->context, 7836e7a7f3dSMarc-André Lureau pa_stream_get_device_index (pa->stream), 7846e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7856e7a7f3dSMarc-André Lureau if (!op) { 7866e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7876e7a7f3dSMarc-André Lureau "set_source_volume() failed\n"); 7886e7a7f3dSMarc-André Lureau } else { 7896e7a7f3dSMarc-André Lureau pa_operation_unref(op); 7906e7a7f3dSMarc-André Lureau } 7916e7a7f3dSMarc-André Lureau 7926e7a7f3dSMarc-André Lureau op = pa_context_set_source_mute_by_index (g->context, 7936e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7946e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 7956e7a7f3dSMarc-André Lureau if (!op) { 7966e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7976e7a7f3dSMarc-André Lureau "set_source_mute() failed\n"); 7986e7a7f3dSMarc-André Lureau } else { 7996e7a7f3dSMarc-André Lureau pa_operation_unref (op); 8006e7a7f3dSMarc-André Lureau } 8016e7a7f3dSMarc-André Lureau 8026e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 8036e7a7f3dSMarc-André Lureau } 8046e7a7f3dSMarc-André Lureau } 805b8e59f18Smalc return 0; 806b8e59f18Smalc } 807b8e59f18Smalc 808b8e59f18Smalc /* common */ 809b8e59f18Smalc static void *qpa_audio_init (void) 810b8e59f18Smalc { 811ea9ebc2cSMarc-André Lureau paaudio *g = &glob_paaudio; 812ea9ebc2cSMarc-André Lureau 813ea9ebc2cSMarc-André Lureau g->mainloop = pa_threaded_mainloop_new (); 814ea9ebc2cSMarc-André Lureau if (!g->mainloop) { 815ea9ebc2cSMarc-André Lureau goto fail; 816ea9ebc2cSMarc-André Lureau } 817ea9ebc2cSMarc-André Lureau 818ea9ebc2cSMarc-André Lureau g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); 819ea9ebc2cSMarc-André Lureau if (!g->context) { 820ea9ebc2cSMarc-André Lureau goto fail; 821ea9ebc2cSMarc-André Lureau } 822ea9ebc2cSMarc-André Lureau 823ea9ebc2cSMarc-André Lureau pa_context_set_state_callback (g->context, context_state_cb, g); 824ea9ebc2cSMarc-André Lureau 825ea9ebc2cSMarc-André Lureau if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { 826ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 827ea9ebc2cSMarc-André Lureau "pa_context_connect() failed\n"); 828ea9ebc2cSMarc-André Lureau goto fail; 829ea9ebc2cSMarc-André Lureau } 830ea9ebc2cSMarc-André Lureau 831ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 832ea9ebc2cSMarc-André Lureau 833ea9ebc2cSMarc-André Lureau if (pa_threaded_mainloop_start (g->mainloop) < 0) { 834ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 835ea9ebc2cSMarc-André Lureau } 836ea9ebc2cSMarc-André Lureau 837ea9ebc2cSMarc-André Lureau for (;;) { 838ea9ebc2cSMarc-André Lureau pa_context_state_t state; 839ea9ebc2cSMarc-André Lureau 840ea9ebc2cSMarc-André Lureau state = pa_context_get_state (g->context); 841ea9ebc2cSMarc-André Lureau 842ea9ebc2cSMarc-André Lureau if (state == PA_CONTEXT_READY) { 843ea9ebc2cSMarc-André Lureau break; 844ea9ebc2cSMarc-André Lureau } 845ea9ebc2cSMarc-André Lureau 846ea9ebc2cSMarc-André Lureau if (!PA_CONTEXT_IS_GOOD (state)) { 847ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 848ea9ebc2cSMarc-André Lureau "Wrong context state\n"); 849ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 850ea9ebc2cSMarc-André Lureau } 851ea9ebc2cSMarc-André Lureau 852ea9ebc2cSMarc-André Lureau /* Wait until the context is ready */ 853ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 854ea9ebc2cSMarc-André Lureau } 855ea9ebc2cSMarc-André Lureau 856ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 857ea9ebc2cSMarc-André Lureau 858ea9ebc2cSMarc-André Lureau return &glob_paaudio; 859ea9ebc2cSMarc-André Lureau 860ea9ebc2cSMarc-André Lureau unlock_and_fail: 861ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 862ea9ebc2cSMarc-André Lureau fail: 863ea9ebc2cSMarc-André Lureau AUD_log (AUDIO_CAP, "Failed to initialize PA context"); 864ea9ebc2cSMarc-André Lureau return NULL; 865b8e59f18Smalc } 866b8e59f18Smalc 867b8e59f18Smalc static void qpa_audio_fini (void *opaque) 868b8e59f18Smalc { 869ea9ebc2cSMarc-André Lureau paaudio *g = opaque; 870ea9ebc2cSMarc-André Lureau 871ea9ebc2cSMarc-André Lureau if (g->mainloop) { 872ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_stop (g->mainloop); 873ea9ebc2cSMarc-André Lureau } 874ea9ebc2cSMarc-André Lureau 875ea9ebc2cSMarc-André Lureau if (g->context) { 876ea9ebc2cSMarc-André Lureau pa_context_disconnect (g->context); 877ea9ebc2cSMarc-André Lureau pa_context_unref (g->context); 878ea9ebc2cSMarc-André Lureau g->context = NULL; 879ea9ebc2cSMarc-André Lureau } 880ea9ebc2cSMarc-André Lureau 881ea9ebc2cSMarc-André Lureau if (g->mainloop) { 882ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_free (g->mainloop); 883ea9ebc2cSMarc-André Lureau } 884ea9ebc2cSMarc-André Lureau 885ea9ebc2cSMarc-André Lureau g->mainloop = NULL; 886b8e59f18Smalc } 887b8e59f18Smalc 888b8e59f18Smalc struct audio_option qpa_options[] = { 88998f9f48cSmalc { 89098f9f48cSmalc .name = "SAMPLES", 8912700efa3SJuan Quintela .tag = AUD_OPT_INT, 892ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.samples, 89398f9f48cSmalc .descr = "buffer size in samples" 89498f9f48cSmalc }, 89598f9f48cSmalc { 89698f9f48cSmalc .name = "SERVER", 8972700efa3SJuan Quintela .tag = AUD_OPT_STR, 898ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.server, 89998f9f48cSmalc .descr = "server address" 90098f9f48cSmalc }, 90198f9f48cSmalc { 90298f9f48cSmalc .name = "SINK", 9032700efa3SJuan Quintela .tag = AUD_OPT_STR, 904ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.sink, 90598f9f48cSmalc .descr = "sink device name" 90698f9f48cSmalc }, 90798f9f48cSmalc { 90898f9f48cSmalc .name = "SOURCE", 9092700efa3SJuan Quintela .tag = AUD_OPT_STR, 910ea9ebc2cSMarc-André Lureau .valp = &glob_paaudio.source, 91198f9f48cSmalc .descr = "source device name" 91298f9f48cSmalc }, 9132700efa3SJuan Quintela { /* End of list */ } 914b8e59f18Smalc }; 915b8e59f18Smalc 91635f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 9171dd3e4d1SJuan Quintela .init_out = qpa_init_out, 9181dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 9191dd3e4d1SJuan Quintela .run_out = qpa_run_out, 9201dd3e4d1SJuan Quintela .write = qpa_write, 9211dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 9221dd3e4d1SJuan Quintela 9231dd3e4d1SJuan Quintela .init_in = qpa_init_in, 9241dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 9251dd3e4d1SJuan Quintela .run_in = qpa_run_in, 9261dd3e4d1SJuan Quintela .read = qpa_read, 9271dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 928b8e59f18Smalc }; 929b8e59f18Smalc 930b8e59f18Smalc struct audio_driver pa_audio_driver = { 931bee37f32SJuan Quintela .name = "pa", 932bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 933bee37f32SJuan Quintela .options = qpa_options, 934bee37f32SJuan Quintela .init = qpa_audio_init, 935bee37f32SJuan Quintela .fini = qpa_audio_fini, 936bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 9371a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 938bee37f32SJuan Quintela .max_voices_out = INT_MAX, 939bee37f32SJuan Quintela .max_voices_in = INT_MAX, 940bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 9416e7a7f3dSMarc-André Lureau .voice_size_in = sizeof (PAVoiceIn), 9426e7a7f3dSMarc-André Lureau .ctl_caps = VOICE_VOLUME_CAP 943b8e59f18Smalc }; 944