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 { 12*9a644c4bSKővágó, Zoltán int samples; 13*9a644c4bSKővágó, Zoltán char *server; 14*9a644c4bSKővágó, Zoltán char *sink; 15*9a644c4bSKővágó, Zoltán char *source; 16*9a644c4bSKővágó, Zoltán } PAConf; 17*9a644c4bSKővágó, Zoltán 18*9a644c4bSKővágó, Zoltán typedef struct { 19*9a644c4bSKővágó, Zoltán PAConf conf; 20*9a644c4bSKővágó, Zoltán pa_threaded_mainloop *mainloop; 21*9a644c4bSKővágó, Zoltán pa_context *context; 22*9a644c4bSKővágó, Zoltán } paaudio; 23*9a644c4bSKővágó, Zoltán 24*9a644c4bSKő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; 33*9a644c4bSKő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; 47*9a644c4bSKővágó, Zoltán paaudio *g; 48b8e59f18Smalc } PAVoiceIn; 49b8e59f18Smalc 50b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) 51b8e59f18Smalc { 52b8e59f18Smalc va_list ap; 53b8e59f18Smalc 54b8e59f18Smalc va_start (ap, fmt); 55b8e59f18Smalc AUD_vlog (AUDIO_CAP, fmt, ap); 56b8e59f18Smalc va_end (ap); 57b8e59f18Smalc 58b8e59f18Smalc AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); 59b8e59f18Smalc } 60b8e59f18Smalc 618f473dd1SGerd Hoffmann #ifndef PA_CONTEXT_IS_GOOD 628f473dd1SGerd Hoffmann static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) 638f473dd1SGerd Hoffmann { 648f473dd1SGerd Hoffmann return 658f473dd1SGerd Hoffmann x == PA_CONTEXT_CONNECTING || 668f473dd1SGerd Hoffmann x == PA_CONTEXT_AUTHORIZING || 678f473dd1SGerd Hoffmann x == PA_CONTEXT_SETTING_NAME || 688f473dd1SGerd Hoffmann x == PA_CONTEXT_READY; 698f473dd1SGerd Hoffmann } 708f473dd1SGerd Hoffmann #endif 718f473dd1SGerd Hoffmann 728f473dd1SGerd Hoffmann #ifndef PA_STREAM_IS_GOOD 738f473dd1SGerd Hoffmann static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) 748f473dd1SGerd Hoffmann { 758f473dd1SGerd Hoffmann return 768f473dd1SGerd Hoffmann x == PA_STREAM_CREATING || 778f473dd1SGerd Hoffmann x == PA_STREAM_READY; 788f473dd1SGerd Hoffmann } 798f473dd1SGerd Hoffmann #endif 808f473dd1SGerd Hoffmann 81ea9ebc2cSMarc-André Lureau #define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ 82ea9ebc2cSMarc-André Lureau do { \ 83ea9ebc2cSMarc-André Lureau if (!(expression)) { \ 84ea9ebc2cSMarc-André Lureau if (rerror) { \ 85ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 86ea9ebc2cSMarc-André Lureau } \ 87ea9ebc2cSMarc-André Lureau goto label; \ 88ea9ebc2cSMarc-André Lureau } \ 89ea9ebc2cSMarc-André Lureau } while (0); 90ea9ebc2cSMarc-André Lureau 91ea9ebc2cSMarc-André Lureau #define CHECK_DEAD_GOTO(c, stream, rerror, label) \ 92ea9ebc2cSMarc-André Lureau do { \ 93ea9ebc2cSMarc-André Lureau if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ 94ea9ebc2cSMarc-André Lureau !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ 95ea9ebc2cSMarc-André Lureau if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ 96ea9ebc2cSMarc-André Lureau ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ 97ea9ebc2cSMarc-André Lureau if (rerror) { \ 98ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 99ea9ebc2cSMarc-André Lureau } \ 100ea9ebc2cSMarc-André Lureau } else { \ 101ea9ebc2cSMarc-André Lureau if (rerror) { \ 102ea9ebc2cSMarc-André Lureau *(rerror) = PA_ERR_BADSTATE; \ 103ea9ebc2cSMarc-André Lureau } \ 104ea9ebc2cSMarc-André Lureau } \ 105ea9ebc2cSMarc-André Lureau goto label; \ 106ea9ebc2cSMarc-André Lureau } \ 107ea9ebc2cSMarc-André Lureau } while (0); 108ea9ebc2cSMarc-André Lureau 109ea9ebc2cSMarc-André Lureau static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) 110ea9ebc2cSMarc-André Lureau { 111*9a644c4bSKővágó, Zoltán paaudio *g = p->g; 112ea9ebc2cSMarc-André Lureau 113ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 114ea9ebc2cSMarc-André Lureau 115ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 116ea9ebc2cSMarc-André Lureau 117ea9ebc2cSMarc-André Lureau while (length > 0) { 118ea9ebc2cSMarc-André Lureau size_t l; 119ea9ebc2cSMarc-André Lureau 120ea9ebc2cSMarc-André Lureau while (!p->read_data) { 121ea9ebc2cSMarc-André Lureau int r; 122ea9ebc2cSMarc-André Lureau 123ea9ebc2cSMarc-André Lureau r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); 124ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 125ea9ebc2cSMarc-André Lureau 126ea9ebc2cSMarc-André Lureau if (!p->read_data) { 127ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 128ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 129ea9ebc2cSMarc-André Lureau } else { 130ea9ebc2cSMarc-André Lureau p->read_index = 0; 131ea9ebc2cSMarc-André Lureau } 132ea9ebc2cSMarc-André Lureau } 133ea9ebc2cSMarc-André Lureau 134ea9ebc2cSMarc-André Lureau l = p->read_length < length ? p->read_length : length; 135ea9ebc2cSMarc-André Lureau memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); 136ea9ebc2cSMarc-André Lureau 137ea9ebc2cSMarc-André Lureau data = (uint8_t *) data + l; 138ea9ebc2cSMarc-André Lureau length -= l; 139ea9ebc2cSMarc-André Lureau 140ea9ebc2cSMarc-André Lureau p->read_index += l; 141ea9ebc2cSMarc-André Lureau p->read_length -= l; 142ea9ebc2cSMarc-André Lureau 143ea9ebc2cSMarc-André Lureau if (!p->read_length) { 144ea9ebc2cSMarc-André Lureau int r; 145ea9ebc2cSMarc-André Lureau 146ea9ebc2cSMarc-André Lureau r = pa_stream_drop (p->stream); 147ea9ebc2cSMarc-André Lureau p->read_data = NULL; 148ea9ebc2cSMarc-André Lureau p->read_length = 0; 149ea9ebc2cSMarc-André Lureau p->read_index = 0; 150ea9ebc2cSMarc-André Lureau 151ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 152ea9ebc2cSMarc-André Lureau } 153ea9ebc2cSMarc-André Lureau } 154ea9ebc2cSMarc-André Lureau 155ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 156ea9ebc2cSMarc-André Lureau return 0; 157ea9ebc2cSMarc-André Lureau 158ea9ebc2cSMarc-André Lureau unlock_and_fail: 159ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 160ea9ebc2cSMarc-André Lureau return -1; 161ea9ebc2cSMarc-André Lureau } 162ea9ebc2cSMarc-André Lureau 163ea9ebc2cSMarc-André Lureau static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) 164ea9ebc2cSMarc-André Lureau { 165*9a644c4bSKővágó, Zoltán paaudio *g = p->g; 166ea9ebc2cSMarc-André Lureau 167ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 168ea9ebc2cSMarc-André Lureau 169ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 170ea9ebc2cSMarc-André Lureau 171ea9ebc2cSMarc-André Lureau while (length > 0) { 172ea9ebc2cSMarc-André Lureau size_t l; 173ea9ebc2cSMarc-André Lureau int r; 174ea9ebc2cSMarc-André Lureau 175ea9ebc2cSMarc-André Lureau while (!(l = pa_stream_writable_size (p->stream))) { 176ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 177ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 178ea9ebc2cSMarc-André Lureau } 179ea9ebc2cSMarc-André Lureau 180ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); 181ea9ebc2cSMarc-André Lureau 182ea9ebc2cSMarc-André Lureau if (l > length) { 183ea9ebc2cSMarc-André Lureau l = length; 184ea9ebc2cSMarc-André Lureau } 185ea9ebc2cSMarc-André Lureau 186ea9ebc2cSMarc-André Lureau r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); 187ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); 188ea9ebc2cSMarc-André Lureau 189ea9ebc2cSMarc-André Lureau data = (const uint8_t *) data + l; 190ea9ebc2cSMarc-André Lureau length -= l; 191ea9ebc2cSMarc-André Lureau } 192ea9ebc2cSMarc-André Lureau 193ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 194ea9ebc2cSMarc-André Lureau return 0; 195ea9ebc2cSMarc-André Lureau 196ea9ebc2cSMarc-André Lureau unlock_and_fail: 197ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 198ea9ebc2cSMarc-André Lureau return -1; 199ea9ebc2cSMarc-André Lureau } 200ea9ebc2cSMarc-André Lureau 201b8e59f18Smalc static void *qpa_thread_out (void *arg) 202b8e59f18Smalc { 203b8e59f18Smalc PAVoiceOut *pa = arg; 204b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 205b8e59f18Smalc 206b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 207b8e59f18Smalc return NULL; 208b8e59f18Smalc } 209b8e59f18Smalc 210b8e59f18Smalc for (;;) { 211b8e59f18Smalc int decr, to_mix, rpos; 212b8e59f18Smalc 213b8e59f18Smalc for (;;) { 214b8e59f18Smalc if (pa->done) { 215b8e59f18Smalc goto exit; 216b8e59f18Smalc } 217b8e59f18Smalc 2186315633bSGerd Hoffmann if (pa->live > 0) { 219b8e59f18Smalc break; 220b8e59f18Smalc } 221b8e59f18Smalc 222b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 223b8e59f18Smalc goto exit; 224b8e59f18Smalc } 225b8e59f18Smalc } 226b8e59f18Smalc 227*9a644c4bSKővágó, Zoltán decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); 2286315633bSGerd Hoffmann rpos = pa->rpos; 229b8e59f18Smalc 230b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 231b8e59f18Smalc return NULL; 232b8e59f18Smalc } 233b8e59f18Smalc 234b8e59f18Smalc while (to_mix) { 235b8e59f18Smalc int error; 236b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 2371ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 238b8e59f18Smalc 239b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 240b8e59f18Smalc 241ea9ebc2cSMarc-André Lureau if (qpa_simple_write (pa, pa->pcm_buf, 242b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 243b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 244b8e59f18Smalc return NULL; 245b8e59f18Smalc } 246b8e59f18Smalc 247b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 248b8e59f18Smalc to_mix -= chunk; 249b8e59f18Smalc } 250b8e59f18Smalc 251b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 252b8e59f18Smalc return NULL; 253b8e59f18Smalc } 254b8e59f18Smalc 255b8e59f18Smalc pa->rpos = rpos; 2566315633bSGerd Hoffmann pa->live -= decr; 257b8e59f18Smalc pa->decr += decr; 258b8e59f18Smalc } 259b8e59f18Smalc 260b8e59f18Smalc exit: 261b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 262b8e59f18Smalc return NULL; 263b8e59f18Smalc } 264b8e59f18Smalc 265bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 266b8e59f18Smalc { 267bdff253cSmalc int decr; 268b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 269b8e59f18Smalc 270b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 271b8e59f18Smalc return 0; 272b8e59f18Smalc } 273b8e59f18Smalc 274b8e59f18Smalc decr = audio_MIN (live, pa->decr); 275b8e59f18Smalc pa->decr -= decr; 276b8e59f18Smalc pa->live = live - decr; 277b8e59f18Smalc hw->rpos = pa->rpos; 278b8e59f18Smalc if (pa->live > 0) { 279b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 280b8e59f18Smalc } 281b8e59f18Smalc else { 282b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 283b8e59f18Smalc } 284b8e59f18Smalc return decr; 285b8e59f18Smalc } 286b8e59f18Smalc 287b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 288b8e59f18Smalc { 289b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 290b8e59f18Smalc } 291b8e59f18Smalc 292b8e59f18Smalc /* capture */ 293b8e59f18Smalc static void *qpa_thread_in (void *arg) 294b8e59f18Smalc { 295b8e59f18Smalc PAVoiceIn *pa = arg; 296b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 297b8e59f18Smalc 298b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 299b8e59f18Smalc return NULL; 300b8e59f18Smalc } 301b8e59f18Smalc 302b8e59f18Smalc for (;;) { 303b8e59f18Smalc int incr, to_grab, wpos; 304b8e59f18Smalc 305b8e59f18Smalc for (;;) { 306b8e59f18Smalc if (pa->done) { 307b8e59f18Smalc goto exit; 308b8e59f18Smalc } 309b8e59f18Smalc 3106315633bSGerd Hoffmann if (pa->dead > 0) { 311b8e59f18Smalc break; 312b8e59f18Smalc } 313b8e59f18Smalc 314b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 315b8e59f18Smalc goto exit; 316b8e59f18Smalc } 317b8e59f18Smalc } 318b8e59f18Smalc 319*9a644c4bSKővágó, Zoltán incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); 3206315633bSGerd Hoffmann wpos = pa->wpos; 321b8e59f18Smalc 322b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 323b8e59f18Smalc return NULL; 324b8e59f18Smalc } 325b8e59f18Smalc 326b8e59f18Smalc while (to_grab) { 327b8e59f18Smalc int error; 328b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 329b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 330b8e59f18Smalc 331ea9ebc2cSMarc-André Lureau if (qpa_simple_read (pa, buf, 332b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 333b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 334b8e59f18Smalc return NULL; 335b8e59f18Smalc } 336b8e59f18Smalc 33700e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 338b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 339b8e59f18Smalc to_grab -= chunk; 340b8e59f18Smalc } 341b8e59f18Smalc 342b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 343b8e59f18Smalc return NULL; 344b8e59f18Smalc } 345b8e59f18Smalc 346b8e59f18Smalc pa->wpos = wpos; 347b8e59f18Smalc pa->dead -= incr; 348b8e59f18Smalc pa->incr += incr; 349b8e59f18Smalc } 350b8e59f18Smalc 351b8e59f18Smalc exit: 352b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 353b8e59f18Smalc return NULL; 354b8e59f18Smalc } 355b8e59f18Smalc 356b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 357b8e59f18Smalc { 358b8e59f18Smalc int live, incr, dead; 359b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 360b8e59f18Smalc 361b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 362b8e59f18Smalc return 0; 363b8e59f18Smalc } 364b8e59f18Smalc 365b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 366b8e59f18Smalc dead = hw->samples - live; 367b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 368b8e59f18Smalc pa->incr -= incr; 369b8e59f18Smalc pa->dead = dead - incr; 370b8e59f18Smalc hw->wpos = pa->wpos; 371b8e59f18Smalc if (pa->dead > 0) { 372b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 373b8e59f18Smalc } 374b8e59f18Smalc else { 375b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 376b8e59f18Smalc } 377b8e59f18Smalc return incr; 378b8e59f18Smalc } 379b8e59f18Smalc 380b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 381b8e59f18Smalc { 382b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 383b8e59f18Smalc } 384b8e59f18Smalc 385b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 386b8e59f18Smalc { 387b8e59f18Smalc int format; 388b8e59f18Smalc 389b8e59f18Smalc switch (afmt) { 390b8e59f18Smalc case AUD_FMT_S8: 391b8e59f18Smalc case AUD_FMT_U8: 392b8e59f18Smalc format = PA_SAMPLE_U8; 393b8e59f18Smalc break; 394b8e59f18Smalc case AUD_FMT_S16: 395b8e59f18Smalc case AUD_FMT_U16: 396b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 397b8e59f18Smalc break; 398b8e59f18Smalc case AUD_FMT_S32: 399b8e59f18Smalc case AUD_FMT_U32: 400b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 401b8e59f18Smalc break; 402b8e59f18Smalc default: 403b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 404b8e59f18Smalc format = PA_SAMPLE_U8; 405b8e59f18Smalc break; 406b8e59f18Smalc } 407b8e59f18Smalc return format; 408b8e59f18Smalc } 409b8e59f18Smalc 410b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 411b8e59f18Smalc { 412b8e59f18Smalc switch (fmt) { 413b8e59f18Smalc case PA_SAMPLE_U8: 414b8e59f18Smalc return AUD_FMT_U8; 415b8e59f18Smalc case PA_SAMPLE_S16BE: 416b8e59f18Smalc *endianness = 1; 417b8e59f18Smalc return AUD_FMT_S16; 418b8e59f18Smalc case PA_SAMPLE_S16LE: 419b8e59f18Smalc *endianness = 0; 420b8e59f18Smalc return AUD_FMT_S16; 421b8e59f18Smalc case PA_SAMPLE_S32BE: 422b8e59f18Smalc *endianness = 1; 423b8e59f18Smalc return AUD_FMT_S32; 424b8e59f18Smalc case PA_SAMPLE_S32LE: 425b8e59f18Smalc *endianness = 0; 426b8e59f18Smalc return AUD_FMT_S32; 427b8e59f18Smalc default: 428b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 429b8e59f18Smalc return AUD_FMT_U8; 430b8e59f18Smalc } 431b8e59f18Smalc } 432b8e59f18Smalc 433ea9ebc2cSMarc-André Lureau static void context_state_cb (pa_context *c, void *userdata) 434ea9ebc2cSMarc-André Lureau { 435*9a644c4bSKővágó, Zoltán paaudio *g = userdata; 436ea9ebc2cSMarc-André Lureau 437ea9ebc2cSMarc-André Lureau switch (pa_context_get_state(c)) { 438ea9ebc2cSMarc-André Lureau case PA_CONTEXT_READY: 439ea9ebc2cSMarc-André Lureau case PA_CONTEXT_TERMINATED: 440ea9ebc2cSMarc-André Lureau case PA_CONTEXT_FAILED: 441ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 442ea9ebc2cSMarc-André Lureau break; 443ea9ebc2cSMarc-André Lureau 444ea9ebc2cSMarc-André Lureau case PA_CONTEXT_UNCONNECTED: 445ea9ebc2cSMarc-André Lureau case PA_CONTEXT_CONNECTING: 446ea9ebc2cSMarc-André Lureau case PA_CONTEXT_AUTHORIZING: 447ea9ebc2cSMarc-André Lureau case PA_CONTEXT_SETTING_NAME: 448ea9ebc2cSMarc-André Lureau break; 449ea9ebc2cSMarc-André Lureau } 450ea9ebc2cSMarc-André Lureau } 451ea9ebc2cSMarc-André Lureau 452ea9ebc2cSMarc-André Lureau static void stream_state_cb (pa_stream *s, void * userdata) 453ea9ebc2cSMarc-André Lureau { 454*9a644c4bSKővágó, Zoltán paaudio *g = userdata; 455ea9ebc2cSMarc-André Lureau 456ea9ebc2cSMarc-André Lureau switch (pa_stream_get_state (s)) { 457ea9ebc2cSMarc-André Lureau 458ea9ebc2cSMarc-André Lureau case PA_STREAM_READY: 459ea9ebc2cSMarc-André Lureau case PA_STREAM_FAILED: 460ea9ebc2cSMarc-André Lureau case PA_STREAM_TERMINATED: 461ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 462ea9ebc2cSMarc-André Lureau break; 463ea9ebc2cSMarc-André Lureau 464ea9ebc2cSMarc-André Lureau case PA_STREAM_UNCONNECTED: 465ea9ebc2cSMarc-André Lureau case PA_STREAM_CREATING: 466ea9ebc2cSMarc-André Lureau break; 467ea9ebc2cSMarc-André Lureau } 468ea9ebc2cSMarc-André Lureau } 469ea9ebc2cSMarc-André Lureau 470ea9ebc2cSMarc-André Lureau static void stream_request_cb (pa_stream *s, size_t length, void *userdata) 471ea9ebc2cSMarc-André Lureau { 472*9a644c4bSKővágó, Zoltán paaudio *g = userdata; 473ea9ebc2cSMarc-André Lureau 474ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 475ea9ebc2cSMarc-André Lureau } 476ea9ebc2cSMarc-André Lureau 477ea9ebc2cSMarc-André Lureau static pa_stream *qpa_simple_new ( 478*9a644c4bSKővágó, Zoltán paaudio *g, 479ea9ebc2cSMarc-André Lureau const char *name, 480ea9ebc2cSMarc-André Lureau pa_stream_direction_t dir, 481ea9ebc2cSMarc-André Lureau const char *dev, 482ea9ebc2cSMarc-André Lureau const pa_sample_spec *ss, 483ea9ebc2cSMarc-André Lureau const pa_channel_map *map, 484ea9ebc2cSMarc-André Lureau const pa_buffer_attr *attr, 485ea9ebc2cSMarc-André Lureau int *rerror) 486ea9ebc2cSMarc-André Lureau { 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 5375706db1dSKővágó, Zoltán static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, 5385706db1dSKővágó, Zoltán void *drv_opaque) 539b8e59f18Smalc { 540b8e59f18Smalc int error; 541*9a644c4bSKővágó, Zoltán pa_sample_spec ss; 542*9a644c4bSKővágó, Zoltán pa_buffer_attr ba; 5431ea879e5Smalc struct audsettings obt_as = *as; 544b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 545*9a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 546b8e59f18Smalc 547b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 548b8e59f18Smalc ss.channels = as->nchannels; 549b8e59f18Smalc ss.rate = as->freq; 550b8e59f18Smalc 551e6d16fa4SGerd Hoffmann /* 5520e8ae611SGerd Hoffmann * qemu audio tick runs at 100 Hz (by default), so processing 5530e8ae611SGerd Hoffmann * data chunks worth 10 ms of sound should be a good fit. 554e6d16fa4SGerd Hoffmann */ 5550e8ae611SGerd Hoffmann ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); 5560e8ae611SGerd Hoffmann ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); 557e6d16fa4SGerd Hoffmann ba.maxlength = -1; 558e6d16fa4SGerd Hoffmann ba.prebuf = -1; 559e6d16fa4SGerd Hoffmann 560b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 561b8e59f18Smalc 562ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 563*9a644c4bSKővágó, Zoltán g, 564b8e59f18Smalc "qemu", 565b8e59f18Smalc PA_STREAM_PLAYBACK, 566*9a644c4bSKővágó, Zoltán g->conf.sink, 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); 578*9a644c4bSKővágó, Zoltán hw->samples = g->conf.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 6055706db1dSKővágó, Zoltán static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 606b8e59f18Smalc { 607b8e59f18Smalc int error; 608*9a644c4bSKővágó, Zoltán pa_sample_spec ss; 6091ea879e5Smalc struct audsettings obt_as = *as; 610b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 611*9a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 612b8e59f18Smalc 613b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 614b8e59f18Smalc ss.channels = as->nchannels; 615b8e59f18Smalc ss.rate = as->freq; 616b8e59f18Smalc 617b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 618b8e59f18Smalc 619ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 620*9a644c4bSKővágó, Zoltán g, 621b8e59f18Smalc "qemu", 622b8e59f18Smalc PA_STREAM_RECORD, 623*9a644c4bSKővágó, Zoltán g->conf.source, 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); 635*9a644c4bSKővágó, Zoltán hw->samples = g->conf.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; 707*9a644c4bSKővágó, Zoltán paaudio *g = pa->g; 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; 759*9a644c4bSKővágó, Zoltán paaudio *g = pa->g; 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 */ 809*9a644c4bSKővágó, Zoltán static PAConf glob_conf = { 810*9a644c4bSKővágó, Zoltán .samples = 4096, 811*9a644c4bSKővágó, Zoltán }; 812*9a644c4bSKővágó, Zoltán 813b8e59f18Smalc static void *qpa_audio_init (void) 814b8e59f18Smalc { 815*9a644c4bSKővágó, Zoltán paaudio *g = g_malloc(sizeof(paaudio)); 816*9a644c4bSKővágó, Zoltán g->conf = glob_conf; 817ea9ebc2cSMarc-André Lureau 818ea9ebc2cSMarc-André Lureau g->mainloop = pa_threaded_mainloop_new (); 819ea9ebc2cSMarc-André Lureau if (!g->mainloop) { 820ea9ebc2cSMarc-André Lureau goto fail; 821ea9ebc2cSMarc-André Lureau } 822ea9ebc2cSMarc-André Lureau 823*9a644c4bSKővágó, Zoltán g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), 824*9a644c4bSKővágó, Zoltán g->conf.server); 825ea9ebc2cSMarc-André Lureau if (!g->context) { 826ea9ebc2cSMarc-André Lureau goto fail; 827ea9ebc2cSMarc-André Lureau } 828ea9ebc2cSMarc-André Lureau 829ea9ebc2cSMarc-André Lureau pa_context_set_state_callback (g->context, context_state_cb, g); 830ea9ebc2cSMarc-André Lureau 831*9a644c4bSKővágó, Zoltán if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { 832ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 833ea9ebc2cSMarc-André Lureau "pa_context_connect() failed\n"); 834ea9ebc2cSMarc-André Lureau goto fail; 835ea9ebc2cSMarc-André Lureau } 836ea9ebc2cSMarc-André Lureau 837ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 838ea9ebc2cSMarc-André Lureau 839ea9ebc2cSMarc-André Lureau if (pa_threaded_mainloop_start (g->mainloop) < 0) { 840ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 841ea9ebc2cSMarc-André Lureau } 842ea9ebc2cSMarc-André Lureau 843ea9ebc2cSMarc-André Lureau for (;;) { 844ea9ebc2cSMarc-André Lureau pa_context_state_t state; 845ea9ebc2cSMarc-André Lureau 846ea9ebc2cSMarc-André Lureau state = pa_context_get_state (g->context); 847ea9ebc2cSMarc-André Lureau 848ea9ebc2cSMarc-André Lureau if (state == PA_CONTEXT_READY) { 849ea9ebc2cSMarc-André Lureau break; 850ea9ebc2cSMarc-André Lureau } 851ea9ebc2cSMarc-André Lureau 852ea9ebc2cSMarc-André Lureau if (!PA_CONTEXT_IS_GOOD (state)) { 853ea9ebc2cSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 854ea9ebc2cSMarc-André Lureau "Wrong context state\n"); 855ea9ebc2cSMarc-André Lureau goto unlock_and_fail; 856ea9ebc2cSMarc-André Lureau } 857ea9ebc2cSMarc-André Lureau 858ea9ebc2cSMarc-André Lureau /* Wait until the context is ready */ 859ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 860ea9ebc2cSMarc-André Lureau } 861ea9ebc2cSMarc-André Lureau 862ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 863ea9ebc2cSMarc-André Lureau 864*9a644c4bSKővágó, Zoltán return g; 865ea9ebc2cSMarc-André Lureau 866ea9ebc2cSMarc-André Lureau unlock_and_fail: 867ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 868ea9ebc2cSMarc-André Lureau fail: 869ea9ebc2cSMarc-André Lureau AUD_log (AUDIO_CAP, "Failed to initialize PA context"); 870*9a644c4bSKővágó, Zoltán g_free(g); 871ea9ebc2cSMarc-André Lureau return NULL; 872b8e59f18Smalc } 873b8e59f18Smalc 874b8e59f18Smalc static void qpa_audio_fini (void *opaque) 875b8e59f18Smalc { 876ea9ebc2cSMarc-André Lureau paaudio *g = opaque; 877ea9ebc2cSMarc-André Lureau 878ea9ebc2cSMarc-André Lureau if (g->mainloop) { 879ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_stop (g->mainloop); 880ea9ebc2cSMarc-André Lureau } 881ea9ebc2cSMarc-André Lureau 882ea9ebc2cSMarc-André Lureau if (g->context) { 883ea9ebc2cSMarc-André Lureau pa_context_disconnect (g->context); 884ea9ebc2cSMarc-André Lureau pa_context_unref (g->context); 885ea9ebc2cSMarc-André Lureau } 886ea9ebc2cSMarc-André Lureau 887ea9ebc2cSMarc-André Lureau if (g->mainloop) { 888ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_free (g->mainloop); 889ea9ebc2cSMarc-André Lureau } 890ea9ebc2cSMarc-André Lureau 891*9a644c4bSKővágó, Zoltán g_free(g); 892b8e59f18Smalc } 893b8e59f18Smalc 894b8e59f18Smalc struct audio_option qpa_options[] = { 89598f9f48cSmalc { 89698f9f48cSmalc .name = "SAMPLES", 8972700efa3SJuan Quintela .tag = AUD_OPT_INT, 898*9a644c4bSKővágó, Zoltán .valp = &glob_conf.samples, 89998f9f48cSmalc .descr = "buffer size in samples" 90098f9f48cSmalc }, 90198f9f48cSmalc { 90298f9f48cSmalc .name = "SERVER", 9032700efa3SJuan Quintela .tag = AUD_OPT_STR, 904*9a644c4bSKővágó, Zoltán .valp = &glob_conf.server, 90598f9f48cSmalc .descr = "server address" 90698f9f48cSmalc }, 90798f9f48cSmalc { 90898f9f48cSmalc .name = "SINK", 9092700efa3SJuan Quintela .tag = AUD_OPT_STR, 910*9a644c4bSKővágó, Zoltán .valp = &glob_conf.sink, 91198f9f48cSmalc .descr = "sink device name" 91298f9f48cSmalc }, 91398f9f48cSmalc { 91498f9f48cSmalc .name = "SOURCE", 9152700efa3SJuan Quintela .tag = AUD_OPT_STR, 916*9a644c4bSKővágó, Zoltán .valp = &glob_conf.source, 91798f9f48cSmalc .descr = "source device name" 91898f9f48cSmalc }, 9192700efa3SJuan Quintela { /* End of list */ } 920b8e59f18Smalc }; 921b8e59f18Smalc 92235f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = { 9231dd3e4d1SJuan Quintela .init_out = qpa_init_out, 9241dd3e4d1SJuan Quintela .fini_out = qpa_fini_out, 9251dd3e4d1SJuan Quintela .run_out = qpa_run_out, 9261dd3e4d1SJuan Quintela .write = qpa_write, 9271dd3e4d1SJuan Quintela .ctl_out = qpa_ctl_out, 9281dd3e4d1SJuan Quintela 9291dd3e4d1SJuan Quintela .init_in = qpa_init_in, 9301dd3e4d1SJuan Quintela .fini_in = qpa_fini_in, 9311dd3e4d1SJuan Quintela .run_in = qpa_run_in, 9321dd3e4d1SJuan Quintela .read = qpa_read, 9331dd3e4d1SJuan Quintela .ctl_in = qpa_ctl_in 934b8e59f18Smalc }; 935b8e59f18Smalc 936b8e59f18Smalc struct audio_driver pa_audio_driver = { 937bee37f32SJuan Quintela .name = "pa", 938bee37f32SJuan Quintela .descr = "http://www.pulseaudio.org/", 939bee37f32SJuan Quintela .options = qpa_options, 940bee37f32SJuan Quintela .init = qpa_audio_init, 941bee37f32SJuan Quintela .fini = qpa_audio_fini, 942bee37f32SJuan Quintela .pcm_ops = &qpa_pcm_ops, 9431a4ea1e3SMichael S. Tsirkin .can_be_default = 1, 944bee37f32SJuan Quintela .max_voices_out = INT_MAX, 945bee37f32SJuan Quintela .max_voices_in = INT_MAX, 946bee37f32SJuan Quintela .voice_size_out = sizeof (PAVoiceOut), 9476e7a7f3dSMarc-André Lureau .voice_size_in = sizeof (PAVoiceIn), 9486e7a7f3dSMarc-André Lureau .ctl_caps = VOICE_VOLUME_CAP 949b8e59f18Smalc }; 950