1*c2d3d1c2SDorinda Bassey /* 2*c2d3d1c2SDorinda Bassey * QEMU Pipewire audio driver 3*c2d3d1c2SDorinda Bassey * 4*c2d3d1c2SDorinda Bassey * Copyright (c) 2023 Red Hat Inc. 5*c2d3d1c2SDorinda Bassey * 6*c2d3d1c2SDorinda Bassey * Author: Dorinda Bassey <dbassey@redhat.com> 7*c2d3d1c2SDorinda Bassey * 8*c2d3d1c2SDorinda Bassey * SPDX-License-Identifier: GPL-2.0-or-later 9*c2d3d1c2SDorinda Bassey */ 10*c2d3d1c2SDorinda Bassey 11*c2d3d1c2SDorinda Bassey #include "qemu/osdep.h" 12*c2d3d1c2SDorinda Bassey #include "qemu/module.h" 13*c2d3d1c2SDorinda Bassey #include "audio.h" 14*c2d3d1c2SDorinda Bassey #include <errno.h> 15*c2d3d1c2SDorinda Bassey #include "qemu/error-report.h" 16*c2d3d1c2SDorinda Bassey #include <spa/param/audio/format-utils.h> 17*c2d3d1c2SDorinda Bassey #include <spa/utils/ringbuffer.h> 18*c2d3d1c2SDorinda Bassey #include <spa/utils/result.h> 19*c2d3d1c2SDorinda Bassey #include <spa/param/props.h> 20*c2d3d1c2SDorinda Bassey 21*c2d3d1c2SDorinda Bassey #include <pipewire/pipewire.h> 22*c2d3d1c2SDorinda Bassey #include "trace.h" 23*c2d3d1c2SDorinda Bassey 24*c2d3d1c2SDorinda Bassey #define AUDIO_CAP "pipewire" 25*c2d3d1c2SDorinda Bassey #define RINGBUFFER_SIZE (1u << 22) 26*c2d3d1c2SDorinda Bassey #define RINGBUFFER_MASK (RINGBUFFER_SIZE - 1) 27*c2d3d1c2SDorinda Bassey 28*c2d3d1c2SDorinda Bassey #include "audio_int.h" 29*c2d3d1c2SDorinda Bassey 30*c2d3d1c2SDorinda Bassey typedef struct pwvolume { 31*c2d3d1c2SDorinda Bassey uint32_t channels; 32*c2d3d1c2SDorinda Bassey float values[SPA_AUDIO_MAX_CHANNELS]; 33*c2d3d1c2SDorinda Bassey } pwvolume; 34*c2d3d1c2SDorinda Bassey 35*c2d3d1c2SDorinda Bassey typedef struct pwaudio { 36*c2d3d1c2SDorinda Bassey Audiodev *dev; 37*c2d3d1c2SDorinda Bassey struct pw_thread_loop *thread_loop; 38*c2d3d1c2SDorinda Bassey struct pw_context *context; 39*c2d3d1c2SDorinda Bassey 40*c2d3d1c2SDorinda Bassey struct pw_core *core; 41*c2d3d1c2SDorinda Bassey struct spa_hook core_listener; 42*c2d3d1c2SDorinda Bassey int last_seq, pending_seq, error; 43*c2d3d1c2SDorinda Bassey } pwaudio; 44*c2d3d1c2SDorinda Bassey 45*c2d3d1c2SDorinda Bassey typedef struct PWVoice { 46*c2d3d1c2SDorinda Bassey pwaudio *g; 47*c2d3d1c2SDorinda Bassey struct pw_stream *stream; 48*c2d3d1c2SDorinda Bassey struct spa_hook stream_listener; 49*c2d3d1c2SDorinda Bassey struct spa_audio_info_raw info; 50*c2d3d1c2SDorinda Bassey uint32_t highwater_mark; 51*c2d3d1c2SDorinda Bassey uint32_t frame_size, req; 52*c2d3d1c2SDorinda Bassey struct spa_ringbuffer ring; 53*c2d3d1c2SDorinda Bassey uint8_t buffer[RINGBUFFER_SIZE]; 54*c2d3d1c2SDorinda Bassey 55*c2d3d1c2SDorinda Bassey pwvolume volume; 56*c2d3d1c2SDorinda Bassey bool muted; 57*c2d3d1c2SDorinda Bassey } PWVoice; 58*c2d3d1c2SDorinda Bassey 59*c2d3d1c2SDorinda Bassey typedef struct PWVoiceOut { 60*c2d3d1c2SDorinda Bassey HWVoiceOut hw; 61*c2d3d1c2SDorinda Bassey PWVoice v; 62*c2d3d1c2SDorinda Bassey } PWVoiceOut; 63*c2d3d1c2SDorinda Bassey 64*c2d3d1c2SDorinda Bassey typedef struct PWVoiceIn { 65*c2d3d1c2SDorinda Bassey HWVoiceIn hw; 66*c2d3d1c2SDorinda Bassey PWVoice v; 67*c2d3d1c2SDorinda Bassey } PWVoiceIn; 68*c2d3d1c2SDorinda Bassey 69*c2d3d1c2SDorinda Bassey static void 70*c2d3d1c2SDorinda Bassey stream_destroy(void *data) 71*c2d3d1c2SDorinda Bassey { 72*c2d3d1c2SDorinda Bassey PWVoice *v = (PWVoice *) data; 73*c2d3d1c2SDorinda Bassey spa_hook_remove(&v->stream_listener); 74*c2d3d1c2SDorinda Bassey v->stream = NULL; 75*c2d3d1c2SDorinda Bassey } 76*c2d3d1c2SDorinda Bassey 77*c2d3d1c2SDorinda Bassey /* output data processing function to read stuffs from the buffer */ 78*c2d3d1c2SDorinda Bassey static void 79*c2d3d1c2SDorinda Bassey playback_on_process(void *data) 80*c2d3d1c2SDorinda Bassey { 81*c2d3d1c2SDorinda Bassey PWVoice *v = data; 82*c2d3d1c2SDorinda Bassey void *p; 83*c2d3d1c2SDorinda Bassey struct pw_buffer *b; 84*c2d3d1c2SDorinda Bassey struct spa_buffer *buf; 85*c2d3d1c2SDorinda Bassey uint32_t req, index, n_bytes; 86*c2d3d1c2SDorinda Bassey int32_t avail; 87*c2d3d1c2SDorinda Bassey 88*c2d3d1c2SDorinda Bassey assert(v->stream); 89*c2d3d1c2SDorinda Bassey 90*c2d3d1c2SDorinda Bassey /* obtain a buffer to read from */ 91*c2d3d1c2SDorinda Bassey b = pw_stream_dequeue_buffer(v->stream); 92*c2d3d1c2SDorinda Bassey if (b == NULL) { 93*c2d3d1c2SDorinda Bassey error_report("out of buffers: %s", strerror(errno)); 94*c2d3d1c2SDorinda Bassey return; 95*c2d3d1c2SDorinda Bassey } 96*c2d3d1c2SDorinda Bassey 97*c2d3d1c2SDorinda Bassey buf = b->buffer; 98*c2d3d1c2SDorinda Bassey p = buf->datas[0].data; 99*c2d3d1c2SDorinda Bassey if (p == NULL) { 100*c2d3d1c2SDorinda Bassey return; 101*c2d3d1c2SDorinda Bassey } 102*c2d3d1c2SDorinda Bassey /* calculate the total no of bytes to read data from buffer */ 103*c2d3d1c2SDorinda Bassey req = b->requested * v->frame_size; 104*c2d3d1c2SDorinda Bassey if (req == 0) { 105*c2d3d1c2SDorinda Bassey req = v->req; 106*c2d3d1c2SDorinda Bassey } 107*c2d3d1c2SDorinda Bassey n_bytes = SPA_MIN(req, buf->datas[0].maxsize); 108*c2d3d1c2SDorinda Bassey 109*c2d3d1c2SDorinda Bassey /* get no of available bytes to read data from buffer */ 110*c2d3d1c2SDorinda Bassey avail = spa_ringbuffer_get_read_index(&v->ring, &index); 111*c2d3d1c2SDorinda Bassey 112*c2d3d1c2SDorinda Bassey if (avail <= 0) { 113*c2d3d1c2SDorinda Bassey PWVoiceOut *vo = container_of(data, PWVoiceOut, v); 114*c2d3d1c2SDorinda Bassey audio_pcm_info_clear_buf(&vo->hw.info, p, n_bytes / v->frame_size); 115*c2d3d1c2SDorinda Bassey } else { 116*c2d3d1c2SDorinda Bassey if ((uint32_t) avail < n_bytes) { 117*c2d3d1c2SDorinda Bassey /* 118*c2d3d1c2SDorinda Bassey * PipeWire immediately calls this callback again if we provide 119*c2d3d1c2SDorinda Bassey * less than n_bytes. Then audio_pcm_info_clear_buf() fills the 120*c2d3d1c2SDorinda Bassey * rest of the buffer with silence. 121*c2d3d1c2SDorinda Bassey */ 122*c2d3d1c2SDorinda Bassey n_bytes = avail; 123*c2d3d1c2SDorinda Bassey } 124*c2d3d1c2SDorinda Bassey 125*c2d3d1c2SDorinda Bassey spa_ringbuffer_read_data(&v->ring, 126*c2d3d1c2SDorinda Bassey v->buffer, RINGBUFFER_SIZE, 127*c2d3d1c2SDorinda Bassey index & RINGBUFFER_MASK, p, n_bytes); 128*c2d3d1c2SDorinda Bassey 129*c2d3d1c2SDorinda Bassey index += n_bytes; 130*c2d3d1c2SDorinda Bassey spa_ringbuffer_read_update(&v->ring, index); 131*c2d3d1c2SDorinda Bassey 132*c2d3d1c2SDorinda Bassey } 133*c2d3d1c2SDorinda Bassey buf->datas[0].chunk->offset = 0; 134*c2d3d1c2SDorinda Bassey buf->datas[0].chunk->stride = v->frame_size; 135*c2d3d1c2SDorinda Bassey buf->datas[0].chunk->size = n_bytes; 136*c2d3d1c2SDorinda Bassey 137*c2d3d1c2SDorinda Bassey /* queue the buffer for playback */ 138*c2d3d1c2SDorinda Bassey pw_stream_queue_buffer(v->stream, b); 139*c2d3d1c2SDorinda Bassey } 140*c2d3d1c2SDorinda Bassey 141*c2d3d1c2SDorinda Bassey /* output data processing function to generate stuffs in the buffer */ 142*c2d3d1c2SDorinda Bassey static void 143*c2d3d1c2SDorinda Bassey capture_on_process(void *data) 144*c2d3d1c2SDorinda Bassey { 145*c2d3d1c2SDorinda Bassey PWVoice *v = (PWVoice *) data; 146*c2d3d1c2SDorinda Bassey void *p; 147*c2d3d1c2SDorinda Bassey struct pw_buffer *b; 148*c2d3d1c2SDorinda Bassey struct spa_buffer *buf; 149*c2d3d1c2SDorinda Bassey int32_t filled; 150*c2d3d1c2SDorinda Bassey uint32_t index, offs, n_bytes; 151*c2d3d1c2SDorinda Bassey 152*c2d3d1c2SDorinda Bassey assert(v->stream); 153*c2d3d1c2SDorinda Bassey 154*c2d3d1c2SDorinda Bassey /* obtain a buffer */ 155*c2d3d1c2SDorinda Bassey b = pw_stream_dequeue_buffer(v->stream); 156*c2d3d1c2SDorinda Bassey if (b == NULL) { 157*c2d3d1c2SDorinda Bassey error_report("out of buffers: %s", strerror(errno)); 158*c2d3d1c2SDorinda Bassey return; 159*c2d3d1c2SDorinda Bassey } 160*c2d3d1c2SDorinda Bassey 161*c2d3d1c2SDorinda Bassey /* Write data into buffer */ 162*c2d3d1c2SDorinda Bassey buf = b->buffer; 163*c2d3d1c2SDorinda Bassey p = buf->datas[0].data; 164*c2d3d1c2SDorinda Bassey if (p == NULL) { 165*c2d3d1c2SDorinda Bassey return; 166*c2d3d1c2SDorinda Bassey } 167*c2d3d1c2SDorinda Bassey offs = SPA_MIN(buf->datas[0].chunk->offset, buf->datas[0].maxsize); 168*c2d3d1c2SDorinda Bassey n_bytes = SPA_MIN(buf->datas[0].chunk->size, buf->datas[0].maxsize - offs); 169*c2d3d1c2SDorinda Bassey 170*c2d3d1c2SDorinda Bassey filled = spa_ringbuffer_get_write_index(&v->ring, &index); 171*c2d3d1c2SDorinda Bassey 172*c2d3d1c2SDorinda Bassey 173*c2d3d1c2SDorinda Bassey if (filled < 0) { 174*c2d3d1c2SDorinda Bassey error_report("%p: underrun write:%u filled:%d", p, index, filled); 175*c2d3d1c2SDorinda Bassey } else { 176*c2d3d1c2SDorinda Bassey if ((uint32_t) filled + n_bytes > RINGBUFFER_SIZE) { 177*c2d3d1c2SDorinda Bassey error_report("%p: overrun write:%u filled:%d + size:%u > max:%u", 178*c2d3d1c2SDorinda Bassey p, index, filled, n_bytes, RINGBUFFER_SIZE); 179*c2d3d1c2SDorinda Bassey } 180*c2d3d1c2SDorinda Bassey } 181*c2d3d1c2SDorinda Bassey spa_ringbuffer_write_data(&v->ring, 182*c2d3d1c2SDorinda Bassey v->buffer, RINGBUFFER_SIZE, 183*c2d3d1c2SDorinda Bassey index & RINGBUFFER_MASK, 184*c2d3d1c2SDorinda Bassey SPA_PTROFF(p, offs, void), n_bytes); 185*c2d3d1c2SDorinda Bassey index += n_bytes; 186*c2d3d1c2SDorinda Bassey spa_ringbuffer_write_update(&v->ring, index); 187*c2d3d1c2SDorinda Bassey 188*c2d3d1c2SDorinda Bassey /* queue the buffer for playback */ 189*c2d3d1c2SDorinda Bassey pw_stream_queue_buffer(v->stream, b); 190*c2d3d1c2SDorinda Bassey } 191*c2d3d1c2SDorinda Bassey 192*c2d3d1c2SDorinda Bassey static void 193*c2d3d1c2SDorinda Bassey on_stream_state_changed(void *data, enum pw_stream_state old, 194*c2d3d1c2SDorinda Bassey enum pw_stream_state state, const char *error) 195*c2d3d1c2SDorinda Bassey { 196*c2d3d1c2SDorinda Bassey PWVoice *v = (PWVoice *) data; 197*c2d3d1c2SDorinda Bassey 198*c2d3d1c2SDorinda Bassey trace_pw_state_changed(pw_stream_get_node_id(v->stream), 199*c2d3d1c2SDorinda Bassey pw_stream_state_as_string(state)); 200*c2d3d1c2SDorinda Bassey 201*c2d3d1c2SDorinda Bassey switch (state) { 202*c2d3d1c2SDorinda Bassey case PW_STREAM_STATE_ERROR: 203*c2d3d1c2SDorinda Bassey case PW_STREAM_STATE_UNCONNECTED: 204*c2d3d1c2SDorinda Bassey break; 205*c2d3d1c2SDorinda Bassey case PW_STREAM_STATE_PAUSED: 206*c2d3d1c2SDorinda Bassey case PW_STREAM_STATE_CONNECTING: 207*c2d3d1c2SDorinda Bassey case PW_STREAM_STATE_STREAMING: 208*c2d3d1c2SDorinda Bassey break; 209*c2d3d1c2SDorinda Bassey } 210*c2d3d1c2SDorinda Bassey } 211*c2d3d1c2SDorinda Bassey 212*c2d3d1c2SDorinda Bassey static const struct pw_stream_events capture_stream_events = { 213*c2d3d1c2SDorinda Bassey PW_VERSION_STREAM_EVENTS, 214*c2d3d1c2SDorinda Bassey .destroy = stream_destroy, 215*c2d3d1c2SDorinda Bassey .state_changed = on_stream_state_changed, 216*c2d3d1c2SDorinda Bassey .process = capture_on_process 217*c2d3d1c2SDorinda Bassey }; 218*c2d3d1c2SDorinda Bassey 219*c2d3d1c2SDorinda Bassey static const struct pw_stream_events playback_stream_events = { 220*c2d3d1c2SDorinda Bassey PW_VERSION_STREAM_EVENTS, 221*c2d3d1c2SDorinda Bassey .destroy = stream_destroy, 222*c2d3d1c2SDorinda Bassey .state_changed = on_stream_state_changed, 223*c2d3d1c2SDorinda Bassey .process = playback_on_process 224*c2d3d1c2SDorinda Bassey }; 225*c2d3d1c2SDorinda Bassey 226*c2d3d1c2SDorinda Bassey static size_t 227*c2d3d1c2SDorinda Bassey qpw_read(HWVoiceIn *hw, void *data, size_t len) 228*c2d3d1c2SDorinda Bassey { 229*c2d3d1c2SDorinda Bassey PWVoiceIn *pw = (PWVoiceIn *) hw; 230*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 231*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 232*c2d3d1c2SDorinda Bassey const char *error = NULL; 233*c2d3d1c2SDorinda Bassey size_t l; 234*c2d3d1c2SDorinda Bassey int32_t avail; 235*c2d3d1c2SDorinda Bassey uint32_t index; 236*c2d3d1c2SDorinda Bassey 237*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 238*c2d3d1c2SDorinda Bassey if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { 239*c2d3d1c2SDorinda Bassey /* wait for stream to become ready */ 240*c2d3d1c2SDorinda Bassey l = 0; 241*c2d3d1c2SDorinda Bassey goto done_unlock; 242*c2d3d1c2SDorinda Bassey } 243*c2d3d1c2SDorinda Bassey /* get no of available bytes to read data from buffer */ 244*c2d3d1c2SDorinda Bassey avail = spa_ringbuffer_get_read_index(&v->ring, &index); 245*c2d3d1c2SDorinda Bassey 246*c2d3d1c2SDorinda Bassey trace_pw_read(avail, index, len); 247*c2d3d1c2SDorinda Bassey 248*c2d3d1c2SDorinda Bassey if (avail < (int32_t) len) { 249*c2d3d1c2SDorinda Bassey len = avail; 250*c2d3d1c2SDorinda Bassey } 251*c2d3d1c2SDorinda Bassey 252*c2d3d1c2SDorinda Bassey spa_ringbuffer_read_data(&v->ring, 253*c2d3d1c2SDorinda Bassey v->buffer, RINGBUFFER_SIZE, 254*c2d3d1c2SDorinda Bassey index & RINGBUFFER_MASK, data, len); 255*c2d3d1c2SDorinda Bassey index += len; 256*c2d3d1c2SDorinda Bassey spa_ringbuffer_read_update(&v->ring, index); 257*c2d3d1c2SDorinda Bassey l = len; 258*c2d3d1c2SDorinda Bassey 259*c2d3d1c2SDorinda Bassey done_unlock: 260*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 261*c2d3d1c2SDorinda Bassey return l; 262*c2d3d1c2SDorinda Bassey } 263*c2d3d1c2SDorinda Bassey 264*c2d3d1c2SDorinda Bassey static size_t qpw_buffer_get_free(HWVoiceOut *hw) 265*c2d3d1c2SDorinda Bassey { 266*c2d3d1c2SDorinda Bassey PWVoiceOut *pw = (PWVoiceOut *)hw; 267*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 268*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 269*c2d3d1c2SDorinda Bassey const char *error = NULL; 270*c2d3d1c2SDorinda Bassey int32_t filled, avail; 271*c2d3d1c2SDorinda Bassey uint32_t index; 272*c2d3d1c2SDorinda Bassey 273*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 274*c2d3d1c2SDorinda Bassey if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { 275*c2d3d1c2SDorinda Bassey /* wait for stream to become ready */ 276*c2d3d1c2SDorinda Bassey avail = 0; 277*c2d3d1c2SDorinda Bassey goto done_unlock; 278*c2d3d1c2SDorinda Bassey } 279*c2d3d1c2SDorinda Bassey 280*c2d3d1c2SDorinda Bassey filled = spa_ringbuffer_get_write_index(&v->ring, &index); 281*c2d3d1c2SDorinda Bassey avail = v->highwater_mark - filled; 282*c2d3d1c2SDorinda Bassey 283*c2d3d1c2SDorinda Bassey done_unlock: 284*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 285*c2d3d1c2SDorinda Bassey return avail; 286*c2d3d1c2SDorinda Bassey } 287*c2d3d1c2SDorinda Bassey 288*c2d3d1c2SDorinda Bassey static size_t 289*c2d3d1c2SDorinda Bassey qpw_write(HWVoiceOut *hw, void *data, size_t len) 290*c2d3d1c2SDorinda Bassey { 291*c2d3d1c2SDorinda Bassey PWVoiceOut *pw = (PWVoiceOut *) hw; 292*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 293*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 294*c2d3d1c2SDorinda Bassey const char *error = NULL; 295*c2d3d1c2SDorinda Bassey int32_t filled, avail; 296*c2d3d1c2SDorinda Bassey uint32_t index; 297*c2d3d1c2SDorinda Bassey 298*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 299*c2d3d1c2SDorinda Bassey if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { 300*c2d3d1c2SDorinda Bassey /* wait for stream to become ready */ 301*c2d3d1c2SDorinda Bassey len = 0; 302*c2d3d1c2SDorinda Bassey goto done_unlock; 303*c2d3d1c2SDorinda Bassey } 304*c2d3d1c2SDorinda Bassey filled = spa_ringbuffer_get_write_index(&v->ring, &index); 305*c2d3d1c2SDorinda Bassey avail = v->highwater_mark - filled; 306*c2d3d1c2SDorinda Bassey 307*c2d3d1c2SDorinda Bassey trace_pw_write(filled, avail, index, len); 308*c2d3d1c2SDorinda Bassey 309*c2d3d1c2SDorinda Bassey if (len > avail) { 310*c2d3d1c2SDorinda Bassey len = avail; 311*c2d3d1c2SDorinda Bassey } 312*c2d3d1c2SDorinda Bassey 313*c2d3d1c2SDorinda Bassey if (filled < 0) { 314*c2d3d1c2SDorinda Bassey error_report("%p: underrun write:%u filled:%d", pw, index, filled); 315*c2d3d1c2SDorinda Bassey } else { 316*c2d3d1c2SDorinda Bassey if ((uint32_t) filled + len > RINGBUFFER_SIZE) { 317*c2d3d1c2SDorinda Bassey error_report("%p: overrun write:%u filled:%d + size:%zu > max:%u", 318*c2d3d1c2SDorinda Bassey pw, index, filled, len, RINGBUFFER_SIZE); 319*c2d3d1c2SDorinda Bassey } 320*c2d3d1c2SDorinda Bassey } 321*c2d3d1c2SDorinda Bassey 322*c2d3d1c2SDorinda Bassey spa_ringbuffer_write_data(&v->ring, 323*c2d3d1c2SDorinda Bassey v->buffer, RINGBUFFER_SIZE, 324*c2d3d1c2SDorinda Bassey index & RINGBUFFER_MASK, data, len); 325*c2d3d1c2SDorinda Bassey index += len; 326*c2d3d1c2SDorinda Bassey spa_ringbuffer_write_update(&v->ring, index); 327*c2d3d1c2SDorinda Bassey 328*c2d3d1c2SDorinda Bassey done_unlock: 329*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 330*c2d3d1c2SDorinda Bassey return len; 331*c2d3d1c2SDorinda Bassey } 332*c2d3d1c2SDorinda Bassey 333*c2d3d1c2SDorinda Bassey static int 334*c2d3d1c2SDorinda Bassey audfmt_to_pw(AudioFormat fmt, int endianness) 335*c2d3d1c2SDorinda Bassey { 336*c2d3d1c2SDorinda Bassey int format; 337*c2d3d1c2SDorinda Bassey 338*c2d3d1c2SDorinda Bassey switch (fmt) { 339*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_S8: 340*c2d3d1c2SDorinda Bassey format = SPA_AUDIO_FORMAT_S8; 341*c2d3d1c2SDorinda Bassey break; 342*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_U8: 343*c2d3d1c2SDorinda Bassey format = SPA_AUDIO_FORMAT_U8; 344*c2d3d1c2SDorinda Bassey break; 345*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_S16: 346*c2d3d1c2SDorinda Bassey format = endianness ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE; 347*c2d3d1c2SDorinda Bassey break; 348*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_U16: 349*c2d3d1c2SDorinda Bassey format = endianness ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE; 350*c2d3d1c2SDorinda Bassey break; 351*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_S32: 352*c2d3d1c2SDorinda Bassey format = endianness ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE; 353*c2d3d1c2SDorinda Bassey break; 354*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_U32: 355*c2d3d1c2SDorinda Bassey format = endianness ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE; 356*c2d3d1c2SDorinda Bassey break; 357*c2d3d1c2SDorinda Bassey case AUDIO_FORMAT_F32: 358*c2d3d1c2SDorinda Bassey format = endianness ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE; 359*c2d3d1c2SDorinda Bassey break; 360*c2d3d1c2SDorinda Bassey default: 361*c2d3d1c2SDorinda Bassey dolog("Internal logic error: Bad audio format %d\n", fmt); 362*c2d3d1c2SDorinda Bassey format = SPA_AUDIO_FORMAT_U8; 363*c2d3d1c2SDorinda Bassey break; 364*c2d3d1c2SDorinda Bassey } 365*c2d3d1c2SDorinda Bassey return format; 366*c2d3d1c2SDorinda Bassey } 367*c2d3d1c2SDorinda Bassey 368*c2d3d1c2SDorinda Bassey static AudioFormat 369*c2d3d1c2SDorinda Bassey pw_to_audfmt(enum spa_audio_format fmt, int *endianness, 370*c2d3d1c2SDorinda Bassey uint32_t *sample_size) 371*c2d3d1c2SDorinda Bassey { 372*c2d3d1c2SDorinda Bassey switch (fmt) { 373*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_S8: 374*c2d3d1c2SDorinda Bassey *sample_size = 1; 375*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_S8; 376*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_U8: 377*c2d3d1c2SDorinda Bassey *sample_size = 1; 378*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U8; 379*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_S16_BE: 380*c2d3d1c2SDorinda Bassey *sample_size = 2; 381*c2d3d1c2SDorinda Bassey *endianness = 1; 382*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_S16; 383*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_S16_LE: 384*c2d3d1c2SDorinda Bassey *sample_size = 2; 385*c2d3d1c2SDorinda Bassey *endianness = 0; 386*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_S16; 387*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_U16_BE: 388*c2d3d1c2SDorinda Bassey *sample_size = 2; 389*c2d3d1c2SDorinda Bassey *endianness = 1; 390*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U16; 391*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_U16_LE: 392*c2d3d1c2SDorinda Bassey *sample_size = 2; 393*c2d3d1c2SDorinda Bassey *endianness = 0; 394*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U16; 395*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_S32_BE: 396*c2d3d1c2SDorinda Bassey *sample_size = 4; 397*c2d3d1c2SDorinda Bassey *endianness = 1; 398*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_S32; 399*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_S32_LE: 400*c2d3d1c2SDorinda Bassey *sample_size = 4; 401*c2d3d1c2SDorinda Bassey *endianness = 0; 402*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_S32; 403*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_U32_BE: 404*c2d3d1c2SDorinda Bassey *sample_size = 4; 405*c2d3d1c2SDorinda Bassey *endianness = 1; 406*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U32; 407*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_U32_LE: 408*c2d3d1c2SDorinda Bassey *sample_size = 4; 409*c2d3d1c2SDorinda Bassey *endianness = 0; 410*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U32; 411*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_F32_BE: 412*c2d3d1c2SDorinda Bassey *sample_size = 4; 413*c2d3d1c2SDorinda Bassey *endianness = 1; 414*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_F32; 415*c2d3d1c2SDorinda Bassey case SPA_AUDIO_FORMAT_F32_LE: 416*c2d3d1c2SDorinda Bassey *sample_size = 4; 417*c2d3d1c2SDorinda Bassey *endianness = 0; 418*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_F32; 419*c2d3d1c2SDorinda Bassey default: 420*c2d3d1c2SDorinda Bassey *sample_size = 1; 421*c2d3d1c2SDorinda Bassey dolog("Internal logic error: Bad spa_audio_format %d\n", fmt); 422*c2d3d1c2SDorinda Bassey return AUDIO_FORMAT_U8; 423*c2d3d1c2SDorinda Bassey } 424*c2d3d1c2SDorinda Bassey } 425*c2d3d1c2SDorinda Bassey 426*c2d3d1c2SDorinda Bassey static int 427*c2d3d1c2SDorinda Bassey create_stream(pwaudio *c, PWVoice *v, const char *stream_name, 428*c2d3d1c2SDorinda Bassey const char *name, enum spa_direction dir) 429*c2d3d1c2SDorinda Bassey { 430*c2d3d1c2SDorinda Bassey int res; 431*c2d3d1c2SDorinda Bassey uint32_t n_params; 432*c2d3d1c2SDorinda Bassey const struct spa_pod *params[2]; 433*c2d3d1c2SDorinda Bassey uint8_t buffer[1024]; 434*c2d3d1c2SDorinda Bassey struct spa_pod_builder b; 435*c2d3d1c2SDorinda Bassey uint64_t buf_samples; 436*c2d3d1c2SDorinda Bassey struct pw_properties *props; 437*c2d3d1c2SDorinda Bassey 438*c2d3d1c2SDorinda Bassey props = pw_properties_new(NULL, NULL); 439*c2d3d1c2SDorinda Bassey 440*c2d3d1c2SDorinda Bassey /* 75% of the timer period for faster updates */ 441*c2d3d1c2SDorinda Bassey buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate 442*c2d3d1c2SDorinda Bassey * 3 / 4 / 1000000; 443*c2d3d1c2SDorinda Bassey pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u", 444*c2d3d1c2SDorinda Bassey buf_samples, v->info.rate); 445*c2d3d1c2SDorinda Bassey 446*c2d3d1c2SDorinda Bassey trace_pw_period(buf_samples, v->info.rate); 447*c2d3d1c2SDorinda Bassey if (name) { 448*c2d3d1c2SDorinda Bassey pw_properties_set(props, PW_KEY_TARGET_OBJECT, name); 449*c2d3d1c2SDorinda Bassey } 450*c2d3d1c2SDorinda Bassey v->stream = pw_stream_new(c->core, stream_name, props); 451*c2d3d1c2SDorinda Bassey 452*c2d3d1c2SDorinda Bassey if (v->stream == NULL) { 453*c2d3d1c2SDorinda Bassey return -1; 454*c2d3d1c2SDorinda Bassey } 455*c2d3d1c2SDorinda Bassey 456*c2d3d1c2SDorinda Bassey if (dir == SPA_DIRECTION_INPUT) { 457*c2d3d1c2SDorinda Bassey pw_stream_add_listener(v->stream, 458*c2d3d1c2SDorinda Bassey &v->stream_listener, &capture_stream_events, v); 459*c2d3d1c2SDorinda Bassey } else { 460*c2d3d1c2SDorinda Bassey pw_stream_add_listener(v->stream, 461*c2d3d1c2SDorinda Bassey &v->stream_listener, &playback_stream_events, v); 462*c2d3d1c2SDorinda Bassey } 463*c2d3d1c2SDorinda Bassey 464*c2d3d1c2SDorinda Bassey n_params = 0; 465*c2d3d1c2SDorinda Bassey spa_pod_builder_init(&b, buffer, sizeof(buffer)); 466*c2d3d1c2SDorinda Bassey params[n_params++] = spa_format_audio_raw_build(&b, 467*c2d3d1c2SDorinda Bassey SPA_PARAM_EnumFormat, 468*c2d3d1c2SDorinda Bassey &v->info); 469*c2d3d1c2SDorinda Bassey 470*c2d3d1c2SDorinda Bassey /* connect the stream to a sink or source */ 471*c2d3d1c2SDorinda Bassey res = pw_stream_connect(v->stream, 472*c2d3d1c2SDorinda Bassey dir == 473*c2d3d1c2SDorinda Bassey SPA_DIRECTION_INPUT ? PW_DIRECTION_INPUT : 474*c2d3d1c2SDorinda Bassey PW_DIRECTION_OUTPUT, PW_ID_ANY, 475*c2d3d1c2SDorinda Bassey PW_STREAM_FLAG_AUTOCONNECT | 476*c2d3d1c2SDorinda Bassey PW_STREAM_FLAG_INACTIVE | 477*c2d3d1c2SDorinda Bassey PW_STREAM_FLAG_MAP_BUFFERS | 478*c2d3d1c2SDorinda Bassey PW_STREAM_FLAG_RT_PROCESS, params, n_params); 479*c2d3d1c2SDorinda Bassey if (res < 0) { 480*c2d3d1c2SDorinda Bassey pw_stream_destroy(v->stream); 481*c2d3d1c2SDorinda Bassey return -1; 482*c2d3d1c2SDorinda Bassey } 483*c2d3d1c2SDorinda Bassey 484*c2d3d1c2SDorinda Bassey return 0; 485*c2d3d1c2SDorinda Bassey } 486*c2d3d1c2SDorinda Bassey 487*c2d3d1c2SDorinda Bassey static int 488*c2d3d1c2SDorinda Bassey qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, 489*c2d3d1c2SDorinda Bassey const char *name, enum spa_direction dir) 490*c2d3d1c2SDorinda Bassey { 491*c2d3d1c2SDorinda Bassey int r; 492*c2d3d1c2SDorinda Bassey 493*c2d3d1c2SDorinda Bassey switch (v->info.channels) { 494*c2d3d1c2SDorinda Bassey case 8: 495*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 496*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 497*c2d3d1c2SDorinda Bassey v->info.position[2] = SPA_AUDIO_CHANNEL_FC; 498*c2d3d1c2SDorinda Bassey v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; 499*c2d3d1c2SDorinda Bassey v->info.position[4] = SPA_AUDIO_CHANNEL_RL; 500*c2d3d1c2SDorinda Bassey v->info.position[5] = SPA_AUDIO_CHANNEL_RR; 501*c2d3d1c2SDorinda Bassey v->info.position[6] = SPA_AUDIO_CHANNEL_SL; 502*c2d3d1c2SDorinda Bassey v->info.position[7] = SPA_AUDIO_CHANNEL_SR; 503*c2d3d1c2SDorinda Bassey break; 504*c2d3d1c2SDorinda Bassey case 6: 505*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 506*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 507*c2d3d1c2SDorinda Bassey v->info.position[2] = SPA_AUDIO_CHANNEL_FC; 508*c2d3d1c2SDorinda Bassey v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; 509*c2d3d1c2SDorinda Bassey v->info.position[4] = SPA_AUDIO_CHANNEL_RL; 510*c2d3d1c2SDorinda Bassey v->info.position[5] = SPA_AUDIO_CHANNEL_RR; 511*c2d3d1c2SDorinda Bassey break; 512*c2d3d1c2SDorinda Bassey case 5: 513*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 514*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 515*c2d3d1c2SDorinda Bassey v->info.position[2] = SPA_AUDIO_CHANNEL_FC; 516*c2d3d1c2SDorinda Bassey v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; 517*c2d3d1c2SDorinda Bassey v->info.position[4] = SPA_AUDIO_CHANNEL_RC; 518*c2d3d1c2SDorinda Bassey break; 519*c2d3d1c2SDorinda Bassey case 4: 520*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 521*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 522*c2d3d1c2SDorinda Bassey v->info.position[2] = SPA_AUDIO_CHANNEL_FC; 523*c2d3d1c2SDorinda Bassey v->info.position[3] = SPA_AUDIO_CHANNEL_RC; 524*c2d3d1c2SDorinda Bassey break; 525*c2d3d1c2SDorinda Bassey case 3: 526*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 527*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 528*c2d3d1c2SDorinda Bassey v->info.position[2] = SPA_AUDIO_CHANNEL_LFE; 529*c2d3d1c2SDorinda Bassey break; 530*c2d3d1c2SDorinda Bassey case 2: 531*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_FL; 532*c2d3d1c2SDorinda Bassey v->info.position[1] = SPA_AUDIO_CHANNEL_FR; 533*c2d3d1c2SDorinda Bassey break; 534*c2d3d1c2SDorinda Bassey case 1: 535*c2d3d1c2SDorinda Bassey v->info.position[0] = SPA_AUDIO_CHANNEL_MONO; 536*c2d3d1c2SDorinda Bassey break; 537*c2d3d1c2SDorinda Bassey default: 538*c2d3d1c2SDorinda Bassey for (size_t i = 0; i < v->info.channels; i++) { 539*c2d3d1c2SDorinda Bassey v->info.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; 540*c2d3d1c2SDorinda Bassey } 541*c2d3d1c2SDorinda Bassey break; 542*c2d3d1c2SDorinda Bassey } 543*c2d3d1c2SDorinda Bassey 544*c2d3d1c2SDorinda Bassey /* create a new unconnected pwstream */ 545*c2d3d1c2SDorinda Bassey r = create_stream(c, v, stream_name, name, dir); 546*c2d3d1c2SDorinda Bassey if (r < 0) { 547*c2d3d1c2SDorinda Bassey AUD_log(AUDIO_CAP, "Failed to create stream."); 548*c2d3d1c2SDorinda Bassey return -1; 549*c2d3d1c2SDorinda Bassey } 550*c2d3d1c2SDorinda Bassey 551*c2d3d1c2SDorinda Bassey return r; 552*c2d3d1c2SDorinda Bassey } 553*c2d3d1c2SDorinda Bassey 554*c2d3d1c2SDorinda Bassey static int 555*c2d3d1c2SDorinda Bassey qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) 556*c2d3d1c2SDorinda Bassey { 557*c2d3d1c2SDorinda Bassey PWVoiceOut *pw = (PWVoiceOut *) hw; 558*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 559*c2d3d1c2SDorinda Bassey struct audsettings obt_as = *as; 560*c2d3d1c2SDorinda Bassey pwaudio *c = v->g = drv_opaque; 561*c2d3d1c2SDorinda Bassey AudiodevPipewireOptions *popts = &c->dev->u.pipewire; 562*c2d3d1c2SDorinda Bassey AudiodevPipewirePerDirectionOptions *ppdo = popts->out; 563*c2d3d1c2SDorinda Bassey int r; 564*c2d3d1c2SDorinda Bassey 565*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 566*c2d3d1c2SDorinda Bassey 567*c2d3d1c2SDorinda Bassey v->info.format = audfmt_to_pw(as->fmt, as->endianness); 568*c2d3d1c2SDorinda Bassey v->info.channels = as->nchannels; 569*c2d3d1c2SDorinda Bassey v->info.rate = as->freq; 570*c2d3d1c2SDorinda Bassey 571*c2d3d1c2SDorinda Bassey obt_as.fmt = 572*c2d3d1c2SDorinda Bassey pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size); 573*c2d3d1c2SDorinda Bassey v->frame_size *= as->nchannels; 574*c2d3d1c2SDorinda Bassey 575*c2d3d1c2SDorinda Bassey v->req = (uint64_t)c->dev->timer_period * v->info.rate 576*c2d3d1c2SDorinda Bassey * 1 / 2 / 1000000 * v->frame_size; 577*c2d3d1c2SDorinda Bassey 578*c2d3d1c2SDorinda Bassey /* call the function that creates a new stream for playback */ 579*c2d3d1c2SDorinda Bassey r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, 580*c2d3d1c2SDorinda Bassey ppdo->name, SPA_DIRECTION_OUTPUT); 581*c2d3d1c2SDorinda Bassey if (r < 0) { 582*c2d3d1c2SDorinda Bassey error_report("qpw_stream_new for playback failed"); 583*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 584*c2d3d1c2SDorinda Bassey return -1; 585*c2d3d1c2SDorinda Bassey } 586*c2d3d1c2SDorinda Bassey 587*c2d3d1c2SDorinda Bassey /* report the audio format we support */ 588*c2d3d1c2SDorinda Bassey audio_pcm_init_info(&hw->info, &obt_as); 589*c2d3d1c2SDorinda Bassey 590*c2d3d1c2SDorinda Bassey /* report the buffer size to qemu */ 591*c2d3d1c2SDorinda Bassey hw->samples = audio_buffer_frames( 592*c2d3d1c2SDorinda Bassey qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440); 593*c2d3d1c2SDorinda Bassey v->highwater_mark = MIN(RINGBUFFER_SIZE, 594*c2d3d1c2SDorinda Bassey (ppdo->has_latency ? ppdo->latency : 46440) 595*c2d3d1c2SDorinda Bassey * (uint64_t)v->info.rate / 1000000 * v->frame_size); 596*c2d3d1c2SDorinda Bassey 597*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 598*c2d3d1c2SDorinda Bassey return 0; 599*c2d3d1c2SDorinda Bassey } 600*c2d3d1c2SDorinda Bassey 601*c2d3d1c2SDorinda Bassey static int 602*c2d3d1c2SDorinda Bassey qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 603*c2d3d1c2SDorinda Bassey { 604*c2d3d1c2SDorinda Bassey PWVoiceIn *pw = (PWVoiceIn *) hw; 605*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 606*c2d3d1c2SDorinda Bassey struct audsettings obt_as = *as; 607*c2d3d1c2SDorinda Bassey pwaudio *c = v->g = drv_opaque; 608*c2d3d1c2SDorinda Bassey AudiodevPipewireOptions *popts = &c->dev->u.pipewire; 609*c2d3d1c2SDorinda Bassey AudiodevPipewirePerDirectionOptions *ppdo = popts->in; 610*c2d3d1c2SDorinda Bassey int r; 611*c2d3d1c2SDorinda Bassey 612*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 613*c2d3d1c2SDorinda Bassey 614*c2d3d1c2SDorinda Bassey v->info.format = audfmt_to_pw(as->fmt, as->endianness); 615*c2d3d1c2SDorinda Bassey v->info.channels = as->nchannels; 616*c2d3d1c2SDorinda Bassey v->info.rate = as->freq; 617*c2d3d1c2SDorinda Bassey 618*c2d3d1c2SDorinda Bassey obt_as.fmt = 619*c2d3d1c2SDorinda Bassey pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size); 620*c2d3d1c2SDorinda Bassey v->frame_size *= as->nchannels; 621*c2d3d1c2SDorinda Bassey 622*c2d3d1c2SDorinda Bassey /* call the function that creates a new stream for recording */ 623*c2d3d1c2SDorinda Bassey r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, 624*c2d3d1c2SDorinda Bassey ppdo->name, SPA_DIRECTION_INPUT); 625*c2d3d1c2SDorinda Bassey if (r < 0) { 626*c2d3d1c2SDorinda Bassey error_report("qpw_stream_new for recording failed"); 627*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 628*c2d3d1c2SDorinda Bassey return -1; 629*c2d3d1c2SDorinda Bassey } 630*c2d3d1c2SDorinda Bassey 631*c2d3d1c2SDorinda Bassey /* report the audio format we support */ 632*c2d3d1c2SDorinda Bassey audio_pcm_init_info(&hw->info, &obt_as); 633*c2d3d1c2SDorinda Bassey 634*c2d3d1c2SDorinda Bassey /* report the buffer size to qemu */ 635*c2d3d1c2SDorinda Bassey hw->samples = audio_buffer_frames( 636*c2d3d1c2SDorinda Bassey qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440); 637*c2d3d1c2SDorinda Bassey 638*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 639*c2d3d1c2SDorinda Bassey return 0; 640*c2d3d1c2SDorinda Bassey } 641*c2d3d1c2SDorinda Bassey 642*c2d3d1c2SDorinda Bassey static void 643*c2d3d1c2SDorinda Bassey qpw_fini_out(HWVoiceOut *hw) 644*c2d3d1c2SDorinda Bassey { 645*c2d3d1c2SDorinda Bassey PWVoiceOut *pw = (PWVoiceOut *) hw; 646*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 647*c2d3d1c2SDorinda Bassey 648*c2d3d1c2SDorinda Bassey if (v->stream) { 649*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 650*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 651*c2d3d1c2SDorinda Bassey pw_stream_destroy(v->stream); 652*c2d3d1c2SDorinda Bassey v->stream = NULL; 653*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 654*c2d3d1c2SDorinda Bassey } 655*c2d3d1c2SDorinda Bassey } 656*c2d3d1c2SDorinda Bassey 657*c2d3d1c2SDorinda Bassey static void 658*c2d3d1c2SDorinda Bassey qpw_fini_in(HWVoiceIn *hw) 659*c2d3d1c2SDorinda Bassey { 660*c2d3d1c2SDorinda Bassey PWVoiceIn *pw = (PWVoiceIn *) hw; 661*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 662*c2d3d1c2SDorinda Bassey 663*c2d3d1c2SDorinda Bassey if (v->stream) { 664*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 665*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 666*c2d3d1c2SDorinda Bassey pw_stream_destroy(v->stream); 667*c2d3d1c2SDorinda Bassey v->stream = NULL; 668*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 669*c2d3d1c2SDorinda Bassey } 670*c2d3d1c2SDorinda Bassey } 671*c2d3d1c2SDorinda Bassey 672*c2d3d1c2SDorinda Bassey static void 673*c2d3d1c2SDorinda Bassey qpw_enable_out(HWVoiceOut *hw, bool enable) 674*c2d3d1c2SDorinda Bassey { 675*c2d3d1c2SDorinda Bassey PWVoiceOut *po = (PWVoiceOut *) hw; 676*c2d3d1c2SDorinda Bassey PWVoice *v = &po->v; 677*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 678*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 679*c2d3d1c2SDorinda Bassey pw_stream_set_active(v->stream, enable); 680*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 681*c2d3d1c2SDorinda Bassey } 682*c2d3d1c2SDorinda Bassey 683*c2d3d1c2SDorinda Bassey static void 684*c2d3d1c2SDorinda Bassey qpw_enable_in(HWVoiceIn *hw, bool enable) 685*c2d3d1c2SDorinda Bassey { 686*c2d3d1c2SDorinda Bassey PWVoiceIn *pi = (PWVoiceIn *) hw; 687*c2d3d1c2SDorinda Bassey PWVoice *v = &pi->v; 688*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 689*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 690*c2d3d1c2SDorinda Bassey pw_stream_set_active(v->stream, enable); 691*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 692*c2d3d1c2SDorinda Bassey } 693*c2d3d1c2SDorinda Bassey 694*c2d3d1c2SDorinda Bassey static void 695*c2d3d1c2SDorinda Bassey qpw_volume_out(HWVoiceOut *hw, Volume *vol) 696*c2d3d1c2SDorinda Bassey { 697*c2d3d1c2SDorinda Bassey PWVoiceOut *pw = (PWVoiceOut *) hw; 698*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 699*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 700*c2d3d1c2SDorinda Bassey int i, ret; 701*c2d3d1c2SDorinda Bassey 702*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 703*c2d3d1c2SDorinda Bassey v->volume.channels = vol->channels; 704*c2d3d1c2SDorinda Bassey 705*c2d3d1c2SDorinda Bassey for (i = 0; i < vol->channels; ++i) { 706*c2d3d1c2SDorinda Bassey v->volume.values[i] = (float)vol->vol[i] / 255; 707*c2d3d1c2SDorinda Bassey } 708*c2d3d1c2SDorinda Bassey 709*c2d3d1c2SDorinda Bassey ret = pw_stream_set_control(v->stream, 710*c2d3d1c2SDorinda Bassey SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); 711*c2d3d1c2SDorinda Bassey trace_pw_vol(ret == 0 ? "success" : "failed"); 712*c2d3d1c2SDorinda Bassey 713*c2d3d1c2SDorinda Bassey v->muted = vol->mute; 714*c2d3d1c2SDorinda Bassey float val = v->muted ? 1.f : 0.f; 715*c2d3d1c2SDorinda Bassey ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); 716*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 717*c2d3d1c2SDorinda Bassey } 718*c2d3d1c2SDorinda Bassey 719*c2d3d1c2SDorinda Bassey static void 720*c2d3d1c2SDorinda Bassey qpw_volume_in(HWVoiceIn *hw, Volume *vol) 721*c2d3d1c2SDorinda Bassey { 722*c2d3d1c2SDorinda Bassey PWVoiceIn *pw = (PWVoiceIn *) hw; 723*c2d3d1c2SDorinda Bassey PWVoice *v = &pw->v; 724*c2d3d1c2SDorinda Bassey pwaudio *c = v->g; 725*c2d3d1c2SDorinda Bassey int i, ret; 726*c2d3d1c2SDorinda Bassey 727*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(c->thread_loop); 728*c2d3d1c2SDorinda Bassey v->volume.channels = vol->channels; 729*c2d3d1c2SDorinda Bassey 730*c2d3d1c2SDorinda Bassey for (i = 0; i < vol->channels; ++i) { 731*c2d3d1c2SDorinda Bassey v->volume.values[i] = (float)vol->vol[i] / 255; 732*c2d3d1c2SDorinda Bassey } 733*c2d3d1c2SDorinda Bassey 734*c2d3d1c2SDorinda Bassey ret = pw_stream_set_control(v->stream, 735*c2d3d1c2SDorinda Bassey SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); 736*c2d3d1c2SDorinda Bassey trace_pw_vol(ret == 0 ? "success" : "failed"); 737*c2d3d1c2SDorinda Bassey 738*c2d3d1c2SDorinda Bassey v->muted = vol->mute; 739*c2d3d1c2SDorinda Bassey float val = v->muted ? 1.f : 0.f; 740*c2d3d1c2SDorinda Bassey ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); 741*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(c->thread_loop); 742*c2d3d1c2SDorinda Bassey } 743*c2d3d1c2SDorinda Bassey 744*c2d3d1c2SDorinda Bassey static int wait_resync(pwaudio *pw) 745*c2d3d1c2SDorinda Bassey { 746*c2d3d1c2SDorinda Bassey int res; 747*c2d3d1c2SDorinda Bassey pw->pending_seq = pw_core_sync(pw->core, PW_ID_CORE, pw->pending_seq); 748*c2d3d1c2SDorinda Bassey 749*c2d3d1c2SDorinda Bassey while (true) { 750*c2d3d1c2SDorinda Bassey pw_thread_loop_wait(pw->thread_loop); 751*c2d3d1c2SDorinda Bassey 752*c2d3d1c2SDorinda Bassey res = pw->error; 753*c2d3d1c2SDorinda Bassey if (res < 0) { 754*c2d3d1c2SDorinda Bassey pw->error = 0; 755*c2d3d1c2SDorinda Bassey return res; 756*c2d3d1c2SDorinda Bassey } 757*c2d3d1c2SDorinda Bassey if (pw->pending_seq == pw->last_seq) { 758*c2d3d1c2SDorinda Bassey break; 759*c2d3d1c2SDorinda Bassey } 760*c2d3d1c2SDorinda Bassey } 761*c2d3d1c2SDorinda Bassey return 0; 762*c2d3d1c2SDorinda Bassey } 763*c2d3d1c2SDorinda Bassey static void 764*c2d3d1c2SDorinda Bassey on_core_error(void *data, uint32_t id, int seq, int res, const char *message) 765*c2d3d1c2SDorinda Bassey { 766*c2d3d1c2SDorinda Bassey pwaudio *pw = data; 767*c2d3d1c2SDorinda Bassey 768*c2d3d1c2SDorinda Bassey error_report("error id:%u seq:%d res:%d (%s): %s", 769*c2d3d1c2SDorinda Bassey id, seq, res, spa_strerror(res), message); 770*c2d3d1c2SDorinda Bassey 771*c2d3d1c2SDorinda Bassey /* stop and exit the thread loop */ 772*c2d3d1c2SDorinda Bassey pw_thread_loop_signal(pw->thread_loop, FALSE); 773*c2d3d1c2SDorinda Bassey } 774*c2d3d1c2SDorinda Bassey 775*c2d3d1c2SDorinda Bassey static void 776*c2d3d1c2SDorinda Bassey on_core_done(void *data, uint32_t id, int seq) 777*c2d3d1c2SDorinda Bassey { 778*c2d3d1c2SDorinda Bassey pwaudio *pw = data; 779*c2d3d1c2SDorinda Bassey assert(id == PW_ID_CORE); 780*c2d3d1c2SDorinda Bassey pw->last_seq = seq; 781*c2d3d1c2SDorinda Bassey if (pw->pending_seq == seq) { 782*c2d3d1c2SDorinda Bassey /* stop and exit the thread loop */ 783*c2d3d1c2SDorinda Bassey pw_thread_loop_signal(pw->thread_loop, FALSE); 784*c2d3d1c2SDorinda Bassey } 785*c2d3d1c2SDorinda Bassey } 786*c2d3d1c2SDorinda Bassey 787*c2d3d1c2SDorinda Bassey static const struct pw_core_events core_events = { 788*c2d3d1c2SDorinda Bassey PW_VERSION_CORE_EVENTS, 789*c2d3d1c2SDorinda Bassey .done = on_core_done, 790*c2d3d1c2SDorinda Bassey .error = on_core_error, 791*c2d3d1c2SDorinda Bassey }; 792*c2d3d1c2SDorinda Bassey 793*c2d3d1c2SDorinda Bassey static void * 794*c2d3d1c2SDorinda Bassey qpw_audio_init(Audiodev *dev) 795*c2d3d1c2SDorinda Bassey { 796*c2d3d1c2SDorinda Bassey g_autofree pwaudio *pw = g_new0(pwaudio, 1); 797*c2d3d1c2SDorinda Bassey pw_init(NULL, NULL); 798*c2d3d1c2SDorinda Bassey 799*c2d3d1c2SDorinda Bassey trace_pw_audio_init(); 800*c2d3d1c2SDorinda Bassey assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE); 801*c2d3d1c2SDorinda Bassey 802*c2d3d1c2SDorinda Bassey pw->dev = dev; 803*c2d3d1c2SDorinda Bassey pw->thread_loop = pw_thread_loop_new("Pipewire thread loop", NULL); 804*c2d3d1c2SDorinda Bassey if (pw->thread_loop == NULL) { 805*c2d3d1c2SDorinda Bassey error_report("Could not create Pipewire loop"); 806*c2d3d1c2SDorinda Bassey goto fail; 807*c2d3d1c2SDorinda Bassey } 808*c2d3d1c2SDorinda Bassey 809*c2d3d1c2SDorinda Bassey pw->context = 810*c2d3d1c2SDorinda Bassey pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); 811*c2d3d1c2SDorinda Bassey if (pw->context == NULL) { 812*c2d3d1c2SDorinda Bassey error_report("Could not create Pipewire context"); 813*c2d3d1c2SDorinda Bassey goto fail; 814*c2d3d1c2SDorinda Bassey } 815*c2d3d1c2SDorinda Bassey 816*c2d3d1c2SDorinda Bassey if (pw_thread_loop_start(pw->thread_loop) < 0) { 817*c2d3d1c2SDorinda Bassey error_report("Could not start Pipewire loop"); 818*c2d3d1c2SDorinda Bassey goto fail; 819*c2d3d1c2SDorinda Bassey } 820*c2d3d1c2SDorinda Bassey 821*c2d3d1c2SDorinda Bassey pw_thread_loop_lock(pw->thread_loop); 822*c2d3d1c2SDorinda Bassey 823*c2d3d1c2SDorinda Bassey pw->core = pw_context_connect(pw->context, NULL, 0); 824*c2d3d1c2SDorinda Bassey if (pw->core == NULL) { 825*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(pw->thread_loop); 826*c2d3d1c2SDorinda Bassey goto fail; 827*c2d3d1c2SDorinda Bassey } 828*c2d3d1c2SDorinda Bassey 829*c2d3d1c2SDorinda Bassey if (pw_core_add_listener(pw->core, &pw->core_listener, 830*c2d3d1c2SDorinda Bassey &core_events, pw) < 0) { 831*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(pw->thread_loop); 832*c2d3d1c2SDorinda Bassey goto fail; 833*c2d3d1c2SDorinda Bassey } 834*c2d3d1c2SDorinda Bassey if (wait_resync(pw) < 0) { 835*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(pw->thread_loop); 836*c2d3d1c2SDorinda Bassey } 837*c2d3d1c2SDorinda Bassey 838*c2d3d1c2SDorinda Bassey pw_thread_loop_unlock(pw->thread_loop); 839*c2d3d1c2SDorinda Bassey 840*c2d3d1c2SDorinda Bassey return g_steal_pointer(&pw); 841*c2d3d1c2SDorinda Bassey 842*c2d3d1c2SDorinda Bassey fail: 843*c2d3d1c2SDorinda Bassey AUD_log(AUDIO_CAP, "Failed to initialize PW context"); 844*c2d3d1c2SDorinda Bassey if (pw->thread_loop) { 845*c2d3d1c2SDorinda Bassey pw_thread_loop_stop(pw->thread_loop); 846*c2d3d1c2SDorinda Bassey } 847*c2d3d1c2SDorinda Bassey if (pw->context) { 848*c2d3d1c2SDorinda Bassey g_clear_pointer(&pw->context, pw_context_destroy); 849*c2d3d1c2SDorinda Bassey } 850*c2d3d1c2SDorinda Bassey if (pw->thread_loop) { 851*c2d3d1c2SDorinda Bassey g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy); 852*c2d3d1c2SDorinda Bassey } 853*c2d3d1c2SDorinda Bassey return NULL; 854*c2d3d1c2SDorinda Bassey } 855*c2d3d1c2SDorinda Bassey 856*c2d3d1c2SDorinda Bassey static void 857*c2d3d1c2SDorinda Bassey qpw_audio_fini(void *opaque) 858*c2d3d1c2SDorinda Bassey { 859*c2d3d1c2SDorinda Bassey pwaudio *pw = opaque; 860*c2d3d1c2SDorinda Bassey 861*c2d3d1c2SDorinda Bassey if (pw->thread_loop) { 862*c2d3d1c2SDorinda Bassey pw_thread_loop_stop(pw->thread_loop); 863*c2d3d1c2SDorinda Bassey } 864*c2d3d1c2SDorinda Bassey 865*c2d3d1c2SDorinda Bassey if (pw->core) { 866*c2d3d1c2SDorinda Bassey spa_hook_remove(&pw->core_listener); 867*c2d3d1c2SDorinda Bassey spa_zero(pw->core_listener); 868*c2d3d1c2SDorinda Bassey pw_core_disconnect(pw->core); 869*c2d3d1c2SDorinda Bassey } 870*c2d3d1c2SDorinda Bassey 871*c2d3d1c2SDorinda Bassey if (pw->context) { 872*c2d3d1c2SDorinda Bassey pw_context_destroy(pw->context); 873*c2d3d1c2SDorinda Bassey } 874*c2d3d1c2SDorinda Bassey pw_thread_loop_destroy(pw->thread_loop); 875*c2d3d1c2SDorinda Bassey 876*c2d3d1c2SDorinda Bassey g_free(pw); 877*c2d3d1c2SDorinda Bassey } 878*c2d3d1c2SDorinda Bassey 879*c2d3d1c2SDorinda Bassey static struct audio_pcm_ops qpw_pcm_ops = { 880*c2d3d1c2SDorinda Bassey .init_out = qpw_init_out, 881*c2d3d1c2SDorinda Bassey .fini_out = qpw_fini_out, 882*c2d3d1c2SDorinda Bassey .write = qpw_write, 883*c2d3d1c2SDorinda Bassey .buffer_get_free = qpw_buffer_get_free, 884*c2d3d1c2SDorinda Bassey .run_buffer_out = audio_generic_run_buffer_out, 885*c2d3d1c2SDorinda Bassey .enable_out = qpw_enable_out, 886*c2d3d1c2SDorinda Bassey .volume_out = qpw_volume_out, 887*c2d3d1c2SDorinda Bassey .volume_in = qpw_volume_in, 888*c2d3d1c2SDorinda Bassey 889*c2d3d1c2SDorinda Bassey .init_in = qpw_init_in, 890*c2d3d1c2SDorinda Bassey .fini_in = qpw_fini_in, 891*c2d3d1c2SDorinda Bassey .read = qpw_read, 892*c2d3d1c2SDorinda Bassey .run_buffer_in = audio_generic_run_buffer_in, 893*c2d3d1c2SDorinda Bassey .enable_in = qpw_enable_in 894*c2d3d1c2SDorinda Bassey }; 895*c2d3d1c2SDorinda Bassey 896*c2d3d1c2SDorinda Bassey static struct audio_driver pw_audio_driver = { 897*c2d3d1c2SDorinda Bassey .name = "pipewire", 898*c2d3d1c2SDorinda Bassey .descr = "http://www.pipewire.org/", 899*c2d3d1c2SDorinda Bassey .init = qpw_audio_init, 900*c2d3d1c2SDorinda Bassey .fini = qpw_audio_fini, 901*c2d3d1c2SDorinda Bassey .pcm_ops = &qpw_pcm_ops, 902*c2d3d1c2SDorinda Bassey .can_be_default = 1, 903*c2d3d1c2SDorinda Bassey .max_voices_out = INT_MAX, 904*c2d3d1c2SDorinda Bassey .max_voices_in = INT_MAX, 905*c2d3d1c2SDorinda Bassey .voice_size_out = sizeof(PWVoiceOut), 906*c2d3d1c2SDorinda Bassey .voice_size_in = sizeof(PWVoiceIn), 907*c2d3d1c2SDorinda Bassey }; 908*c2d3d1c2SDorinda Bassey 909*c2d3d1c2SDorinda Bassey static void 910*c2d3d1c2SDorinda Bassey register_audio_pw(void) 911*c2d3d1c2SDorinda Bassey { 912*c2d3d1c2SDorinda Bassey audio_driver_register(&pw_audio_driver); 913*c2d3d1c2SDorinda Bassey } 914*c2d3d1c2SDorinda Bassey 915*c2d3d1c2SDorinda Bassey type_init(register_audio_pw); 916