diff --git a/audio/pulse.c b/audio/pulse.c index 79e0fd8822..819ab6eccf 100644 --- a/audio/pulse.c +++ b/audio/pulse.c @@ -26,7 +26,7 @@ typedef struct { - pa_mainloop *mainloop; + pa_threaded_mainloop *mainloop; pa_context *context; pa_stream *stream; bool nonblock; @@ -37,6 +37,9 @@ static void __pulse_free(void *data) pa_t *pa = data; if (pa) { + if (pa->mainloop) + pa_threaded_mainloop_stop(pa->mainloop); + if (pa->stream) { pa_stream_disconnect(pa->stream); @@ -50,7 +53,7 @@ static void __pulse_free(void *data) } if (pa->mainloop) - pa_mainloop_free(pa->mainloop); + pa_threaded_mainloop_free(pa->mainloop); free(pa); } @@ -68,29 +71,77 @@ static inline uint8_t is_little_endian(void) return u.y[0]; } +static void context_state_cb(pa_context *c, void *data) +{ + pa_t *pa = data; + switch (pa_context_get_state(c)) + { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(pa->mainloop, 0); + break; + default: + break; + } +} + +static void stream_state_cb(pa_stream *s, void *data) +{ + pa_t *pa = data; + switch (pa_stream_get_state(s)) { + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal(pa->mainloop, 0); + break; + default: + break; + } +} + +static void stream_request_cb(pa_stream *s, size_t length, void *data) +{ + (void)length; + (void)s; + pa_t *pa = data; + pa_threaded_mainloop_signal(pa->mainloop, 0); +} + +static void stream_latency_update_cb(pa_stream *s, void *data) +{ + (void)s; + pa_t *pa = data; + pa_threaded_mainloop_signal(pa->mainloop, 0); +} + static void* __pulse_init(const char* device, int rate, int latency) { pa_t *pa = calloc(1, sizeof(*pa)); if (!pa) goto error; - pa->mainloop = pa_mainloop_new(); + pa->mainloop = pa_threaded_mainloop_new(); if (!pa->mainloop) goto error; - pa->context = pa_context_new(pa_mainloop_get_api(pa->mainloop), "SSNES"); + pa->context = pa_context_new(pa_threaded_mainloop_get_api(pa->mainloop), "SSNES"); if (!pa->context) goto error; + + pa_context_set_state_callback(pa->context, context_state_cb, pa); + if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0) goto error; - pa_context_state_t cstate; - do - { - pa_mainloop_iterate(pa->mainloop, 1, NULL); - cstate = pa_context_get_state(pa->context); - if (!PA_CONTEXT_IS_GOOD(cstate)) goto error; - } while (cstate != PA_CONTEXT_READY); + pa_threaded_mainloop_lock(pa->mainloop); + if (pa_threaded_mainloop_start(pa->mainloop) < 0) + goto error; + + pa_threaded_mainloop_wait(pa->mainloop); + + if (pa_context_get_state(pa->context) != PA_CONTEXT_READY) + goto unlock_error; pa_sample_spec spec = { .format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE, @@ -100,7 +151,11 @@ static void* __pulse_init(const char* device, int rate, int latency) pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL); if (!pa->stream) - goto error; + goto unlock_error; + + pa_stream_set_state_callback(pa->stream, stream_state_cb, pa); + pa_stream_set_write_callback(pa->stream, stream_request_cb, pa); + pa_stream_set_latency_update_callback(pa->stream, stream_latency_update_cb, pa); pa_buffer_attr buffer_attr = { .maxlength = -1, @@ -113,16 +168,17 @@ static void* __pulse_init(const char* device, int rate, int latency) if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) goto error; - pa_stream_state_t sstate; - do - { - pa_mainloop_iterate(pa->mainloop, 1, NULL); - sstate = pa_stream_get_state(pa->stream); - if(!PA_STREAM_IS_GOOD(sstate)) goto error; - } while(sstate != PA_STREAM_READY); + pa_threaded_mainloop_wait(pa->mainloop); + + if (pa_stream_get_state(pa->stream) != PA_STREAM_READY) + goto unlock_error; + + pa_threaded_mainloop_unlock(pa->mainloop); return pa; +unlock_error: + pa_threaded_mainloop_unlock(pa->mainloop); error: __pulse_free(pa); return NULL; @@ -132,12 +188,15 @@ static ssize_t __pulse_write(void* data, const void* buf, size_t size) { pa_t *pa = data; + pa_threaded_mainloop_lock(pa->mainloop); unsigned length = pa_stream_writable_size(pa->stream); + pa_threaded_mainloop_unlock(pa->mainloop); while (length < size) { - pa_mainloop_iterate(pa->mainloop, 1, NULL); - + pa_threaded_mainloop_wait(pa->mainloop); + pa_threaded_mainloop_lock(pa->mainloop); length = pa_stream_writable_size(pa->stream); + pa_threaded_mainloop_unlock(pa->mainloop); if (pa->nonblock) break; @@ -145,7 +204,9 @@ static ssize_t __pulse_write(void* data, const void* buf, size_t size) size_t write_size = length < size ? length : size; + pa_threaded_mainloop_lock(pa->mainloop); pa_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE); + pa_threaded_mainloop_unlock(pa->mainloop); return write_size; }