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 { 129a644c4bSKővágó, Zoltán int samples; 139a644c4bSKővágó, Zoltán char *server; 149a644c4bSKővágó, Zoltán char *sink; 159a644c4bSKővágó, Zoltán char *source; 169a644c4bSKővágó, Zoltán } PAConf; 179a644c4bSKővágó, Zoltán 189a644c4bSKővágó, Zoltán typedef struct { 199a644c4bSKővágó, Zoltán PAConf conf; 209a644c4bSKővágó, Zoltán pa_threaded_mainloop *mainloop; 219a644c4bSKővágó, Zoltán pa_context *context; 229a644c4bSKővágó, Zoltán } paaudio; 239a644c4bSKővágó, Zoltán 249a644c4bSKővágó, Zoltán typedef struct { 25b8e59f18Smalc HWVoiceOut hw; 26b8e59f18Smalc int done; 27b8e59f18Smalc int live; 28b8e59f18Smalc int decr; 29b8e59f18Smalc int rpos; 30ea9ebc2cSMarc-André Lureau pa_stream *stream; 31b8e59f18Smalc void *pcm_buf; 32b8e59f18Smalc struct audio_pt pt; 339a644c4bSKővágó, Zoltán paaudio *g; 34b8e59f18Smalc } PAVoiceOut; 35b8e59f18Smalc 36b8e59f18Smalc typedef struct { 37b8e59f18Smalc HWVoiceIn hw; 38b8e59f18Smalc int done; 39b8e59f18Smalc int dead; 40b8e59f18Smalc int incr; 41b8e59f18Smalc int wpos; 42ea9ebc2cSMarc-André Lureau pa_stream *stream; 43b8e59f18Smalc void *pcm_buf; 44b8e59f18Smalc struct audio_pt pt; 45ea9ebc2cSMarc-André Lureau const void *read_data; 46ea9ebc2cSMarc-André Lureau size_t read_index, read_length; 479a644c4bSKővágó, Zoltán paaudio *g; 48b8e59f18Smalc } PAVoiceIn; 49b8e59f18Smalc 50*49dd6d0dSKővágó, Zoltán static void qpa_audio_fini(void *opaque); 51*49dd6d0dSKővágó, Zoltán 52b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) 53b8e59f18Smalc { 54b8e59f18Smalc va_list ap; 55b8e59f18Smalc 56b8e59f18Smalc va_start (ap, fmt); 57b8e59f18Smalc AUD_vlog (AUDIO_CAP, fmt, ap); 58b8e59f18Smalc va_end (ap); 59b8e59f18Smalc 60b8e59f18Smalc AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); 61b8e59f18Smalc } 62b8e59f18Smalc 638f473dd1SGerd Hoffmann #ifndef PA_CONTEXT_IS_GOOD 648f473dd1SGerd Hoffmann static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) 658f473dd1SGerd Hoffmann { 668f473dd1SGerd Hoffmann return 678f473dd1SGerd Hoffmann x == PA_CONTEXT_CONNECTING || 688f473dd1SGerd Hoffmann x == PA_CONTEXT_AUTHORIZING || 698f473dd1SGerd Hoffmann x == PA_CONTEXT_SETTING_NAME || 708f473dd1SGerd Hoffmann x == PA_CONTEXT_READY; 718f473dd1SGerd Hoffmann } 728f473dd1SGerd Hoffmann #endif 738f473dd1SGerd Hoffmann 748f473dd1SGerd Hoffmann #ifndef PA_STREAM_IS_GOOD 758f473dd1SGerd Hoffmann static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) 768f473dd1SGerd Hoffmann { 778f473dd1SGerd Hoffmann return 788f473dd1SGerd Hoffmann x == PA_STREAM_CREATING || 798f473dd1SGerd Hoffmann x == PA_STREAM_READY; 808f473dd1SGerd Hoffmann } 818f473dd1SGerd Hoffmann #endif 828f473dd1SGerd Hoffmann 83ea9ebc2cSMarc-André Lureau #define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ 84ea9ebc2cSMarc-André Lureau do { \ 85ea9ebc2cSMarc-André Lureau if (!(expression)) { \ 86ea9ebc2cSMarc-André Lureau if (rerror) { \ 87ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 88ea9ebc2cSMarc-André Lureau } \ 89ea9ebc2cSMarc-André Lureau goto label; \ 90ea9ebc2cSMarc-André Lureau } \ 91ea9ebc2cSMarc-André Lureau } while (0); 92ea9ebc2cSMarc-André Lureau 93ea9ebc2cSMarc-André Lureau #define CHECK_DEAD_GOTO(c, stream, rerror, label) \ 94ea9ebc2cSMarc-André Lureau do { \ 95ea9ebc2cSMarc-André Lureau if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ 96ea9ebc2cSMarc-André Lureau !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ 97ea9ebc2cSMarc-André Lureau if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ 98ea9ebc2cSMarc-André Lureau ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ 99ea9ebc2cSMarc-André Lureau if (rerror) { \ 100ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 101ea9ebc2cSMarc-André Lureau } \ 102ea9ebc2cSMarc-André Lureau } else { \ 103ea9ebc2cSMarc-André Lureau if (rerror) { \ 104ea9ebc2cSMarc-André Lureau *(rerror) = PA_ERR_BADSTATE; \ 105ea9ebc2cSMarc-André Lureau } \ 106ea9ebc2cSMarc-André Lureau } \ 107ea9ebc2cSMarc-André Lureau goto label; \ 108ea9ebc2cSMarc-André Lureau } \ 109ea9ebc2cSMarc-André Lureau } while (0); 110ea9ebc2cSMarc-André Lureau 111ea9ebc2cSMarc-André Lureau static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) 112ea9ebc2cSMarc-André Lureau { 1139a644c4bSKővágó, Zoltán paaudio *g = p->g; 114ea9ebc2cSMarc-André Lureau 115ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 116ea9ebc2cSMarc-André Lureau 117ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 118ea9ebc2cSMarc-André Lureau 119ea9ebc2cSMarc-André Lureau while (length > 0) { 120ea9ebc2cSMarc-André Lureau size_t l; 121ea9ebc2cSMarc-André Lureau 122ea9ebc2cSMarc-André Lureau while (!p->read_data) { 123ea9ebc2cSMarc-André Lureau int r; 124ea9ebc2cSMarc-André Lureau 125ea9ebc2cSMarc-André Lureau r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); 126ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 127ea9ebc2cSMarc-André Lureau 128ea9ebc2cSMarc-André Lureau if (!p->read_data) { 129ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 130ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 131ea9ebc2cSMarc-André Lureau } else { 132ea9ebc2cSMarc-André Lureau p->read_index = 0; 133ea9ebc2cSMarc-André Lureau } 134ea9ebc2cSMarc-André Lureau } 135ea9ebc2cSMarc-André Lureau 136ea9ebc2cSMarc-André Lureau l = p->read_length < length ? p->read_length : length; 137ea9ebc2cSMarc-André Lureau memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); 138ea9ebc2cSMarc-André Lureau 139ea9ebc2cSMarc-André Lureau data = (uint8_t *) data + l; 140ea9ebc2cSMarc-André Lureau length -= l; 141ea9ebc2cSMarc-André Lureau 142ea9ebc2cSMarc-André Lureau p->read_index += l; 143ea9ebc2cSMarc-André Lureau p->read_length -= l; 144ea9ebc2cSMarc-André Lureau 145ea9ebc2cSMarc-André Lureau if (!p->read_length) { 146ea9ebc2cSMarc-André Lureau int r; 147ea9ebc2cSMarc-André Lureau 148ea9ebc2cSMarc-André Lureau r = pa_stream_drop (p->stream); 149ea9ebc2cSMarc-André Lureau p->read_data = NULL; 150ea9ebc2cSMarc-André Lureau p->read_length = 0; 151ea9ebc2cSMarc-André Lureau p->read_index = 0; 152ea9ebc2cSMarc-André Lureau 153ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 154ea9ebc2cSMarc-André Lureau } 155ea9ebc2cSMarc-André Lureau } 156ea9ebc2cSMarc-André Lureau 157ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 158ea9ebc2cSMarc-André Lureau return 0; 159ea9ebc2cSMarc-André Lureau 160ea9ebc2cSMarc-André Lureau unlock_and_fail: 161ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 162ea9ebc2cSMarc-André Lureau return -1; 163ea9ebc2cSMarc-André Lureau } 164ea9ebc2cSMarc-André Lureau 165ea9ebc2cSMarc-André Lureau static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) 166ea9ebc2cSMarc-André Lureau { 1679a644c4bSKővágó, Zoltán paaudio *g = p->g; 168ea9ebc2cSMarc-André Lureau 169ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 170ea9ebc2cSMarc-André Lureau 171ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 172ea9ebc2cSMarc-André Lureau 173ea9ebc2cSMarc-André Lureau while (length > 0) { 174ea9ebc2cSMarc-André Lureau size_t l; 175ea9ebc2cSMarc-André Lureau int r; 176ea9ebc2cSMarc-André Lureau 177ea9ebc2cSMarc-André Lureau while (!(l = pa_stream_writable_size (p->stream))) { 178ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 179ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 180ea9ebc2cSMarc-André Lureau } 181ea9ebc2cSMarc-André Lureau 182ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); 183ea9ebc2cSMarc-André Lureau 184ea9ebc2cSMarc-André Lureau if (l > length) { 185ea9ebc2cSMarc-André Lureau l = length; 186ea9ebc2cSMarc-André Lureau } 187ea9ebc2cSMarc-André Lureau 188ea9ebc2cSMarc-André Lureau r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); 189ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); 190ea9ebc2cSMarc-André Lureau 191ea9ebc2cSMarc-André Lureau data = (const uint8_t *) data + l; 192ea9ebc2cSMarc-André Lureau length -= l; 193ea9ebc2cSMarc-André Lureau } 194ea9ebc2cSMarc-André Lureau 195ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 196ea9ebc2cSMarc-André Lureau return 0; 197ea9ebc2cSMarc-André Lureau 198ea9ebc2cSMarc-André Lureau unlock_and_fail: 199ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 200ea9ebc2cSMarc-André Lureau return -1; 201ea9ebc2cSMarc-André Lureau } 202ea9ebc2cSMarc-André Lureau 203b8e59f18Smalc static void *qpa_thread_out (void *arg) 204b8e59f18Smalc { 205b8e59f18Smalc PAVoiceOut *pa = arg; 206b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 207b8e59f18Smalc 208b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 209b8e59f18Smalc return NULL; 210b8e59f18Smalc } 211b8e59f18Smalc 212b8e59f18Smalc for (;;) { 213b8e59f18Smalc int decr, to_mix, rpos; 214b8e59f18Smalc 215b8e59f18Smalc for (;;) { 216b8e59f18Smalc if (pa->done) { 217b8e59f18Smalc goto exit; 218b8e59f18Smalc } 219b8e59f18Smalc 2206315633bSGerd Hoffmann if (pa->live > 0) { 221b8e59f18Smalc break; 222b8e59f18Smalc } 223b8e59f18Smalc 224b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 225b8e59f18Smalc goto exit; 226b8e59f18Smalc } 227b8e59f18Smalc } 228b8e59f18Smalc 2299a644c4bSKővágó, Zoltán decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); 2306315633bSGerd Hoffmann rpos = pa->rpos; 231b8e59f18Smalc 232b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 233b8e59f18Smalc return NULL; 234b8e59f18Smalc } 235b8e59f18Smalc 236b8e59f18Smalc while (to_mix) { 237b8e59f18Smalc int error; 238b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 2391ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 240b8e59f18Smalc 241b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 242b8e59f18Smalc 243ea9ebc2cSMarc-André Lureau if (qpa_simple_write (pa, pa->pcm_buf, 244b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 245b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 246b8e59f18Smalc return NULL; 247b8e59f18Smalc } 248b8e59f18Smalc 249b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 250b8e59f18Smalc to_mix -= chunk; 251b8e59f18Smalc } 252b8e59f18Smalc 253b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 254b8e59f18Smalc return NULL; 255b8e59f18Smalc } 256b8e59f18Smalc 257b8e59f18Smalc pa->rpos = rpos; 2586315633bSGerd Hoffmann pa->live -= decr; 259b8e59f18Smalc pa->decr += decr; 260b8e59f18Smalc } 261b8e59f18Smalc 262b8e59f18Smalc exit: 263b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 264b8e59f18Smalc return NULL; 265b8e59f18Smalc } 266b8e59f18Smalc 267bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 268b8e59f18Smalc { 269bdff253cSmalc int decr; 270b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 271b8e59f18Smalc 272b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 273b8e59f18Smalc return 0; 274b8e59f18Smalc } 275b8e59f18Smalc 276b8e59f18Smalc decr = audio_MIN (live, pa->decr); 277b8e59f18Smalc pa->decr -= decr; 278b8e59f18Smalc pa->live = live - decr; 279b8e59f18Smalc hw->rpos = pa->rpos; 280b8e59f18Smalc if (pa->live > 0) { 281b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 282b8e59f18Smalc } 283b8e59f18Smalc else { 284b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 285b8e59f18Smalc } 286b8e59f18Smalc return decr; 287b8e59f18Smalc } 288b8e59f18Smalc 289b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 290b8e59f18Smalc { 291b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 292b8e59f18Smalc } 293b8e59f18Smalc 294b8e59f18Smalc /* capture */ 295b8e59f18Smalc static void *qpa_thread_in (void *arg) 296b8e59f18Smalc { 297b8e59f18Smalc PAVoiceIn *pa = arg; 298b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 299b8e59f18Smalc 300b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 301b8e59f18Smalc return NULL; 302b8e59f18Smalc } 303b8e59f18Smalc 304b8e59f18Smalc for (;;) { 305b8e59f18Smalc int incr, to_grab, wpos; 306b8e59f18Smalc 307b8e59f18Smalc for (;;) { 308b8e59f18Smalc if (pa->done) { 309b8e59f18Smalc goto exit; 310b8e59f18Smalc } 311b8e59f18Smalc 3126315633bSGerd Hoffmann if (pa->dead > 0) { 313b8e59f18Smalc break; 314b8e59f18Smalc } 315b8e59f18Smalc 316b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 317b8e59f18Smalc goto exit; 318b8e59f18Smalc } 319b8e59f18Smalc } 320b8e59f18Smalc 3219a644c4bSKővágó, Zoltán incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); 3226315633bSGerd Hoffmann wpos = pa->wpos; 323b8e59f18Smalc 324b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 325b8e59f18Smalc return NULL; 326b8e59f18Smalc } 327b8e59f18Smalc 328b8e59f18Smalc while (to_grab) { 329b8e59f18Smalc int error; 330b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 331b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 332b8e59f18Smalc 333ea9ebc2cSMarc-André Lureau if (qpa_simple_read (pa, buf, 334b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 335b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 336b8e59f18Smalc return NULL; 337b8e59f18Smalc } 338b8e59f18Smalc 33900e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 340b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 341b8e59f18Smalc to_grab -= chunk; 342b8e59f18Smalc } 343b8e59f18Smalc 344b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 345b8e59f18Smalc return NULL; 346b8e59f18Smalc } 347b8e59f18Smalc 348b8e59f18Smalc pa->wpos = wpos; 349b8e59f18Smalc pa->dead -= incr; 350b8e59f18Smalc pa->incr += incr; 351b8e59f18Smalc } 352b8e59f18Smalc 353b8e59f18Smalc exit: 354b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 355b8e59f18Smalc return NULL; 356b8e59f18Smalc } 357b8e59f18Smalc 358b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 359b8e59f18Smalc { 360b8e59f18Smalc int live, incr, dead; 361b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 362b8e59f18Smalc 363b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 364b8e59f18Smalc return 0; 365b8e59f18Smalc } 366b8e59f18Smalc 367b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 368b8e59f18Smalc dead = hw->samples - live; 369b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 370b8e59f18Smalc pa->incr -= incr; 371b8e59f18Smalc pa->dead = dead - incr; 372b8e59f18Smalc hw->wpos = pa->wpos; 373b8e59f18Smalc if (pa->dead > 0) { 374b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 375b8e59f18Smalc } 376b8e59f18Smalc else { 377b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 378b8e59f18Smalc } 379b8e59f18Smalc return incr; 380b8e59f18Smalc } 381b8e59f18Smalc 382b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 383b8e59f18Smalc { 384b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 385b8e59f18Smalc } 386b8e59f18Smalc 387b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 388b8e59f18Smalc { 389b8e59f18Smalc int format; 390b8e59f18Smalc 391b8e59f18Smalc switch (afmt) { 392b8e59f18Smalc case AUD_FMT_S8: 393b8e59f18Smalc case AUD_FMT_U8: 394b8e59f18Smalc format = PA_SAMPLE_U8; 395b8e59f18Smalc break; 396b8e59f18Smalc case AUD_FMT_S16: 397b8e59f18Smalc case AUD_FMT_U16: 398b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 399b8e59f18Smalc break; 400b8e59f18Smalc case AUD_FMT_S32: 401b8e59f18Smalc case AUD_FMT_U32: 402b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 403b8e59f18Smalc break; 404b8e59f18Smalc default: 405b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 406b8e59f18Smalc format = PA_SAMPLE_U8; 407b8e59f18Smalc break; 408b8e59f18Smalc } 409b8e59f18Smalc return format; 410b8e59f18Smalc } 411b8e59f18Smalc 412b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 413b8e59f18Smalc { 414b8e59f18Smalc switch (fmt) { 415b8e59f18Smalc case PA_SAMPLE_U8: 416b8e59f18Smalc return AUD_FMT_U8; 417b8e59f18Smalc case PA_SAMPLE_S16BE: 418b8e59f18Smalc *endianness = 1; 419b8e59f18Smalc return AUD_FMT_S16; 420b8e59f18Smalc case PA_SAMPLE_S16LE: 421b8e59f18Smalc *endianness = 0; 422b8e59f18Smalc return AUD_FMT_S16; 423b8e59f18Smalc case PA_SAMPLE_S32BE: 424b8e59f18Smalc *endianness = 1; 425b8e59f18Smalc return AUD_FMT_S32; 426b8e59f18Smalc case PA_SAMPLE_S32LE: 427b8e59f18Smalc *endianness = 0; 428b8e59f18Smalc return AUD_FMT_S32; 429b8e59f18Smalc default: 430b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 431b8e59f18Smalc return AUD_FMT_U8; 432b8e59f18Smalc } 433b8e59f18Smalc } 434b8e59f18Smalc 435ea9ebc2cSMarc-André Lureau static void context_state_cb (pa_context *c, void *userdata) 436ea9ebc2cSMarc-André Lureau { 4379a644c4bSKővágó, Zoltán paaudio *g = userdata; 438ea9ebc2cSMarc-André Lureau 439ea9ebc2cSMarc-André Lureau switch (pa_context_get_state(c)) { 440ea9ebc2cSMarc-André Lureau case PA_CONTEXT_READY: 441ea9ebc2cSMarc-André Lureau case PA_CONTEXT_TERMINATED: 442ea9ebc2cSMarc-André Lureau case PA_CONTEXT_FAILED: 443ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 444ea9ebc2cSMarc-André Lureau break; 445ea9ebc2cSMarc-André Lureau 446ea9ebc2cSMarc-André Lureau case PA_CONTEXT_UNCONNECTED: 447ea9ebc2cSMarc-André Lureau case PA_CONTEXT_CONNECTING: 448ea9ebc2cSMarc-André Lureau case PA_CONTEXT_AUTHORIZING: 449ea9ebc2cSMarc-André Lureau case PA_CONTEXT_SETTING_NAME: 450ea9ebc2cSMarc-André Lureau break; 451ea9ebc2cSMarc-André Lureau } 452ea9ebc2cSMarc-André Lureau } 453ea9ebc2cSMarc-André Lureau 454ea9ebc2cSMarc-André Lureau static void stream_state_cb (pa_stream *s, void * userdata) 455ea9ebc2cSMarc-André Lureau { 4569a644c4bSKővágó, Zoltán paaudio *g = userdata; 457ea9ebc2cSMarc-André Lureau 458ea9ebc2cSMarc-André Lureau switch (pa_stream_get_state (s)) { 459ea9ebc2cSMarc-André Lureau 460ea9ebc2cSMarc-André Lureau case PA_STREAM_READY: 461ea9ebc2cSMarc-André Lureau case PA_STREAM_FAILED: 462ea9ebc2cSMarc-André Lureau case PA_STREAM_TERMINATED: 463ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 464ea9ebc2cSMarc-André Lureau break; 465ea9ebc2cSMarc-André Lureau 466ea9ebc2cSMarc-André Lureau case PA_STREAM_UNCONNECTED: 467ea9ebc2cSMarc-André Lureau case PA_STREAM_CREATING: 468ea9ebc2cSMarc-André Lureau break; 469ea9ebc2cSMarc-André Lureau } 470ea9ebc2cSMarc-André Lureau } 471ea9ebc2cSMarc-André Lureau 472ea9ebc2cSMarc-André Lureau static void stream_request_cb (pa_stream *s, size_t length, void *userdata) 473ea9ebc2cSMarc-André Lureau { 4749a644c4bSKővágó, Zoltán paaudio *g = userdata; 475ea9ebc2cSMarc-André Lureau 476ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 477ea9ebc2cSMarc-André Lureau } 478ea9ebc2cSMarc-André Lureau 479ea9ebc2cSMarc-André Lureau static pa_stream *qpa_simple_new ( 4809a644c4bSKővágó, Zoltán paaudio *g, 481ea9ebc2cSMarc-André Lureau const char *name, 482ea9ebc2cSMarc-André Lureau pa_stream_direction_t dir, 483ea9ebc2cSMarc-André Lureau const char *dev, 484ea9ebc2cSMarc-André Lureau const pa_sample_spec *ss, 485ea9ebc2cSMarc-André Lureau const pa_channel_map *map, 486ea9ebc2cSMarc-André Lureau const pa_buffer_attr *attr, 487ea9ebc2cSMarc-André Lureau int *rerror) 488ea9ebc2cSMarc-André Lureau { 489ea9ebc2cSMarc-André Lureau int r; 490ea9ebc2cSMarc-André Lureau pa_stream *stream; 491ea9ebc2cSMarc-André Lureau 492ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 493ea9ebc2cSMarc-André Lureau 494ea9ebc2cSMarc-André Lureau stream = pa_stream_new (g->context, name, ss, map); 495ea9ebc2cSMarc-André Lureau if (!stream) { 496ea9ebc2cSMarc-André Lureau goto fail; 497ea9ebc2cSMarc-André Lureau } 498ea9ebc2cSMarc-André Lureau 499ea9ebc2cSMarc-André Lureau pa_stream_set_state_callback (stream, stream_state_cb, g); 500ea9ebc2cSMarc-André Lureau pa_stream_set_read_callback (stream, stream_request_cb, g); 501ea9ebc2cSMarc-André Lureau pa_stream_set_write_callback (stream, stream_request_cb, g); 502ea9ebc2cSMarc-André Lureau 503ea9ebc2cSMarc-André Lureau if (dir == PA_STREAM_PLAYBACK) { 504ea9ebc2cSMarc-André Lureau r = pa_stream_connect_playback (stream, dev, attr, 505ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5068f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 507ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5088f473dd1SGerd Hoffmann #endif 509ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); 510ea9ebc2cSMarc-André Lureau } else { 511ea9ebc2cSMarc-André Lureau r = pa_stream_connect_record (stream, dev, attr, 512ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5138f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 514ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5158f473dd1SGerd Hoffmann #endif 516ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE); 517ea9ebc2cSMarc-André Lureau } 518ea9ebc2cSMarc-André Lureau 519ea9ebc2cSMarc-André Lureau if (r < 0) { 520ea9ebc2cSMarc-André Lureau goto fail; 521ea9ebc2cSMarc-André Lureau } 522ea9ebc2cSMarc-André Lureau 523ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 524ea9ebc2cSMarc-André Lureau 525ea9ebc2cSMarc-André Lureau return stream; 526ea9ebc2cSMarc-André Lureau 527ea9ebc2cSMarc-André Lureau fail: 528ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 529ea9ebc2cSMarc-André Lureau 530ea9ebc2cSMarc-André Lureau if (stream) { 531ea9ebc2cSMarc-André Lureau pa_stream_unref (stream); 532ea9ebc2cSMarc-André Lureau } 533ea9ebc2cSMarc-André Lureau 534d6c05bbfSGerd Hoffmann *rerror = pa_context_errno (g->context); 535ea9ebc2cSMarc-André Lureau 536ea9ebc2cSMarc-André Lureau return NULL; 537ea9ebc2cSMarc-André Lureau } 538ea9ebc2cSMarc-André Lureau 5395706db1dSKővágó, Zoltán static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, 5405706db1dSKővágó, Zoltán void *drv_opaque) 541b8e59f18Smalc { 542b8e59f18Smalc int error; 5439a644c4bSKővágó, Zoltán pa_sample_spec ss; 5449a644c4bSKővágó, Zoltán pa_buffer_attr ba; 5451ea879e5Smalc struct audsettings obt_as = *as; 546b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 5479a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 548b8e59f18Smalc 549b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 550b8e59f18Smalc ss.channels = as->nchannels; 551b8e59f18Smalc ss.rate = as->freq; 552b8e59f18Smalc 553e6d16fa4SGerd Hoffmann /* 5540e8ae611SGerd Hoffmann * qemu audio tick runs at 100 Hz (by default), so processing 5550e8ae611SGerd Hoffmann * data chunks worth 10 ms of sound should be a good fit. 556e6d16fa4SGerd Hoffmann */ 5570e8ae611SGerd Hoffmann ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); 5580e8ae611SGerd Hoffmann ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); 559e6d16fa4SGerd Hoffmann ba.maxlength = -1; 560e6d16fa4SGerd Hoffmann ba.prebuf = -1; 561e6d16fa4SGerd Hoffmann 562b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 563b8e59f18Smalc 564ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 5659a644c4bSKővágó, Zoltán g, 566b8e59f18Smalc "qemu", 567b8e59f18Smalc PA_STREAM_PLAYBACK, 5689a644c4bSKővágó, Zoltán g->conf.sink, 569b8e59f18Smalc &ss, 570b8e59f18Smalc NULL, /* channel map */ 571e6d16fa4SGerd Hoffmann &ba, /* buffering attributes */ 572b8e59f18Smalc &error 573b8e59f18Smalc ); 574ea9ebc2cSMarc-André Lureau if (!pa->stream) { 575b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 576b8e59f18Smalc goto fail1; 577b8e59f18Smalc } 578b8e59f18Smalc 579b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 5809a644c4bSKővágó, Zoltán hw->samples = g->conf.samples; 581b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 5826315633bSGerd Hoffmann pa->rpos = hw->rpos; 583b8e59f18Smalc if (!pa->pcm_buf) { 584b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 585b8e59f18Smalc hw->samples << hw->info.shift); 586b8e59f18Smalc goto fail2; 587b8e59f18Smalc } 588b8e59f18Smalc 589b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 590b8e59f18Smalc goto fail3; 591b8e59f18Smalc } 592b8e59f18Smalc 593b8e59f18Smalc return 0; 594b8e59f18Smalc 595b8e59f18Smalc fail3: 5967267c094SAnthony Liguori g_free (pa->pcm_buf); 597b8e59f18Smalc pa->pcm_buf = NULL; 598b8e59f18Smalc fail2: 599ea9ebc2cSMarc-André Lureau if (pa->stream) { 600ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 601ea9ebc2cSMarc-André Lureau pa->stream = NULL; 602ea9ebc2cSMarc-André Lureau } 603b8e59f18Smalc fail1: 604b8e59f18Smalc return -1; 605b8e59f18Smalc } 606b8e59f18Smalc 6075706db1dSKővágó, Zoltán static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 608b8e59f18Smalc { 609b8e59f18Smalc int error; 6109a644c4bSKővágó, Zoltán pa_sample_spec ss; 6111ea879e5Smalc struct audsettings obt_as = *as; 612b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 6139a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 614b8e59f18Smalc 615b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 616b8e59f18Smalc ss.channels = as->nchannels; 617b8e59f18Smalc ss.rate = as->freq; 618b8e59f18Smalc 619b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 620b8e59f18Smalc 621ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 6229a644c4bSKővágó, Zoltán g, 623b8e59f18Smalc "qemu", 624b8e59f18Smalc PA_STREAM_RECORD, 6259a644c4bSKővágó, Zoltán g->conf.source, 626b8e59f18Smalc &ss, 627b8e59f18Smalc NULL, /* channel map */ 628b8e59f18Smalc NULL, /* buffering attributes */ 629b8e59f18Smalc &error 630b8e59f18Smalc ); 631ea9ebc2cSMarc-André Lureau if (!pa->stream) { 632b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 633b8e59f18Smalc goto fail1; 634b8e59f18Smalc } 635b8e59f18Smalc 636b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 6379a644c4bSKővágó, Zoltán hw->samples = g->conf.samples; 638b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 6396315633bSGerd Hoffmann pa->wpos = hw->wpos; 640b8e59f18Smalc if (!pa->pcm_buf) { 641b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 642b8e59f18Smalc hw->samples << hw->info.shift); 643b8e59f18Smalc goto fail2; 644b8e59f18Smalc } 645b8e59f18Smalc 646b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 647b8e59f18Smalc goto fail3; 648b8e59f18Smalc } 649b8e59f18Smalc 650b8e59f18Smalc return 0; 651b8e59f18Smalc 652b8e59f18Smalc fail3: 6537267c094SAnthony Liguori g_free (pa->pcm_buf); 654b8e59f18Smalc pa->pcm_buf = NULL; 655b8e59f18Smalc fail2: 656ea9ebc2cSMarc-André Lureau if (pa->stream) { 657ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 658ea9ebc2cSMarc-André Lureau pa->stream = NULL; 659ea9ebc2cSMarc-André Lureau } 660b8e59f18Smalc fail1: 661b8e59f18Smalc return -1; 662b8e59f18Smalc } 663b8e59f18Smalc 664b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 665b8e59f18Smalc { 666b8e59f18Smalc void *ret; 667b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 668b8e59f18Smalc 669b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 670b8e59f18Smalc pa->done = 1; 671b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 672b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 673b8e59f18Smalc 674ea9ebc2cSMarc-André Lureau if (pa->stream) { 675ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 676ea9ebc2cSMarc-André Lureau pa->stream = NULL; 677b8e59f18Smalc } 678b8e59f18Smalc 679b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6807267c094SAnthony Liguori g_free (pa->pcm_buf); 681b8e59f18Smalc pa->pcm_buf = NULL; 682b8e59f18Smalc } 683b8e59f18Smalc 684b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 685b8e59f18Smalc { 686b8e59f18Smalc void *ret; 687b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 688b8e59f18Smalc 689b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 690b8e59f18Smalc pa->done = 1; 691b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 692b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 693b8e59f18Smalc 694ea9ebc2cSMarc-André Lureau if (pa->stream) { 695ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 696ea9ebc2cSMarc-André Lureau pa->stream = NULL; 697b8e59f18Smalc } 698b8e59f18Smalc 699b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 7007267c094SAnthony Liguori g_free (pa->pcm_buf); 701b8e59f18Smalc pa->pcm_buf = NULL; 702b8e59f18Smalc } 703b8e59f18Smalc 704b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 705b8e59f18Smalc { 7066e7a7f3dSMarc-André Lureau PAVoiceOut *pa = (PAVoiceOut *) hw; 7076e7a7f3dSMarc-André Lureau pa_operation *op; 7086e7a7f3dSMarc-André Lureau pa_cvolume v; 7099a644c4bSKővágó, Zoltán paaudio *g = pa->g; 7106e7a7f3dSMarc-André Lureau 7118f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ 7128f473dd1SGerd Hoffmann pa_cvolume_init (&v); /* function is present in 0.9.13+ */ 7138f473dd1SGerd Hoffmann #endif 7146e7a7f3dSMarc-André Lureau 7156e7a7f3dSMarc-André Lureau switch (cmd) { 7166e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7176e7a7f3dSMarc-André Lureau { 7186e7a7f3dSMarc-André Lureau SWVoiceOut *sw; 7196e7a7f3dSMarc-André Lureau va_list ap; 7206e7a7f3dSMarc-André Lureau 7216e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7226e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceOut *); 7236e7a7f3dSMarc-André Lureau va_end (ap); 7246e7a7f3dSMarc-André Lureau 7256e7a7f3dSMarc-André Lureau v.channels = 2; 7266e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7276e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7286e7a7f3dSMarc-André Lureau 7296e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7306e7a7f3dSMarc-André Lureau 7316e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_volume (g->context, 7326e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7336e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7346e7a7f3dSMarc-André Lureau if (!op) 7356e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7366e7a7f3dSMarc-André Lureau "set_sink_input_volume() failed\n"); 7376e7a7f3dSMarc-André Lureau else 7386e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7396e7a7f3dSMarc-André Lureau 7406e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_mute (g->context, 7416e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7426e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 7436e7a7f3dSMarc-André Lureau if (!op) { 7446e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7456e7a7f3dSMarc-André Lureau "set_sink_input_mute() failed\n"); 7466e7a7f3dSMarc-André Lureau } else { 7476e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7486e7a7f3dSMarc-André Lureau } 7496e7a7f3dSMarc-André Lureau 7506e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 7516e7a7f3dSMarc-André Lureau } 7526e7a7f3dSMarc-André Lureau } 753b8e59f18Smalc return 0; 754b8e59f18Smalc } 755b8e59f18Smalc 756b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 757b8e59f18Smalc { 7586e7a7f3dSMarc-André Lureau PAVoiceIn *pa = (PAVoiceIn *) hw; 7596e7a7f3dSMarc-André Lureau pa_operation *op; 7606e7a7f3dSMarc-André Lureau pa_cvolume v; 7619a644c4bSKővágó, Zoltán paaudio *g = pa->g; 7626e7a7f3dSMarc-André Lureau 7638f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION 7646e7a7f3dSMarc-André Lureau pa_cvolume_init (&v); 7658f473dd1SGerd Hoffmann #endif 7666e7a7f3dSMarc-André Lureau 7676e7a7f3dSMarc-André Lureau switch (cmd) { 7686e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7696e7a7f3dSMarc-André Lureau { 7706e7a7f3dSMarc-André Lureau SWVoiceIn *sw; 7716e7a7f3dSMarc-André Lureau va_list ap; 7726e7a7f3dSMarc-André Lureau 7736e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7746e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceIn *); 7756e7a7f3dSMarc-André Lureau va_end (ap); 7766e7a7f3dSMarc-André Lureau 7776e7a7f3dSMarc-André Lureau v.channels = 2; 7786e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7796e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7806e7a7f3dSMarc-André Lureau 7816e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7826e7a7f3dSMarc-André Lureau 7836e7a7f3dSMarc-André Lureau /* FIXME: use the upcoming "set_source_output_{volume,mute}" */ 7846e7a7f3dSMarc-André Lureau op = pa_context_set_source_volume_by_index (g->context, 7856e7a7f3dSMarc-André Lureau pa_stream_get_device_index (pa->stream), 7866e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7876e7a7f3dSMarc-André Lureau if (!op) { 7886e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7896e7a7f3dSMarc-André Lureau "set_source_volume() failed\n"); 7906e7a7f3dSMarc-André Lureau } else { 7916e7a7f3dSMarc-André Lureau pa_operation_unref(op); 7926e7a7f3dSMarc-André Lureau } 7936e7a7f3dSMarc-André Lureau 7946e7a7f3dSMarc-André Lureau op = pa_context_set_source_mute_by_index (g->context, 7956e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7966e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 7976e7a7f3dSMarc-André Lureau if (!op) { 7986e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7996e7a7f3dSMarc-André Lureau "set_source_mute() failed\n"); 8006e7a7f3dSMarc-André Lureau } else { 8016e7a7f3dSMarc-André Lureau pa_operation_unref (op); 8026e7a7f3dSMarc-André Lureau } 8036e7a7f3dSMarc-André Lureau 8046e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 8056e7a7f3dSMarc-André Lureau } 8066e7a7f3dSMarc-André Lureau } 807b8e59f18Smalc return 0; 808b8e59f18Smalc } 809b8e59f18Smalc 810b8e59f18Smalc /* common */ 8119a644c4bSKővágó, Zoltán static PAConf glob_conf = { 8129a644c4bSKővágó, Zoltán .samples = 4096, 8139a644c4bSKővágó, Zoltán }; 8149a644c4bSKővágó, Zoltán 815b8e59f18Smalc static void *qpa_audio_init (void) 816b8e59f18Smalc { 8179a644c4bSKővágó, Zoltán paaudio *g = g_malloc(sizeof(paaudio)); 8189a644c4bSKővágó, Zoltán g->conf = glob_conf; 819*49dd6d0dSKővágó, Zoltán g->mainloop = NULL; 820*49dd6d0dSKővágó, Zoltán g->context = NULL; 821ea9ebc2cSMarc-André Lureau 822ea9ebc2cSMarc-André Lureau g->mainloop = pa_threaded_mainloop_new (); 823ea9ebc2cSMarc-André Lureau if (!g->mainloop) { 824ea9ebc2cSMarc-André Lureau goto fail; 825ea9ebc2cSMarc-André Lureau } 826ea9ebc2cSMarc-André Lureau 8279a644c4bSKővágó, Zoltán g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), 8289a644c4bSKővágó, Zoltán g->conf.server); 829ea9ebc2cSMarc-André Lureau if (!g->context) { 830ea9ebc2cSMarc-André Lureau goto fail; 831ea9ebc2cSMarc-André Lureau } 832ea9ebc2cSMarc-André Lureau 833ea9ebc2cSMarc-André Lureau pa_context_set_state_callback (g->context, context_state_cb, g); 834ea9ebc2cSMarc-André Lureau 8359a644c4bSKővágó, Zoltán if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { 836ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 837ea9ebc2cSMarc-André Lureau "pa_context_connect() failed\n"); 838ea9ebc2cSMarc-André Lureau goto fail; 839ea9ebc2cSMarc-André Lureau } 840ea9ebc2cSMarc-André Lureau 841ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 842ea9ebc2cSMarc-André Lureau 843ea9ebc2cSMarc-André Lureau if (pa_threaded_mainloop_start (g->mainloop) < 0) { 844ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 845ea9ebc2cSMarc-André Lureau } 846ea9ebc2cSMarc-André Lureau 847ea9ebc2cSMarc-André Lureau for (;;) { 848ea9ebc2cSMarc-André Lureau pa_context_state_t state; 849ea9ebc2cSMarc-André Lureau 850ea9ebc2cSMarc-André Lureau state = pa_context_get_state (g->context); 851ea9ebc2cSMarc-André Lureau 852ea9ebc2cSMarc-André Lureau if (state == PA_CONTEXT_READY) { 853ea9ebc2cSMarc-André Lureau break; 854ea9ebc2cSMarc-André Lureau } 855ea9ebc2cSMarc-André Lureau 856ea9ebc2cSMarc-André Lureau if (!PA_CONTEXT_IS_GOOD (state)) { 857ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 858ea9ebc2cSMarc-André Lureau "Wrong context state\n"); 859ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 860ea9ebc2cSMarc-André Lureau } 861ea9ebc2cSMarc-André Lureau 862ea9ebc2cSMarc-André Lureau /* Wait until the context is ready */ 863ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 864ea9ebc2cSMarc-André Lureau } 865ea9ebc2cSMarc-André Lureau 866ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 867ea9ebc2cSMarc-André Lureau 8689a644c4bSKővágó, Zoltán return g; 869ea9ebc2cSMarc-André Lureau 870ea9ebc2cSMarc-André Lureau unlock_and_fail: 871ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 872ea9ebc2cSMarc-André Lureau fail: 873ea9ebc2cSMarc-André Lureau AUD_log (AUDIO_CAP, "Failed to initialize PA context"); 874*49dd6d0dSKővágó, Zoltán qpa_audio_fini(g); 875ea9ebc2cSMarc-André Lureau return NULL; 876b8e59f18Smalc } 877b8e59f18Smalc 878b8e59f18Smalc static void qpa_audio_fini (void *opaque) 879b8e59f18Smalc { 880ea9ebc2cSMarc-André Lureau paaudio *g = opaque; 881ea9ebc2cSMarc-André Lureau 882ea9ebc2cSMarc-André Lureau if (g->mainloop) { 883ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_stop (g->mainloop); 884ea9ebc2cSMarc-André Lureau } 885ea9ebc2cSMarc-André Lureau 886ea9ebc2cSMarc-André Lureau if (g->context) { 887ea9ebc2cSMarc-André Lureau pa_context_disconnect (g->context); 888ea9ebc2cSMarc-André Lureau pa_context_unref (g->context); 889ea9ebc2cSMarc-André Lureau } 890ea9ebc2cSMarc-André Lureau 891ea9ebc2cSMarc-André Lureau if (g->mainloop) { 892ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_free (g->mainloop); 893ea9ebc2cSMarc-André Lureau } 894ea9ebc2cSMarc-André Lureau 8959a644c4bSKővágó, Zoltán g_free(g); 896b8e59f18Smalc } 897b8e59f18Smalc 898b8e59f18Smalc struct audio_option qpa_options[] = { 89998f9f48cSmalc { 90098f9f48cSmalc .name = "SAMPLES", 9012700efa3SJuan Quintela .tag = AUD_OPT_INT, 9029a644c4bSKővágó, Zoltán .valp = &glob_conf.samples, 90398f9f48cSmalc .descr = "buffer size in samples" 90498f9f48cSmalc }, 90598f9f48cSmalc { 90698f9f48cSmalc .name = "SERVER", 9072700efa3SJuan Quintela .tag = AUD_OPT_STR, 9089a644c4bSKővágó, Zoltán .valp = &glob_conf.server, 90998f9f48cSmalc .descr = "server address" 91098f9f48cSmalc }, 91198f9f48cSmalc { 91298f9f48cSmalc .name = "SINK", 9132700efa3SJuan Quintela .tag = AUD_OPT_STR, 9149a644c4bSKővágó, Zoltán .valp = &glob_conf.sink, 91598f9f48cSmalc .descr = "sink device name" 91698f9f48cSmalc }, 91798f9f48cSmalc { 91898f9f48cSmalc .name = "SOURCE", 9192700efa3SJuan Quintela .tag = AUD_OPT_STR, 9209a644c4bSKővágó, Zoltán .valp = &glob_conf.source, 92198f9f48cSmalc .descr = "source device name" 92298f9f48cSmalc }, 9232700efa3SJuan Quintela { /* End of list */ } 924b8e59f18Smalc }; 925b8e59f18Smalc 92635f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 9271dd3e4d1SJuan Quintela .init_out = qpa_init_out, 9281dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 9291dd3e4d1SJuan Quintela .run_out = qpa_run_out, 9301dd3e4d1SJuan Quintela .write = qpa_write, 9311dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 9321dd3e4d1SJuan Quintela 9331dd3e4d1SJuan Quintela .init_in = qpa_init_in, 9341dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 9351dd3e4d1SJuan Quintela .run_in = qpa_run_in, 9361dd3e4d1SJuan Quintela .read = qpa_read, 9371dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 938b8e59f18Smalc }; 939b8e59f18Smalc 940b8e59f18Smalc struct audio_driver pa_audio_driver = { 941bee37f32SJuan Quintela .name = "pa", 942bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 943bee37f32SJuan Quintela .options = qpa_options, 944bee37f32SJuan Quintela .init = qpa_audio_init, 945bee37f32SJuan Quintela .fini = qpa_audio_fini, 946bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 9471a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 948bee37f32SJuan Quintela .max_voices_out = INT_MAX, 949bee37f32SJuan Quintela .max_voices_in = INT_MAX, 950bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 9516e7a7f3dSMarc-André Lureau .voice_size_in = sizeof (PAVoiceIn), 9526e7a7f3dSMarc-André Lureau .ctl_caps = VOICE_VOLUME_CAP 953b8e59f18Smalc }; 954