1b8e59f18Smalc /* public domain */ 26086a565SPeter Maydell #include "qemu/osdep.h" 3b8e59f18Smalc #include "qemu-common.h" 4b8e59f18Smalc #include "audio.h" 5b8e59f18Smalc 6ea9ebc2cSMarc-André Lureau #include <pulse/pulseaudio.h> 7b8e59f18Smalc 8b8e59f18Smalc #define AUDIO_CAP "pulseaudio" 9b8e59f18Smalc #include "audio_int.h" 10b8e59f18Smalc #include "audio_pt_int.h" 11b8e59f18Smalc 12b8e59f18Smalc typedef struct { 139a644c4bSKővágó, Zoltán int samples; 149a644c4bSKővágó, Zoltán char *server; 159a644c4bSKővágó, Zoltán char *sink; 169a644c4bSKővágó, Zoltán char *source; 179a644c4bSKővágó, Zoltán } PAConf; 189a644c4bSKővágó, Zoltán 199a644c4bSKővágó, Zoltán typedef struct { 209a644c4bSKővágó, Zoltán PAConf conf; 219a644c4bSKővágó, Zoltán pa_threaded_mainloop *mainloop; 229a644c4bSKővágó, Zoltán pa_context *context; 239a644c4bSKővágó, Zoltán } paaudio; 249a644c4bSKővágó, Zoltán 259a644c4bSKővágó, Zoltán typedef struct { 26b8e59f18Smalc HWVoiceOut hw; 27b8e59f18Smalc int done; 28b8e59f18Smalc int live; 29b8e59f18Smalc int decr; 30b8e59f18Smalc int rpos; 31ea9ebc2cSMarc-André Lureau pa_stream *stream; 32b8e59f18Smalc void *pcm_buf; 33b8e59f18Smalc struct audio_pt pt; 349a644c4bSKővágó, Zoltán paaudio *g; 35b8e59f18Smalc } PAVoiceOut; 36b8e59f18Smalc 37b8e59f18Smalc typedef struct { 38b8e59f18Smalc HWVoiceIn hw; 39b8e59f18Smalc int done; 40b8e59f18Smalc int dead; 41b8e59f18Smalc int incr; 42b8e59f18Smalc int wpos; 43ea9ebc2cSMarc-André Lureau pa_stream *stream; 44b8e59f18Smalc void *pcm_buf; 45b8e59f18Smalc struct audio_pt pt; 46ea9ebc2cSMarc-André Lureau const void *read_data; 47ea9ebc2cSMarc-André Lureau size_t read_index, read_length; 489a644c4bSKővágó, Zoltán paaudio *g; 49b8e59f18Smalc } PAVoiceIn; 50b8e59f18Smalc 5149dd6d0dSKővágó, Zoltán static void qpa_audio_fini(void *opaque); 5249dd6d0dSKővágó, Zoltán 53b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) 54b8e59f18Smalc { 55b8e59f18Smalc va_list ap; 56b8e59f18Smalc 57b8e59f18Smalc va_start (ap, fmt); 58b8e59f18Smalc AUD_vlog (AUDIO_CAP, fmt, ap); 59b8e59f18Smalc va_end (ap); 60b8e59f18Smalc 61b8e59f18Smalc AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); 62b8e59f18Smalc } 63b8e59f18Smalc 648f473dd1SGerd Hoffmann #ifndef PA_CONTEXT_IS_GOOD 658f473dd1SGerd Hoffmann static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) 668f473dd1SGerd Hoffmann { 678f473dd1SGerd Hoffmann return 688f473dd1SGerd Hoffmann x == PA_CONTEXT_CONNECTING || 698f473dd1SGerd Hoffmann x == PA_CONTEXT_AUTHORIZING || 708f473dd1SGerd Hoffmann x == PA_CONTEXT_SETTING_NAME || 718f473dd1SGerd Hoffmann x == PA_CONTEXT_READY; 728f473dd1SGerd Hoffmann } 738f473dd1SGerd Hoffmann #endif 748f473dd1SGerd Hoffmann 758f473dd1SGerd Hoffmann #ifndef PA_STREAM_IS_GOOD 768f473dd1SGerd Hoffmann static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) 778f473dd1SGerd Hoffmann { 788f473dd1SGerd Hoffmann return 798f473dd1SGerd Hoffmann x == PA_STREAM_CREATING || 808f473dd1SGerd Hoffmann x == PA_STREAM_READY; 818f473dd1SGerd Hoffmann } 828f473dd1SGerd Hoffmann #endif 838f473dd1SGerd Hoffmann 84ea9ebc2cSMarc-André Lureau #define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \ 85ea9ebc2cSMarc-André Lureau do { \ 86ea9ebc2cSMarc-André Lureau if (!(expression)) { \ 87ea9ebc2cSMarc-André Lureau if (rerror) { \ 88ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 89ea9ebc2cSMarc-André Lureau } \ 90ea9ebc2cSMarc-André Lureau goto label; \ 91ea9ebc2cSMarc-André Lureau } \ 92*2562755eSEric Blake } while (0) 93ea9ebc2cSMarc-André Lureau 94ea9ebc2cSMarc-André Lureau #define CHECK_DEAD_GOTO(c, stream, rerror, label) \ 95ea9ebc2cSMarc-André Lureau do { \ 96ea9ebc2cSMarc-André Lureau if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ 97ea9ebc2cSMarc-André Lureau !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ 98ea9ebc2cSMarc-André Lureau if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ 99ea9ebc2cSMarc-André Lureau ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ 100ea9ebc2cSMarc-André Lureau if (rerror) { \ 101ea9ebc2cSMarc-André Lureau *(rerror) = pa_context_errno ((c)->context); \ 102ea9ebc2cSMarc-André Lureau } \ 103ea9ebc2cSMarc-André Lureau } else { \ 104ea9ebc2cSMarc-André Lureau if (rerror) { \ 105ea9ebc2cSMarc-André Lureau *(rerror) = PA_ERR_BADSTATE; \ 106ea9ebc2cSMarc-André Lureau } \ 107ea9ebc2cSMarc-André Lureau } \ 108ea9ebc2cSMarc-André Lureau goto label; \ 109ea9ebc2cSMarc-André Lureau } \ 110*2562755eSEric Blake } while (0) 111ea9ebc2cSMarc-André Lureau 112ea9ebc2cSMarc-André Lureau static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) 113ea9ebc2cSMarc-André Lureau { 1149a644c4bSKővágó, Zoltán paaudio *g = p->g; 115ea9ebc2cSMarc-André Lureau 116ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 117ea9ebc2cSMarc-André Lureau 118ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 119ea9ebc2cSMarc-André Lureau 120ea9ebc2cSMarc-André Lureau while (length > 0) { 121ea9ebc2cSMarc-André Lureau size_t l; 122ea9ebc2cSMarc-André Lureau 123ea9ebc2cSMarc-André Lureau while (!p->read_data) { 124ea9ebc2cSMarc-André Lureau int r; 125ea9ebc2cSMarc-André Lureau 126ea9ebc2cSMarc-André Lureau r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); 127ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 128ea9ebc2cSMarc-André Lureau 129ea9ebc2cSMarc-André Lureau if (!p->read_data) { 130ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 131ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 132ea9ebc2cSMarc-André Lureau } else { 133ea9ebc2cSMarc-André Lureau p->read_index = 0; 134ea9ebc2cSMarc-André Lureau } 135ea9ebc2cSMarc-André Lureau } 136ea9ebc2cSMarc-André Lureau 137ea9ebc2cSMarc-André Lureau l = p->read_length < length ? p->read_length : length; 138ea9ebc2cSMarc-André Lureau memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); 139ea9ebc2cSMarc-André Lureau 140ea9ebc2cSMarc-André Lureau data = (uint8_t *) data + l; 141ea9ebc2cSMarc-André Lureau length -= l; 142ea9ebc2cSMarc-André Lureau 143ea9ebc2cSMarc-André Lureau p->read_index += l; 144ea9ebc2cSMarc-André Lureau p->read_length -= l; 145ea9ebc2cSMarc-André Lureau 146ea9ebc2cSMarc-André Lureau if (!p->read_length) { 147ea9ebc2cSMarc-André Lureau int r; 148ea9ebc2cSMarc-André Lureau 149ea9ebc2cSMarc-André Lureau r = pa_stream_drop (p->stream); 150ea9ebc2cSMarc-André Lureau p->read_data = NULL; 151ea9ebc2cSMarc-André Lureau p->read_length = 0; 152ea9ebc2cSMarc-André Lureau p->read_index = 0; 153ea9ebc2cSMarc-André Lureau 154ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); 155ea9ebc2cSMarc-André Lureau } 156ea9ebc2cSMarc-André Lureau } 157ea9ebc2cSMarc-André Lureau 158ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 159ea9ebc2cSMarc-André Lureau return 0; 160ea9ebc2cSMarc-André Lureau 161ea9ebc2cSMarc-André Lureau unlock_and_fail: 162ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 163ea9ebc2cSMarc-André Lureau return -1; 164ea9ebc2cSMarc-André Lureau } 165ea9ebc2cSMarc-André Lureau 166ea9ebc2cSMarc-André Lureau static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) 167ea9ebc2cSMarc-André Lureau { 1689a644c4bSKővágó, Zoltán paaudio *g = p->g; 169ea9ebc2cSMarc-André Lureau 170ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 171ea9ebc2cSMarc-André Lureau 172ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 173ea9ebc2cSMarc-André Lureau 174ea9ebc2cSMarc-André Lureau while (length > 0) { 175ea9ebc2cSMarc-André Lureau size_t l; 176ea9ebc2cSMarc-André Lureau int r; 177ea9ebc2cSMarc-André Lureau 178ea9ebc2cSMarc-André Lureau while (!(l = pa_stream_writable_size (p->stream))) { 179ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_wait (g->mainloop); 180ea9ebc2cSMarc-André Lureau CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); 181ea9ebc2cSMarc-André Lureau } 182ea9ebc2cSMarc-André Lureau 183ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); 184ea9ebc2cSMarc-André Lureau 185ea9ebc2cSMarc-André Lureau if (l > length) { 186ea9ebc2cSMarc-André Lureau l = length; 187ea9ebc2cSMarc-André Lureau } 188ea9ebc2cSMarc-André Lureau 189ea9ebc2cSMarc-André Lureau r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); 190ea9ebc2cSMarc-André Lureau CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); 191ea9ebc2cSMarc-André Lureau 192ea9ebc2cSMarc-André Lureau data = (const uint8_t *) data + l; 193ea9ebc2cSMarc-André Lureau length -= l; 194ea9ebc2cSMarc-André Lureau } 195ea9ebc2cSMarc-André Lureau 196ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 197ea9ebc2cSMarc-André Lureau return 0; 198ea9ebc2cSMarc-André Lureau 199ea9ebc2cSMarc-André Lureau unlock_and_fail: 200ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 201ea9ebc2cSMarc-André Lureau return -1; 202ea9ebc2cSMarc-André Lureau } 203ea9ebc2cSMarc-André Lureau 204b8e59f18Smalc static void *qpa_thread_out (void *arg) 205b8e59f18Smalc { 206b8e59f18Smalc PAVoiceOut *pa = arg; 207b8e59f18Smalc HWVoiceOut *hw = &pa->hw; 208b8e59f18Smalc 209b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 210b8e59f18Smalc return NULL; 211b8e59f18Smalc } 212b8e59f18Smalc 213b8e59f18Smalc for (;;) { 214b8e59f18Smalc int decr, to_mix, rpos; 215b8e59f18Smalc 216b8e59f18Smalc for (;;) { 217b8e59f18Smalc if (pa->done) { 218b8e59f18Smalc goto exit; 219b8e59f18Smalc } 220b8e59f18Smalc 2216315633bSGerd Hoffmann if (pa->live > 0) { 222b8e59f18Smalc break; 223b8e59f18Smalc } 224b8e59f18Smalc 225b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 226b8e59f18Smalc goto exit; 227b8e59f18Smalc } 228b8e59f18Smalc } 229b8e59f18Smalc 2309a644c4bSKővágó, Zoltán decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); 2316315633bSGerd Hoffmann rpos = pa->rpos; 232b8e59f18Smalc 233b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 234b8e59f18Smalc return NULL; 235b8e59f18Smalc } 236b8e59f18Smalc 237b8e59f18Smalc while (to_mix) { 238b8e59f18Smalc int error; 239b8e59f18Smalc int chunk = audio_MIN (to_mix, hw->samples - rpos); 2401ea879e5Smalc struct st_sample *src = hw->mix_buf + rpos; 241b8e59f18Smalc 242b8e59f18Smalc hw->clip (pa->pcm_buf, src, chunk); 243b8e59f18Smalc 244ea9ebc2cSMarc-André Lureau if (qpa_simple_write (pa, pa->pcm_buf, 245b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 246b8e59f18Smalc qpa_logerr (error, "pa_simple_write failed\n"); 247b8e59f18Smalc return NULL; 248b8e59f18Smalc } 249b8e59f18Smalc 250b8e59f18Smalc rpos = (rpos + chunk) % hw->samples; 251b8e59f18Smalc to_mix -= chunk; 252b8e59f18Smalc } 253b8e59f18Smalc 254b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 255b8e59f18Smalc return NULL; 256b8e59f18Smalc } 257b8e59f18Smalc 258b8e59f18Smalc pa->rpos = rpos; 2596315633bSGerd Hoffmann pa->live -= decr; 260b8e59f18Smalc pa->decr += decr; 261b8e59f18Smalc } 262b8e59f18Smalc 263b8e59f18Smalc exit: 264b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 265b8e59f18Smalc return NULL; 266b8e59f18Smalc } 267b8e59f18Smalc 268bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live) 269b8e59f18Smalc { 270bdff253cSmalc int decr; 271b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 272b8e59f18Smalc 273b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 274b8e59f18Smalc return 0; 275b8e59f18Smalc } 276b8e59f18Smalc 277b8e59f18Smalc decr = audio_MIN (live, pa->decr); 278b8e59f18Smalc pa->decr -= decr; 279b8e59f18Smalc pa->live = live - decr; 280b8e59f18Smalc hw->rpos = pa->rpos; 281b8e59f18Smalc if (pa->live > 0) { 282b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 283b8e59f18Smalc } 284b8e59f18Smalc else { 285b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 286b8e59f18Smalc } 287b8e59f18Smalc return decr; 288b8e59f18Smalc } 289b8e59f18Smalc 290b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len) 291b8e59f18Smalc { 292b8e59f18Smalc return audio_pcm_sw_write (sw, buf, len); 293b8e59f18Smalc } 294b8e59f18Smalc 295b8e59f18Smalc /* capture */ 296b8e59f18Smalc static void *qpa_thread_in (void *arg) 297b8e59f18Smalc { 298b8e59f18Smalc PAVoiceIn *pa = arg; 299b8e59f18Smalc HWVoiceIn *hw = &pa->hw; 300b8e59f18Smalc 301b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 302b8e59f18Smalc return NULL; 303b8e59f18Smalc } 304b8e59f18Smalc 305b8e59f18Smalc for (;;) { 306b8e59f18Smalc int incr, to_grab, wpos; 307b8e59f18Smalc 308b8e59f18Smalc for (;;) { 309b8e59f18Smalc if (pa->done) { 310b8e59f18Smalc goto exit; 311b8e59f18Smalc } 312b8e59f18Smalc 3136315633bSGerd Hoffmann if (pa->dead > 0) { 314b8e59f18Smalc break; 315b8e59f18Smalc } 316b8e59f18Smalc 317b8e59f18Smalc if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { 318b8e59f18Smalc goto exit; 319b8e59f18Smalc } 320b8e59f18Smalc } 321b8e59f18Smalc 3229a644c4bSKővágó, Zoltán incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); 3236315633bSGerd Hoffmann wpos = pa->wpos; 324b8e59f18Smalc 325b8e59f18Smalc if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { 326b8e59f18Smalc return NULL; 327b8e59f18Smalc } 328b8e59f18Smalc 329b8e59f18Smalc while (to_grab) { 330b8e59f18Smalc int error; 331b8e59f18Smalc int chunk = audio_MIN (to_grab, hw->samples - wpos); 332b8e59f18Smalc void *buf = advance (pa->pcm_buf, wpos); 333b8e59f18Smalc 334ea9ebc2cSMarc-André Lureau if (qpa_simple_read (pa, buf, 335b8e59f18Smalc chunk << hw->info.shift, &error) < 0) { 336b8e59f18Smalc qpa_logerr (error, "pa_simple_read failed\n"); 337b8e59f18Smalc return NULL; 338b8e59f18Smalc } 339b8e59f18Smalc 34000e07679SMichael Walle hw->conv (hw->conv_buf + wpos, buf, chunk); 341b8e59f18Smalc wpos = (wpos + chunk) % hw->samples; 342b8e59f18Smalc to_grab -= chunk; 343b8e59f18Smalc } 344b8e59f18Smalc 345b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 346b8e59f18Smalc return NULL; 347b8e59f18Smalc } 348b8e59f18Smalc 349b8e59f18Smalc pa->wpos = wpos; 350b8e59f18Smalc pa->dead -= incr; 351b8e59f18Smalc pa->incr += incr; 352b8e59f18Smalc } 353b8e59f18Smalc 354b8e59f18Smalc exit: 355b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 356b8e59f18Smalc return NULL; 357b8e59f18Smalc } 358b8e59f18Smalc 359b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw) 360b8e59f18Smalc { 361b8e59f18Smalc int live, incr, dead; 362b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 363b8e59f18Smalc 364b8e59f18Smalc if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { 365b8e59f18Smalc return 0; 366b8e59f18Smalc } 367b8e59f18Smalc 368b8e59f18Smalc live = audio_pcm_hw_get_live_in (hw); 369b8e59f18Smalc dead = hw->samples - live; 370b8e59f18Smalc incr = audio_MIN (dead, pa->incr); 371b8e59f18Smalc pa->incr -= incr; 372b8e59f18Smalc pa->dead = dead - incr; 373b8e59f18Smalc hw->wpos = pa->wpos; 374b8e59f18Smalc if (pa->dead > 0) { 375b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 376b8e59f18Smalc } 377b8e59f18Smalc else { 378b8e59f18Smalc audio_pt_unlock (&pa->pt, AUDIO_FUNC); 379b8e59f18Smalc } 380b8e59f18Smalc return incr; 381b8e59f18Smalc } 382b8e59f18Smalc 383b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len) 384b8e59f18Smalc { 385b8e59f18Smalc return audio_pcm_sw_read (sw, buf, len); 386b8e59f18Smalc } 387b8e59f18Smalc 388b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) 389b8e59f18Smalc { 390b8e59f18Smalc int format; 391b8e59f18Smalc 392b8e59f18Smalc switch (afmt) { 393b8e59f18Smalc case AUD_FMT_S8: 394b8e59f18Smalc case AUD_FMT_U8: 395b8e59f18Smalc format = PA_SAMPLE_U8; 396b8e59f18Smalc break; 397b8e59f18Smalc case AUD_FMT_S16: 398b8e59f18Smalc case AUD_FMT_U16: 399b8e59f18Smalc format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 400b8e59f18Smalc break; 401b8e59f18Smalc case AUD_FMT_S32: 402b8e59f18Smalc case AUD_FMT_U32: 403b8e59f18Smalc format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 404b8e59f18Smalc break; 405b8e59f18Smalc default: 406b8e59f18Smalc dolog ("Internal logic error: Bad audio format %d\n", afmt); 407b8e59f18Smalc format = PA_SAMPLE_U8; 408b8e59f18Smalc break; 409b8e59f18Smalc } 410b8e59f18Smalc return format; 411b8e59f18Smalc } 412b8e59f18Smalc 413b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 414b8e59f18Smalc { 415b8e59f18Smalc switch (fmt) { 416b8e59f18Smalc case PA_SAMPLE_U8: 417b8e59f18Smalc return AUD_FMT_U8; 418b8e59f18Smalc case PA_SAMPLE_S16BE: 419b8e59f18Smalc *endianness = 1; 420b8e59f18Smalc return AUD_FMT_S16; 421b8e59f18Smalc case PA_SAMPLE_S16LE: 422b8e59f18Smalc *endianness = 0; 423b8e59f18Smalc return AUD_FMT_S16; 424b8e59f18Smalc case PA_SAMPLE_S32BE: 425b8e59f18Smalc *endianness = 1; 426b8e59f18Smalc return AUD_FMT_S32; 427b8e59f18Smalc case PA_SAMPLE_S32LE: 428b8e59f18Smalc *endianness = 0; 429b8e59f18Smalc return AUD_FMT_S32; 430b8e59f18Smalc default: 431b8e59f18Smalc dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 432b8e59f18Smalc return AUD_FMT_U8; 433b8e59f18Smalc } 434b8e59f18Smalc } 435b8e59f18Smalc 436ea9ebc2cSMarc-André Lureau static void context_state_cb (pa_context *c, void *userdata) 437ea9ebc2cSMarc-André Lureau { 4389a644c4bSKővágó, Zoltán paaudio *g = userdata; 439ea9ebc2cSMarc-André Lureau 440ea9ebc2cSMarc-André Lureau switch (pa_context_get_state(c)) { 441ea9ebc2cSMarc-André Lureau case PA_CONTEXT_READY: 442ea9ebc2cSMarc-André Lureau case PA_CONTEXT_TERMINATED: 443ea9ebc2cSMarc-André Lureau case PA_CONTEXT_FAILED: 444ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 445ea9ebc2cSMarc-André Lureau break; 446ea9ebc2cSMarc-André Lureau 447ea9ebc2cSMarc-André Lureau case PA_CONTEXT_UNCONNECTED: 448ea9ebc2cSMarc-André Lureau case PA_CONTEXT_CONNECTING: 449ea9ebc2cSMarc-André Lureau case PA_CONTEXT_AUTHORIZING: 450ea9ebc2cSMarc-André Lureau case PA_CONTEXT_SETTING_NAME: 451ea9ebc2cSMarc-André Lureau break; 452ea9ebc2cSMarc-André Lureau } 453ea9ebc2cSMarc-André Lureau } 454ea9ebc2cSMarc-André Lureau 455ea9ebc2cSMarc-André Lureau static void stream_state_cb (pa_stream *s, void * userdata) 456ea9ebc2cSMarc-André Lureau { 4579a644c4bSKővágó, Zoltán paaudio *g = userdata; 458ea9ebc2cSMarc-André Lureau 459ea9ebc2cSMarc-André Lureau switch (pa_stream_get_state (s)) { 460ea9ebc2cSMarc-André Lureau 461ea9ebc2cSMarc-André Lureau case PA_STREAM_READY: 462ea9ebc2cSMarc-André Lureau case PA_STREAM_FAILED: 463ea9ebc2cSMarc-André Lureau case PA_STREAM_TERMINATED: 464ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 465ea9ebc2cSMarc-André Lureau break; 466ea9ebc2cSMarc-André Lureau 467ea9ebc2cSMarc-André Lureau case PA_STREAM_UNCONNECTED: 468ea9ebc2cSMarc-André Lureau case PA_STREAM_CREATING: 469ea9ebc2cSMarc-André Lureau break; 470ea9ebc2cSMarc-André Lureau } 471ea9ebc2cSMarc-André Lureau } 472ea9ebc2cSMarc-André Lureau 473ea9ebc2cSMarc-André Lureau static void stream_request_cb (pa_stream *s, size_t length, void *userdata) 474ea9ebc2cSMarc-André Lureau { 4759a644c4bSKővágó, Zoltán paaudio *g = userdata; 476ea9ebc2cSMarc-André Lureau 477ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_signal (g->mainloop, 0); 478ea9ebc2cSMarc-André Lureau } 479ea9ebc2cSMarc-André Lureau 480ea9ebc2cSMarc-André Lureau static pa_stream *qpa_simple_new ( 4819a644c4bSKővágó, Zoltán paaudio *g, 482ea9ebc2cSMarc-André Lureau const char *name, 483ea9ebc2cSMarc-André Lureau pa_stream_direction_t dir, 484ea9ebc2cSMarc-André Lureau const char *dev, 485ea9ebc2cSMarc-André Lureau const pa_sample_spec *ss, 486ea9ebc2cSMarc-André Lureau const pa_channel_map *map, 487ea9ebc2cSMarc-André Lureau const pa_buffer_attr *attr, 488ea9ebc2cSMarc-André Lureau int *rerror) 489ea9ebc2cSMarc-André Lureau { 490ea9ebc2cSMarc-André Lureau int r; 491ea9ebc2cSMarc-André Lureau pa_stream *stream; 492ea9ebc2cSMarc-André Lureau 493ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 494ea9ebc2cSMarc-André Lureau 495ea9ebc2cSMarc-André Lureau stream = pa_stream_new (g->context, name, ss, map); 496ea9ebc2cSMarc-André Lureau if (!stream) { 497ea9ebc2cSMarc-André Lureau goto fail; 498ea9ebc2cSMarc-André Lureau } 499ea9ebc2cSMarc-André Lureau 500ea9ebc2cSMarc-André Lureau pa_stream_set_state_callback (stream, stream_state_cb, g); 501ea9ebc2cSMarc-André Lureau pa_stream_set_read_callback (stream, stream_request_cb, g); 502ea9ebc2cSMarc-André Lureau pa_stream_set_write_callback (stream, stream_request_cb, g); 503ea9ebc2cSMarc-André Lureau 504ea9ebc2cSMarc-André Lureau if (dir == PA_STREAM_PLAYBACK) { 505ea9ebc2cSMarc-André Lureau r = pa_stream_connect_playback (stream, dev, attr, 506ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5078f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 508ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5098f473dd1SGerd Hoffmann #endif 510ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); 511ea9ebc2cSMarc-André Lureau } else { 512ea9ebc2cSMarc-André Lureau r = pa_stream_connect_record (stream, dev, attr, 513ea9ebc2cSMarc-André Lureau PA_STREAM_INTERPOLATE_TIMING 5148f473dd1SGerd Hoffmann #ifdef PA_STREAM_ADJUST_LATENCY 515ea9ebc2cSMarc-André Lureau |PA_STREAM_ADJUST_LATENCY 5168f473dd1SGerd Hoffmann #endif 517ea9ebc2cSMarc-André Lureau |PA_STREAM_AUTO_TIMING_UPDATE); 518ea9ebc2cSMarc-André Lureau } 519ea9ebc2cSMarc-André Lureau 520ea9ebc2cSMarc-André Lureau if (r < 0) { 521ea9ebc2cSMarc-André Lureau goto fail; 522ea9ebc2cSMarc-André Lureau } 523ea9ebc2cSMarc-André Lureau 524ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 525ea9ebc2cSMarc-André Lureau 526ea9ebc2cSMarc-André Lureau return stream; 527ea9ebc2cSMarc-André Lureau 528ea9ebc2cSMarc-André Lureau fail: 529ea9ebc2cSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 530ea9ebc2cSMarc-André Lureau 531ea9ebc2cSMarc-André Lureau if (stream) { 532ea9ebc2cSMarc-André Lureau pa_stream_unref (stream); 533ea9ebc2cSMarc-André Lureau } 534ea9ebc2cSMarc-André Lureau 535d6c05bbfSGerd Hoffmann *rerror = pa_context_errno (g->context); 536ea9ebc2cSMarc-André Lureau 537ea9ebc2cSMarc-André Lureau return NULL; 538ea9ebc2cSMarc-André Lureau } 539ea9ebc2cSMarc-André Lureau 5405706db1dSKővágó, Zoltán static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, 5415706db1dSKővágó, Zoltán void *drv_opaque) 542b8e59f18Smalc { 543b8e59f18Smalc int error; 5449a644c4bSKővágó, Zoltán pa_sample_spec ss; 5459a644c4bSKővágó, Zoltán pa_buffer_attr ba; 5461ea879e5Smalc struct audsettings obt_as = *as; 547b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 5489a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 549b8e59f18Smalc 550b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 551b8e59f18Smalc ss.channels = as->nchannels; 552b8e59f18Smalc ss.rate = as->freq; 553b8e59f18Smalc 554e6d16fa4SGerd Hoffmann /* 5550e8ae611SGerd Hoffmann * qemu audio tick runs at 100 Hz (by default), so processing 5560e8ae611SGerd Hoffmann * data chunks worth 10 ms of sound should be a good fit. 557e6d16fa4SGerd Hoffmann */ 5580e8ae611SGerd Hoffmann ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); 5590e8ae611SGerd Hoffmann ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); 560e6d16fa4SGerd Hoffmann ba.maxlength = -1; 561e6d16fa4SGerd Hoffmann ba.prebuf = -1; 562e6d16fa4SGerd Hoffmann 563b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 564b8e59f18Smalc 565ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 5669a644c4bSKővágó, Zoltán g, 567b8e59f18Smalc "qemu", 568b8e59f18Smalc PA_STREAM_PLAYBACK, 5699a644c4bSKővágó, Zoltán g->conf.sink, 570b8e59f18Smalc &ss, 571b8e59f18Smalc NULL, /* channel map */ 572e6d16fa4SGerd Hoffmann &ba, /* buffering attributes */ 573b8e59f18Smalc &error 574b8e59f18Smalc ); 575ea9ebc2cSMarc-André Lureau if (!pa->stream) { 576b8e59f18Smalc qpa_logerr (error, "pa_simple_new for playback failed\n"); 577b8e59f18Smalc goto fail1; 578b8e59f18Smalc } 579b8e59f18Smalc 580b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 5819a644c4bSKővágó, Zoltán hw->samples = g->conf.samples; 582b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 5836315633bSGerd Hoffmann pa->rpos = hw->rpos; 584b8e59f18Smalc if (!pa->pcm_buf) { 585b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 586b8e59f18Smalc hw->samples << hw->info.shift); 587b8e59f18Smalc goto fail2; 588b8e59f18Smalc } 589b8e59f18Smalc 590b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) { 591b8e59f18Smalc goto fail3; 592b8e59f18Smalc } 593b8e59f18Smalc 594b8e59f18Smalc return 0; 595b8e59f18Smalc 596b8e59f18Smalc fail3: 5977267c094SAnthony Liguori g_free (pa->pcm_buf); 598b8e59f18Smalc pa->pcm_buf = NULL; 599b8e59f18Smalc fail2: 600ea9ebc2cSMarc-André Lureau if (pa->stream) { 601ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 602ea9ebc2cSMarc-André Lureau pa->stream = NULL; 603ea9ebc2cSMarc-André Lureau } 604b8e59f18Smalc fail1: 605b8e59f18Smalc return -1; 606b8e59f18Smalc } 607b8e59f18Smalc 6085706db1dSKővágó, Zoltán static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 609b8e59f18Smalc { 610b8e59f18Smalc int error; 6119a644c4bSKővágó, Zoltán pa_sample_spec ss; 6121ea879e5Smalc struct audsettings obt_as = *as; 613b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 6149a644c4bSKővágó, Zoltán paaudio *g = pa->g = drv_opaque; 615b8e59f18Smalc 616b8e59f18Smalc ss.format = audfmt_to_pa (as->fmt, as->endianness); 617b8e59f18Smalc ss.channels = as->nchannels; 618b8e59f18Smalc ss.rate = as->freq; 619b8e59f18Smalc 620b8e59f18Smalc obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); 621b8e59f18Smalc 622ea9ebc2cSMarc-André Lureau pa->stream = qpa_simple_new ( 6239a644c4bSKővágó, Zoltán g, 624b8e59f18Smalc "qemu", 625b8e59f18Smalc PA_STREAM_RECORD, 6269a644c4bSKővágó, Zoltán g->conf.source, 627b8e59f18Smalc &ss, 628b8e59f18Smalc NULL, /* channel map */ 629b8e59f18Smalc NULL, /* buffering attributes */ 630b8e59f18Smalc &error 631b8e59f18Smalc ); 632ea9ebc2cSMarc-André Lureau if (!pa->stream) { 633b8e59f18Smalc qpa_logerr (error, "pa_simple_new for capture failed\n"); 634b8e59f18Smalc goto fail1; 635b8e59f18Smalc } 636b8e59f18Smalc 637b8e59f18Smalc audio_pcm_init_info (&hw->info, &obt_as); 6389a644c4bSKővágó, Zoltán hw->samples = g->conf.samples; 639b8e59f18Smalc pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 6406315633bSGerd Hoffmann pa->wpos = hw->wpos; 641b8e59f18Smalc if (!pa->pcm_buf) { 642b8e59f18Smalc dolog ("Could not allocate buffer (%d bytes)\n", 643b8e59f18Smalc hw->samples << hw->info.shift); 644b8e59f18Smalc goto fail2; 645b8e59f18Smalc } 646b8e59f18Smalc 647b8e59f18Smalc if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { 648b8e59f18Smalc goto fail3; 649b8e59f18Smalc } 650b8e59f18Smalc 651b8e59f18Smalc return 0; 652b8e59f18Smalc 653b8e59f18Smalc fail3: 6547267c094SAnthony Liguori g_free (pa->pcm_buf); 655b8e59f18Smalc pa->pcm_buf = NULL; 656b8e59f18Smalc fail2: 657ea9ebc2cSMarc-André Lureau if (pa->stream) { 658ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 659ea9ebc2cSMarc-André Lureau pa->stream = NULL; 660ea9ebc2cSMarc-André Lureau } 661b8e59f18Smalc fail1: 662b8e59f18Smalc return -1; 663b8e59f18Smalc } 664b8e59f18Smalc 665b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw) 666b8e59f18Smalc { 667b8e59f18Smalc void *ret; 668b8e59f18Smalc PAVoiceOut *pa = (PAVoiceOut *) hw; 669b8e59f18Smalc 670b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 671b8e59f18Smalc pa->done = 1; 672b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 673b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 674b8e59f18Smalc 675ea9ebc2cSMarc-André Lureau if (pa->stream) { 676ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 677ea9ebc2cSMarc-André Lureau pa->stream = NULL; 678b8e59f18Smalc } 679b8e59f18Smalc 680b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 6817267c094SAnthony Liguori g_free (pa->pcm_buf); 682b8e59f18Smalc pa->pcm_buf = NULL; 683b8e59f18Smalc } 684b8e59f18Smalc 685b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw) 686b8e59f18Smalc { 687b8e59f18Smalc void *ret; 688b8e59f18Smalc PAVoiceIn *pa = (PAVoiceIn *) hw; 689b8e59f18Smalc 690b8e59f18Smalc audio_pt_lock (&pa->pt, AUDIO_FUNC); 691b8e59f18Smalc pa->done = 1; 692b8e59f18Smalc audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); 693b8e59f18Smalc audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); 694b8e59f18Smalc 695ea9ebc2cSMarc-André Lureau if (pa->stream) { 696ea9ebc2cSMarc-André Lureau pa_stream_unref (pa->stream); 697ea9ebc2cSMarc-André Lureau pa->stream = NULL; 698b8e59f18Smalc } 699b8e59f18Smalc 700b8e59f18Smalc audio_pt_fini (&pa->pt, AUDIO_FUNC); 7017267c094SAnthony Liguori g_free (pa->pcm_buf); 702b8e59f18Smalc pa->pcm_buf = NULL; 703b8e59f18Smalc } 704b8e59f18Smalc 705b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) 706b8e59f18Smalc { 7076e7a7f3dSMarc-André Lureau PAVoiceOut *pa = (PAVoiceOut *) hw; 7086e7a7f3dSMarc-André Lureau pa_operation *op; 7096e7a7f3dSMarc-André Lureau pa_cvolume v; 7109a644c4bSKővágó, Zoltán paaudio *g = pa->g; 7116e7a7f3dSMarc-André Lureau 7128f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ 7138f473dd1SGerd Hoffmann pa_cvolume_init (&v); /* function is present in 0.9.13+ */ 7148f473dd1SGerd Hoffmann #endif 7156e7a7f3dSMarc-André Lureau 7166e7a7f3dSMarc-André Lureau switch (cmd) { 7176e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7186e7a7f3dSMarc-André Lureau { 7196e7a7f3dSMarc-André Lureau SWVoiceOut *sw; 7206e7a7f3dSMarc-André Lureau va_list ap; 7216e7a7f3dSMarc-André Lureau 7226e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7236e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceOut *); 7246e7a7f3dSMarc-André Lureau va_end (ap); 7256e7a7f3dSMarc-André Lureau 7266e7a7f3dSMarc-André Lureau v.channels = 2; 7276e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7286e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7296e7a7f3dSMarc-André Lureau 7306e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7316e7a7f3dSMarc-André Lureau 7326e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_volume (g->context, 7336e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7346e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7356e7a7f3dSMarc-André Lureau if (!op) 7366e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7376e7a7f3dSMarc-André Lureau "set_sink_input_volume() failed\n"); 7386e7a7f3dSMarc-André Lureau else 7396e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7406e7a7f3dSMarc-André Lureau 7416e7a7f3dSMarc-André Lureau op = pa_context_set_sink_input_mute (g->context, 7426e7a7f3dSMarc-André Lureau pa_stream_get_index (pa->stream), 7436e7a7f3dSMarc-André Lureau sw->vol.mute, NULL, NULL); 7446e7a7f3dSMarc-André Lureau if (!op) { 7456e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 7466e7a7f3dSMarc-André Lureau "set_sink_input_mute() failed\n"); 7476e7a7f3dSMarc-André Lureau } else { 7486e7a7f3dSMarc-André Lureau pa_operation_unref (op); 7496e7a7f3dSMarc-André Lureau } 7506e7a7f3dSMarc-André Lureau 7516e7a7f3dSMarc-André Lureau pa_threaded_mainloop_unlock (g->mainloop); 7526e7a7f3dSMarc-André Lureau } 7536e7a7f3dSMarc-André Lureau } 754b8e59f18Smalc return 0; 755b8e59f18Smalc } 756b8e59f18Smalc 757b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) 758b8e59f18Smalc { 7596e7a7f3dSMarc-André Lureau PAVoiceIn *pa = (PAVoiceIn *) hw; 7606e7a7f3dSMarc-André Lureau pa_operation *op; 7616e7a7f3dSMarc-André Lureau pa_cvolume v; 7629a644c4bSKővágó, Zoltán paaudio *g = pa->g; 7636e7a7f3dSMarc-André Lureau 7648f473dd1SGerd Hoffmann #ifdef PA_CHECK_VERSION 7656e7a7f3dSMarc-André Lureau pa_cvolume_init (&v); 7668f473dd1SGerd Hoffmann #endif 7676e7a7f3dSMarc-André Lureau 7686e7a7f3dSMarc-André Lureau switch (cmd) { 7696e7a7f3dSMarc-André Lureau case VOICE_VOLUME: 7706e7a7f3dSMarc-André Lureau { 7716e7a7f3dSMarc-André Lureau SWVoiceIn *sw; 7726e7a7f3dSMarc-André Lureau va_list ap; 7736e7a7f3dSMarc-André Lureau 7746e7a7f3dSMarc-André Lureau va_start (ap, cmd); 7756e7a7f3dSMarc-André Lureau sw = va_arg (ap, SWVoiceIn *); 7766e7a7f3dSMarc-André Lureau va_end (ap); 7776e7a7f3dSMarc-André Lureau 7786e7a7f3dSMarc-André Lureau v.channels = 2; 7796e7a7f3dSMarc-André Lureau v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; 7806e7a7f3dSMarc-André Lureau v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; 7816e7a7f3dSMarc-André Lureau 7826e7a7f3dSMarc-André Lureau pa_threaded_mainloop_lock (g->mainloop); 7836e7a7f3dSMarc-André Lureau 784e58ff62dSPeter Krempa op = pa_context_set_source_output_volume (g->context, 785e58ff62dSPeter Krempa pa_stream_get_index (pa->stream), 7866e7a7f3dSMarc-André Lureau &v, NULL, NULL); 7876e7a7f3dSMarc-André Lureau if (!op) { 7886e7a7f3dSMarc-André Lureau qpa_logerr (pa_context_errno (g->context), 789e58ff62dSPeter Krempa "set_source_output_volume() failed\n"); 7906e7a7f3dSMarc-André Lureau } else { 7916e7a7f3dSMarc-André Lureau pa_operation_unref(op); 7926e7a7f3dSMarc-André Lureau } 7936e7a7f3dSMarc-André Lureau 794e58ff62dSPeter Krempa op = pa_context_set_source_output_mute (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), 799e58ff62dSPeter Krempa "set_source_output_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; 81949dd6d0dSKővágó, Zoltán g->mainloop = NULL; 82049dd6d0dSKő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"); 87449dd6d0dSKő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