mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:05:37 +00:00
flesh out buffer semantics
This commit is contained in:
parent
44569708a0
commit
2900616e9b
|
@ -252,6 +252,11 @@ view `coverage/index.html` in a browser.
|
||||||
0. Verify that JACK xrun callback context is the same as process callback.
|
0. Verify that JACK xrun callback context is the same as process callback.
|
||||||
If not, might need to hav xrun callback set a flag and have process callback
|
If not, might need to hav xrun callback set a flag and have process callback
|
||||||
call the underflow callback.
|
call the underflow callback.
|
||||||
|
0. In PulseAudio, to get buffer duration and period duration, fill the buffer
|
||||||
|
with silence before starting, start the stream corked, and have the
|
||||||
|
callback be a callback that just provides silence. Once
|
||||||
|
`soundio_outstream_start` is called, switch to the real callback, then call
|
||||||
|
`pa_stream_flush`, then uncork the stream.
|
||||||
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
||||||
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
||||||
0. Create a test for pausing and resuming input and output streams.
|
0. Create a test for pausing and resuming input and output streams.
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
static const double microphone_latency = 0.2; // seconds
|
||||||
|
|
||||||
static enum SoundIoFormat prioritized_formats[] = {
|
static enum SoundIoFormat prioritized_formats[] = {
|
||||||
SoundIoFormatFloat32NE,
|
SoundIoFormatFloat32NE,
|
||||||
SoundIoFormatFloat32FE,
|
SoundIoFormatFloat32FE,
|
||||||
|
@ -188,6 +190,9 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
int err = (backend == SoundIoBackendNone) ?
|
int err = (backend == SoundIoBackendNone) ?
|
||||||
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
|
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
|
||||||
|
if (err)
|
||||||
|
panic("error connecting: %s", soundio_strerror(err));
|
||||||
|
|
||||||
|
|
||||||
int default_out_device_index = soundio_default_output_device_index(soundio);
|
int default_out_device_index = soundio_default_output_device_index(soundio);
|
||||||
if (default_out_device_index < 0)
|
if (default_out_device_index < 0)
|
||||||
|
@ -274,8 +279,7 @@ int main(int argc, char **argv) {
|
||||||
instream->format = *fmt;
|
instream->format = *fmt;
|
||||||
instream->sample_rate = sample_rate;
|
instream->sample_rate = sample_rate;
|
||||||
instream->layout = *layout;
|
instream->layout = *layout;
|
||||||
instream->buffer_duration = 1.0;
|
instream->period_duration = microphone_latency / 4.0;
|
||||||
instream->period_duration = 0.05;
|
|
||||||
instream->read_callback = read_callback;
|
instream->read_callback = read_callback;
|
||||||
|
|
||||||
if ((err = soundio_instream_open(instream)))
|
if ((err = soundio_instream_open(instream)))
|
||||||
|
@ -287,20 +291,19 @@ int main(int argc, char **argv) {
|
||||||
outstream->format = *fmt;
|
outstream->format = *fmt;
|
||||||
outstream->sample_rate = sample_rate;
|
outstream->sample_rate = sample_rate;
|
||||||
outstream->layout = *layout;
|
outstream->layout = *layout;
|
||||||
outstream->buffer_duration = 0.2;
|
outstream->buffer_duration = microphone_latency;
|
||||||
outstream->period_duration = 0.1;
|
|
||||||
outstream->write_callback = write_callback;
|
outstream->write_callback = write_callback;
|
||||||
outstream->underflow_callback = underflow_callback;
|
outstream->underflow_callback = underflow_callback;
|
||||||
|
|
||||||
if ((err = soundio_outstream_open(outstream)))
|
if ((err = soundio_outstream_open(outstream)))
|
||||||
panic("unable to open output stream: %s", soundio_strerror(err));
|
panic("unable to open output stream: %s", soundio_strerror(err));
|
||||||
|
|
||||||
int capacity = fmax(1.0, instream->buffer_duration) * instream->sample_rate * instream->bytes_per_frame;
|
int capacity = microphone_latency * 2 * instream->sample_rate * instream->bytes_per_frame;
|
||||||
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
|
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
|
||||||
if (!ring_buffer)
|
if (!ring_buffer)
|
||||||
panic("unable to create ring buffer: out of memory");
|
panic("unable to create ring buffer: out of memory");
|
||||||
char *buf = soundio_ring_buffer_write_ptr(ring_buffer);
|
char *buf = soundio_ring_buffer_write_ptr(ring_buffer);
|
||||||
int fill_count = 0.2 * outstream->sample_rate * outstream->bytes_per_frame;
|
int fill_count = microphone_latency * outstream->sample_rate * outstream->bytes_per_frame;
|
||||||
memset(buf, 0, fill_count);
|
memset(buf, 0, fill_count);
|
||||||
soundio_ring_buffer_advance_write_ptr(ring_buffer, fill_count);
|
soundio_ring_buffer_advance_write_ptr(ring_buffer, fill_count);
|
||||||
|
|
||||||
|
|
|
@ -182,13 +182,10 @@ static int refresh_devices_bare(SoundIoPrivate *si) {
|
||||||
SoundIoDevice *device = &dev->pub;
|
SoundIoDevice *device = &dev->pub;
|
||||||
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
||||||
int description_len = client->name_len + 3 + 2 * client->port_count;
|
int description_len = client->name_len + 3 + 2 * client->port_count;
|
||||||
jack_nframes_t max_buffer_frames = 0;
|
|
||||||
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
||||||
SoundIoJackPort *port = &client->ports[port_index];
|
SoundIoJackPort *port = &client->ports[port_index];
|
||||||
|
|
||||||
description_len += port->name_len;
|
description_len += port->name_len;
|
||||||
|
|
||||||
max_buffer_frames = max(max_buffer_frames, port->latency_range.max);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->destruct = destruct_device;
|
dev->destruct = destruct_device;
|
||||||
|
@ -210,9 +207,9 @@ static int refresh_devices_bare(SoundIoPrivate *si) {
|
||||||
device->period_duration_min = sij->period_size / (double) sij->sample_rate;
|
device->period_duration_min = sij->period_size / (double) sij->sample_rate;
|
||||||
device->period_duration_max = device->period_duration_min;
|
device->period_duration_max = device->period_duration_min;
|
||||||
device->period_duration_current = device->period_duration_min;
|
device->period_duration_current = device->period_duration_min;
|
||||||
device->buffer_duration_min = max_buffer_frames / (double) sij->sample_rate;
|
device->buffer_duration_min = device->period_duration_min;
|
||||||
device->buffer_duration_max = device->buffer_duration_min;
|
device->buffer_duration_max = device->period_duration_min;
|
||||||
device->buffer_duration_current = device->buffer_duration_min;
|
device->buffer_duration_current = device->period_duration_min;
|
||||||
dj->port_count = client->port_count;
|
dj->port_count = client->port_count;
|
||||||
dj->ports = allocate<SoundIoDeviceJackPort>(dj->port_count);
|
dj->ports = allocate<SoundIoDeviceJackPort>(dj->port_count);
|
||||||
|
|
||||||
|
|
|
@ -838,8 +838,14 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
ispa->buffer_attr.minreq = UINT32_MAX;
|
ispa->buffer_attr.minreq = UINT32_MAX;
|
||||||
ispa->buffer_attr.fragsize = UINT32_MAX;
|
ispa->buffer_attr.fragsize = UINT32_MAX;
|
||||||
|
|
||||||
if (instream->period_duration > 0.0) {
|
|
||||||
int bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
int bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
||||||
|
if (instream->buffer_duration > 0.0) {
|
||||||
|
int buffer_length = instream->bytes_per_frame *
|
||||||
|
ceil(instream->buffer_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
||||||
|
ispa->buffer_attr.maxlength = buffer_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instream->period_duration > 0.0) {
|
||||||
int buffer_length = instream->bytes_per_frame *
|
int buffer_length = instream->bytes_per_frame *
|
||||||
ceil(instream->period_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
ceil(instream->period_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
||||||
ispa->buffer_attr.fragsize = buffer_length;
|
ispa->buffer_attr.fragsize = buffer_length;
|
||||||
|
@ -872,6 +878,8 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ispa->stream);
|
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ispa->stream);
|
||||||
instream->buffer_duration = (attr->maxlength /
|
instream->buffer_duration = (attr->maxlength /
|
||||||
(double)instream->bytes_per_frame) / (double)instream->sample_rate;
|
(double)instream->bytes_per_frame) / (double)instream->sample_rate;
|
||||||
|
instream->period_duration = (attr->fragsize /
|
||||||
|
(double)instream->bytes_per_frame) / (double)instream->sample_rate;
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -193,6 +193,7 @@ int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
|
||||||
soundio_disconnect(soundio);
|
soundio_disconnect(soundio);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
soundio->current_backend = backend;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,11 +230,13 @@ void soundio_disconnect(struct SoundIo *soundio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_flush_events(struct SoundIo *soundio) {
|
void soundio_flush_events(struct SoundIo *soundio) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
si->flush_events(si);
|
si->flush_events(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_input_device_count(struct SoundIo *soundio) {
|
int soundio_input_device_count(struct SoundIo *soundio) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
if (!si->safe_devices_info)
|
if (!si->safe_devices_info)
|
||||||
soundio_flush_events(soundio);
|
soundio_flush_events(soundio);
|
||||||
|
@ -242,6 +245,7 @@ int soundio_input_device_count(struct SoundIo *soundio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_output_device_count(struct SoundIo *soundio) {
|
int soundio_output_device_count(struct SoundIo *soundio) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
if (!si->safe_devices_info)
|
if (!si->safe_devices_info)
|
||||||
soundio_flush_events(soundio);
|
soundio_flush_events(soundio);
|
||||||
|
@ -250,6 +254,7 @@ int soundio_output_device_count(struct SoundIo *soundio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_default_input_device_index(struct SoundIo *soundio) {
|
int soundio_default_input_device_index(struct SoundIo *soundio) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
if (!si->safe_devices_info)
|
if (!si->safe_devices_info)
|
||||||
soundio_flush_events(soundio);
|
soundio_flush_events(soundio);
|
||||||
|
@ -258,6 +263,7 @@ int soundio_default_input_device_index(struct SoundIo *soundio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_default_output_device_index(struct SoundIo *soundio) {
|
int soundio_default_output_device_index(struct SoundIo *soundio) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
if (!si->safe_devices_info)
|
if (!si->safe_devices_info)
|
||||||
soundio_flush_events(soundio);
|
soundio_flush_events(soundio);
|
||||||
|
@ -266,6 +272,7 @@ int soundio_default_output_device_index(struct SoundIo *soundio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) {
|
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
assert(si->safe_devices_info);
|
assert(si->safe_devices_info);
|
||||||
assert(index >= 0);
|
assert(index >= 0);
|
||||||
|
@ -276,6 +283,7 @@ struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int inde
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) {
|
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) {
|
||||||
|
assert(soundio->current_backend != SoundIoBackendNone);
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||||
assert(si->safe_devices_info);
|
assert(si->safe_devices_info);
|
||||||
assert(index >= 0);
|
assert(index >= 0);
|
||||||
|
|
|
@ -287,15 +287,16 @@ struct SoundIoDevice {
|
||||||
int sample_rate_current;
|
int sample_rate_current;
|
||||||
|
|
||||||
// Buffer duration in seconds. If any values are unknown, they are set to
|
// Buffer duration in seconds. If any values are unknown, they are set to
|
||||||
// 0.0. These values are unknown for PulseAudio. These values are sometimes
|
// 0.0. These values are unknown for PulseAudio. For JACK, buffer duration
|
||||||
// unknown for JACK.
|
// and period duration are the same.
|
||||||
double buffer_duration_min;
|
double buffer_duration_min;
|
||||||
double buffer_duration_max;
|
double buffer_duration_max;
|
||||||
double buffer_duration_current;
|
double buffer_duration_current;
|
||||||
|
|
||||||
// Period duration in seconds. After this much time passes, write_callback
|
// Period duration in seconds. After this much time passes, write_callback
|
||||||
// is called. If values are unknown, they are set to 0.0. These values are
|
// is called. If values are unknown, they are set to 0.0. These values are
|
||||||
// unknown for PulseAudio.
|
// meaningless for PulseAudio. For JACK, buffer duration and period duration
|
||||||
|
// are the same.
|
||||||
double period_duration_min;
|
double period_duration_min;
|
||||||
double period_duration_max;
|
double period_duration_max;
|
||||||
double period_duration_current;
|
double period_duration_current;
|
||||||
|
@ -338,13 +339,14 @@ struct SoundIoOutStream {
|
||||||
struct SoundIoChannelLayout layout;
|
struct SoundIoChannelLayout layout;
|
||||||
|
|
||||||
// Buffer duration in seconds.
|
// Buffer duration in seconds.
|
||||||
// After you call soundio_outstream_open this value is replaced with the
|
// After you call `soundio_outstream_open` this value is replaced with the
|
||||||
// actual duration, as near to this value as possible.
|
// actual duration, as near to this value as possible.
|
||||||
// Defaults to 1 second (and then clamped into range).
|
// Defaults to 1 second (and then clamped into range).
|
||||||
// If the device has unknown buffer duration min and max values, you may
|
// If the device has unknown buffer duration min and max values, you may
|
||||||
// still set this. If you set this and the backend is PulseAudio, it
|
// still set this. If you set this and the backend is PulseAudio, it
|
||||||
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength`
|
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength`
|
||||||
// and `tlength`.
|
// and `tlength`. With PulseAudio, this value is not replaced with the
|
||||||
|
// actual duration until `soundio_outstream_start`.
|
||||||
double buffer_duration;
|
double buffer_duration;
|
||||||
|
|
||||||
// `period_duration` is the latency; how much time it takes
|
// `period_duration` is the latency; how much time it takes
|
||||||
|
@ -353,8 +355,7 @@ struct SoundIoOutStream {
|
||||||
// actual period duration, as near to this value as possible.
|
// actual period duration, as near to this value as possible.
|
||||||
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
||||||
// If the device has unknown period duration min and max values, you may
|
// If the device has unknown period duration min and max values, you may
|
||||||
// still set this. If you set this and the backend is PulseAudio, it
|
// still set this. This value is meaningless for PulseAudio.
|
||||||
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
|
||||||
double period_duration;
|
double period_duration;
|
||||||
|
|
||||||
// Defaults to NULL. Put whatever you want here.
|
// Defaults to NULL. Put whatever you want here.
|
||||||
|
@ -413,14 +414,27 @@ struct SoundIoInStream {
|
||||||
// Defaults to Stereo, if available, followed by the first layout supported.
|
// Defaults to Stereo, if available, followed by the first layout supported.
|
||||||
struct SoundIoChannelLayout layout;
|
struct SoundIoChannelLayout layout;
|
||||||
|
|
||||||
// Buffer duration in seconds. If the captured audio frames exceeds this
|
// Buffer duration in seconds.
|
||||||
// before they are read, a buffer overrun occurs and the frames are lost.
|
// After you call `soundio_instream_open` this value is replaced with the
|
||||||
// Defaults to 1 second (and then clamped into range).
|
// actual duration, as near to this value as possible.
|
||||||
|
// If the captured audio frames exceeds this before they are read, a buffer
|
||||||
|
// overrun occurs and the frames are lost.
|
||||||
|
// Defaults to 1 second (and then clamped into range). For PulseAudio,
|
||||||
|
// defaults to PulseAudio's default value, usually large. If you set this
|
||||||
|
// and the backend is PulseAudio, it sets `PA_STREAM_ADJUST_LATENCY` and
|
||||||
|
// is the value used for `maxlength`. With PulseAudio, this value is not
|
||||||
|
// replaced with the actual duration until `soundio_instream_start`.
|
||||||
double buffer_duration;
|
double buffer_duration;
|
||||||
|
|
||||||
// The latency of the captured audio. After this many seconds pass,
|
// The latency of the captured audio.
|
||||||
// `read_callback` is called.
|
// After you call `soundio_instream_open` this value is replaced with the
|
||||||
|
// actual duration, as near to this value as possible.
|
||||||
|
// After this many seconds pass, `read_callback` is called.
|
||||||
// Defaults to `buffer_duration / 8`.
|
// Defaults to `buffer_duration / 8`.
|
||||||
|
// If you set this and the backend is PulseAudio, it sets
|
||||||
|
// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
||||||
|
// With PulseAudio, this value is not replaced with the actual duration
|
||||||
|
// until `soundio_instream_start`.
|
||||||
double period_duration;
|
double period_duration;
|
||||||
|
|
||||||
// Defaults to NULL. Put whatever you want here.
|
// Defaults to NULL. Put whatever you want here.
|
||||||
|
|
Loading…
Reference in a new issue