mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:25:35 +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.
|
||||
If not, might need to hav xrun callback set a flag and have process 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. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
||||
0. Create a test for pausing and resuming input and output streams.
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
static const double microphone_latency = 0.2; // seconds
|
||||
|
||||
static enum SoundIoFormat prioritized_formats[] = {
|
||||
SoundIoFormatFloat32NE,
|
||||
SoundIoFormatFloat32FE,
|
||||
|
@ -188,6 +190,9 @@ int main(int argc, char **argv) {
|
|||
|
||||
int err = (backend == SoundIoBackendNone) ?
|
||||
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);
|
||||
if (default_out_device_index < 0)
|
||||
|
@ -274,8 +279,7 @@ int main(int argc, char **argv) {
|
|||
instream->format = *fmt;
|
||||
instream->sample_rate = sample_rate;
|
||||
instream->layout = *layout;
|
||||
instream->buffer_duration = 1.0;
|
||||
instream->period_duration = 0.05;
|
||||
instream->period_duration = microphone_latency / 4.0;
|
||||
instream->read_callback = read_callback;
|
||||
|
||||
if ((err = soundio_instream_open(instream)))
|
||||
|
@ -287,20 +291,19 @@ int main(int argc, char **argv) {
|
|||
outstream->format = *fmt;
|
||||
outstream->sample_rate = sample_rate;
|
||||
outstream->layout = *layout;
|
||||
outstream->buffer_duration = 0.2;
|
||||
outstream->period_duration = 0.1;
|
||||
outstream->buffer_duration = microphone_latency;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underflow_callback = underflow_callback;
|
||||
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
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);
|
||||
if (!ring_buffer)
|
||||
panic("unable to create ring buffer: out of memory");
|
||||
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);
|
||||
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;
|
||||
SoundIoDeviceJack *dj = &dev->backend_data.jack;
|
||||
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) {
|
||||
SoundIoJackPort *port = &client->ports[port_index];
|
||||
|
||||
description_len += port->name_len;
|
||||
|
||||
max_buffer_frames = max(max_buffer_frames, port->latency_range.max);
|
||||
}
|
||||
|
||||
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_max = 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_max = device->buffer_duration_min;
|
||||
device->buffer_duration_current = device->buffer_duration_min;
|
||||
device->buffer_duration_min = device->period_duration_min;
|
||||
device->buffer_duration_max = device->period_duration_min;
|
||||
device->buffer_duration_current = device->period_duration_min;
|
||||
dj->port_count = client->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.fragsize = UINT32_MAX;
|
||||
|
||||
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 bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
||||
int buffer_length = instream->bytes_per_frame *
|
||||
ceil(instream->period_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
||||
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);
|
||||
instream->buffer_duration = (attr->maxlength /
|
||||
(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);
|
||||
return 0;
|
||||
|
|
|
@ -193,6 +193,7 @@ int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
|
|||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
}
|
||||
soundio->current_backend = backend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -229,11 +230,13 @@ void soundio_disconnect(struct SoundIo *soundio) {
|
|||
}
|
||||
|
||||
void soundio_flush_events(struct SoundIo *soundio) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
si->flush_events(si);
|
||||
}
|
||||
|
||||
int soundio_input_device_count(struct SoundIo *soundio) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
soundio_flush_events(soundio);
|
||||
|
@ -242,6 +245,7 @@ int soundio_input_device_count(struct SoundIo *soundio) {
|
|||
}
|
||||
|
||||
int soundio_output_device_count(struct SoundIo *soundio) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
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) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
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) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si->safe_devices_info)
|
||||
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) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
assert(si->safe_devices_info);
|
||||
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) {
|
||||
assert(soundio->current_backend != SoundIoBackendNone);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
assert(si->safe_devices_info);
|
||||
assert(index >= 0);
|
||||
|
|
|
@ -287,15 +287,16 @@ struct SoundIoDevice {
|
|||
int sample_rate_current;
|
||||
|
||||
// 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
|
||||
// unknown for JACK.
|
||||
// 0.0. These values are unknown for PulseAudio. For JACK, buffer duration
|
||||
// and period duration are the same.
|
||||
double buffer_duration_min;
|
||||
double buffer_duration_max;
|
||||
double buffer_duration_current;
|
||||
|
||||
// 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
|
||||
// unknown for PulseAudio.
|
||||
// meaningless for PulseAudio. For JACK, buffer duration and period duration
|
||||
// are the same.
|
||||
double period_duration_min;
|
||||
double period_duration_max;
|
||||
double period_duration_current;
|
||||
|
@ -338,13 +339,14 @@ struct SoundIoOutStream {
|
|||
struct SoundIoChannelLayout layout;
|
||||
|
||||
// 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.
|
||||
// Defaults to 1 second (and then clamped into range).
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
// `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.
|
||||
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
||||
// 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
|
||||
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
||||
// still set this. This value is meaningless for PulseAudio.
|
||||
double period_duration;
|
||||
|
||||
// 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.
|
||||
struct SoundIoChannelLayout layout;
|
||||
|
||||
// Buffer duration in seconds. 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).
|
||||
// Buffer duration in seconds.
|
||||
// After you call `soundio_instream_open` this value is replaced with the
|
||||
// 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;
|
||||
|
||||
// The latency of the captured audio. After this many seconds pass,
|
||||
// `read_callback` is called.
|
||||
// The latency of the captured audio.
|
||||
// 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`.
|
||||
// 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;
|
||||
|
||||
// Defaults to NULL. Put whatever you want here.
|
||||
|
|
Loading…
Reference in a new issue