Use threaded mainloop for Pulse. Seems to fix most issues.
This commit is contained in:
parent
e83f3ed444
commit
fff7aba8ec
103
audio/pulse.c
103
audio/pulse.c
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
pa_mainloop *mainloop;
|
pa_threaded_mainloop *mainloop;
|
||||||
pa_context *context;
|
pa_context *context;
|
||||||
pa_stream *stream;
|
pa_stream *stream;
|
||||||
bool nonblock;
|
bool nonblock;
|
||||||
|
@ -37,6 +37,9 @@ static void __pulse_free(void *data)
|
||||||
pa_t *pa = data;
|
pa_t *pa = data;
|
||||||
if (pa)
|
if (pa)
|
||||||
{
|
{
|
||||||
|
if (pa->mainloop)
|
||||||
|
pa_threaded_mainloop_stop(pa->mainloop);
|
||||||
|
|
||||||
if (pa->stream)
|
if (pa->stream)
|
||||||
{
|
{
|
||||||
pa_stream_disconnect(pa->stream);
|
pa_stream_disconnect(pa->stream);
|
||||||
|
@ -50,7 +53,7 @@ static void __pulse_free(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa->mainloop)
|
if (pa->mainloop)
|
||||||
pa_mainloop_free(pa->mainloop);
|
pa_threaded_mainloop_free(pa->mainloop);
|
||||||
|
|
||||||
free(pa);
|
free(pa);
|
||||||
}
|
}
|
||||||
|
@ -68,29 +71,77 @@ static inline uint8_t is_little_endian(void)
|
||||||
return u.y[0];
|
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)
|
static void* __pulse_init(const char* device, int rate, int latency)
|
||||||
{
|
{
|
||||||
pa_t *pa = calloc(1, sizeof(*pa));
|
pa_t *pa = calloc(1, sizeof(*pa));
|
||||||
if (!pa)
|
if (!pa)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
pa->mainloop = pa_mainloop_new();
|
pa->mainloop = pa_threaded_mainloop_new();
|
||||||
if (!pa->mainloop)
|
if (!pa->mainloop)
|
||||||
goto error;
|
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)
|
if (!pa->context)
|
||||||
goto error;
|
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)
|
if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
pa_context_state_t cstate;
|
pa_threaded_mainloop_lock(pa->mainloop);
|
||||||
do
|
if (pa_threaded_mainloop_start(pa->mainloop) < 0)
|
||||||
{
|
goto error;
|
||||||
pa_mainloop_iterate(pa->mainloop, 1, NULL);
|
|
||||||
cstate = pa_context_get_state(pa->context);
|
pa_threaded_mainloop_wait(pa->mainloop);
|
||||||
if (!PA_CONTEXT_IS_GOOD(cstate)) goto error;
|
|
||||||
} while (cstate != PA_CONTEXT_READY);
|
if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
|
||||||
|
goto unlock_error;
|
||||||
|
|
||||||
pa_sample_spec spec = {
|
pa_sample_spec spec = {
|
||||||
.format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE,
|
.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);
|
pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL);
|
||||||
if (!pa->stream)
|
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 = {
|
pa_buffer_attr buffer_attr = {
|
||||||
.maxlength = -1,
|
.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)
|
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
pa_stream_state_t sstate;
|
pa_threaded_mainloop_wait(pa->mainloop);
|
||||||
do
|
|
||||||
{
|
if (pa_stream_get_state(pa->stream) != PA_STREAM_READY)
|
||||||
pa_mainloop_iterate(pa->mainloop, 1, NULL);
|
goto unlock_error;
|
||||||
sstate = pa_stream_get_state(pa->stream);
|
|
||||||
if(!PA_STREAM_IS_GOOD(sstate)) goto error;
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
} while(sstate != PA_STREAM_READY);
|
|
||||||
|
|
||||||
return pa;
|
return pa;
|
||||||
|
|
||||||
|
unlock_error:
|
||||||
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
error:
|
error:
|
||||||
__pulse_free(pa);
|
__pulse_free(pa);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -132,12 +188,15 @@ static ssize_t __pulse_write(void* data, const void* buf, size_t size)
|
||||||
{
|
{
|
||||||
pa_t *pa = data;
|
pa_t *pa = data;
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock(pa->mainloop);
|
||||||
unsigned length = pa_stream_writable_size(pa->stream);
|
unsigned length = pa_stream_writable_size(pa->stream);
|
||||||
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
while (length < size)
|
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);
|
length = pa_stream_writable_size(pa->stream);
|
||||||
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
|
|
||||||
if (pa->nonblock)
|
if (pa->nonblock)
|
||||||
break;
|
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;
|
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_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||||
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
return write_size;
|
return write_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue