Delete the concept of prebuffering

This commit is contained in:
Andrew Kelley 2015-07-29 20:55:28 -07:00
parent d3cf8f02db
commit 44569708a0
9 changed files with 64 additions and 107 deletions

View file

@ -20,10 +20,10 @@ behavior on every platform.
## Features and Limitations ## Features and Limitations
* Supported backends: * Supported backends:
- [JACK](http://jackaudio.org/)
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/) - [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/) - [ALSA](http://www.alsa-project.org/)
- Dummy (silence) - Dummy (silence)
- [JACK](http://jackaudio.org/)
- (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html) - (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx) - (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- (planned) [ASIO](http://www.asio4all.com/) - (planned) [ASIO](http://www.asio4all.com/)
@ -245,19 +245,15 @@ view `coverage/index.html` in a browser.
0. implement CoreAudio (OSX) backend, get examples working 0. implement CoreAudio (OSX) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working 0. implement WASAPI (Windows) backend, get examples working
0. implement ASIO (Windows) backend, get examples working 0. implement ASIO (Windows) backend, get examples working
0. JACK: implement prebuffering ...or delete prebuffering as a concept
0. Avoid calling `soundio_panic` in PulseAudio. 0. Avoid calling `soundio_panic` in PulseAudio.
0. Figure out a way to test prebuf. I suspect prebuf not working for ALSA
which is why we have to pre-fill the ring buffer with silence for
the microphone example.
0. PulseAudio: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`. 0. PulseAudio: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`.
0. clear buffer maybe could take an argument to say how many frames to not clear
0. In ALSA do we need to wake up the poll when destroying the in or out stream? 0. In ALSA do we need to wake up the poll when destroying the in or out stream?
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. 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 clearing the playback buffer and prebuffering.
0. Create a test for pausing and resuming input and output streams. 0. Create a test for pausing and resuming input and output streams.
0. Create a test for the latency / synchronization API. 0. Create a test for the latency / synchronization API.
- Input is an audio file and some events indexed at particular frame - when - Input is an audio file and some events indexed at particular frame - when
@ -290,7 +286,6 @@ view `coverage/index.html` in a browser.
0. make rtprio warning a callback and have existing behavior be the default callback 0. make rtprio warning a callback and have existing behavior be the default callback
0. write detailed docs on buffer underflows explaining when they occur, what state 0. write detailed docs on buffer underflows explaining when they occur, what state
changes are related to them, and how to recover from them. changes are related to them, and how to recover from them.
0. API to trigger playback even if prebuf condition isn't met yet.
0. Consider testing on FreeBSD 0. Consider testing on FreeBSD
## Planned Uses for libsoundio ## Planned Uses for libsoundio

View file

@ -61,7 +61,6 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
int write_count = min_int(available_frame_count, free_count); int write_count = min_int(available_frame_count, free_count);
int frames_left = write_count; int frames_left = write_count;
for (;;) { for (;;) {
int frame_count = frames_left; int frame_count = frames_left;
@ -148,7 +147,7 @@ static void underflow_callback(struct SoundIoOutStream *outstream) {
} }
static int usage(char *exe) { static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio] [--in-device name] [--out-device name]\n", exe); fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio] [--jack] [--in-device name] [--out-device name]\n", exe);
return 1; return 1;
} }
@ -165,6 +164,8 @@ int main(int argc, char **argv) {
backend = SoundIoBackendAlsa; backend = SoundIoBackendAlsa;
} else if (strcmp("--pulseaudio", arg) == 0) { } else if (strcmp("--pulseaudio", arg) == 0) {
backend = SoundIoBackendPulseAudio; backend = SoundIoBackendPulseAudio;
} else if (strcmp("--jack", arg) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("--in-device", arg) == 0) { } else if (strcmp("--in-device", arg) == 0) {
if (++i >= argc) { if (++i >= argc) {
return usage(exe); return usage(exe);
@ -290,7 +291,6 @@ int main(int argc, char **argv) {
outstream->period_duration = 0.1; 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;
outstream->prebuf_duration = 0.0;
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));

View file

@ -1086,8 +1086,6 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
outstream->period_duration = clamp(device->period_duration_min, outstream->period_duration = clamp(device->period_duration_min,
outstream->buffer_duration / 2.0, device->period_duration_max); outstream->buffer_duration / 2.0, device->period_duration_max);
} }
if (outstream->prebuf_duration == -1.0)
outstream->prebuf_duration = outstream->buffer_duration;
int ch_count = outstream->layout.channel_count; int ch_count = outstream->layout.channel_count;
@ -1196,8 +1194,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
} }
snd_pcm_uframes_t prebuf_frames = ceil(outstream->prebuf_duration * (double)outstream->sample_rate); if ((err = snd_pcm_sw_params_set_start_threshold(osa->handle, swparams, 0)) < 0) {
if ((err = snd_pcm_sw_params_set_start_threshold(osa->handle, swparams, prebuf_frames)) < 0) {
outstream_destroy_alsa(si, os); outstream_destroy_alsa(si, os);
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
} }
@ -1243,16 +1240,23 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
} }
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
assert(!osa->thread); assert(!osa->thread);
osa->thread_exit_flag.test_and_set(); // Give the API user a chance to fill the buffer before playback commences.
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
int err; int err;
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread))) { if (avail < 0) {
outstream_destroy_alsa(si, os); if ((err = xrun_recovery(os, avail)) < 0)
return err; return SoundIoErrorStreaming;
} }
outstream->write_callback(outstream, avail);
osa->thread_exit_flag.test_and_set();
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread)))
return err;
return 0; return 0;
} }

View file

@ -17,15 +17,12 @@ static void playback_thread_run(void *arg) {
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = &os->backend_data.dummy; SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
double start_time = soundio_os_get_time();
osd->playback_start_time = start_time;
osd->frames_consumed = 0;
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer); int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes; int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int fill_frames = fill_bytes / outstream->bytes_per_frame;
int free_frames = free_bytes / outstream->bytes_per_frame; int free_frames = free_bytes / outstream->bytes_per_frame;
osd->prebuf_frames_left = osd->prebuf_frame_count - fill_frames;
outstream->write_callback(outstream, free_frames); outstream->write_callback(outstream, free_frames);
double start_time = soundio_os_get_time();
long frames_consumed = 0;
while (osd->abort_flag.test_and_set()) { while (osd->abort_flag.test_and_set()) {
double now = soundio_os_get_time(); double now = soundio_os_get_time();
@ -39,22 +36,20 @@ static void playback_thread_run(void *arg) {
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes; int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int fill_frames = fill_bytes / outstream->bytes_per_frame; int fill_frames = fill_bytes / outstream->bytes_per_frame;
int free_frames = free_bytes / outstream->bytes_per_frame; int free_frames = free_bytes / outstream->bytes_per_frame;
if (osd->prebuf_frames_left > 0) {
outstream->write_callback(outstream, free_frames);
continue;
}
double total_time = soundio_os_get_time() - osd->playback_start_time; double total_time = soundio_os_get_time() - start_time;
long total_frames = total_time * outstream->sample_rate; long total_frames = total_time * outstream->sample_rate;
int frames_to_kill = total_frames - osd->frames_consumed; int frames_to_kill = total_frames - frames_consumed;
int read_count = min(frames_to_kill, fill_frames); int read_count = min(frames_to_kill, fill_frames);
int byte_count = read_count * outstream->bytes_per_frame; int byte_count = read_count * outstream->bytes_per_frame;
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count); soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
osd->frames_consumed += read_count; frames_consumed += read_count;
if (frames_to_kill > read_count) { if (frames_to_kill > fill_frames) {
osd->prebuf_frames_left = osd->prebuf_frame_count;
outstream->underflow_callback(outstream); outstream->underflow_callback(outstream);
outstream->write_callback(outstream, free_frames);
frames_consumed = 0;
start_time = soundio_os_get_time();
} else if (free_frames > 0) { } else if (free_frames > 0) {
outstream->write_callback(outstream, free_frames); outstream->write_callback(outstream, free_frames);
} }
@ -70,26 +65,33 @@ static void capture_thread_run(void *arg) {
double start_time = soundio_os_get_time(); double start_time = soundio_os_get_time();
while (isd->abort_flag.test_and_set()) { while (isd->abort_flag.test_and_set()) {
double now = soundio_os_get_time(); double now = soundio_os_get_time();
double total_time = now - start_time;
long total_frames = total_time * instream->sample_rate;
int frames_to_kill = total_frames - frames_consumed;
int free_bytes = soundio_ring_buffer_free_count(&isd->ring_buffer);
int free_frames = free_bytes / instream->bytes_per_frame;
int write_count = min(frames_to_kill, free_frames);
int frames_left = max(frames_to_kill - write_count, 0);
int byte_count = write_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count);
frames_consumed += write_count;
isd->hole_size += frames_left;
if (write_count > 0)
instream->read_callback(instream, write_count);
now = soundio_os_get_time();
double time_passed = now - start_time; double time_passed = now - start_time;
double next_period = start_time + double next_period = start_time +
ceil(time_passed / instream->period_duration) * instream->period_duration; ceil(time_passed / instream->period_duration) * instream->period_duration;
double relative_time = next_period - now; double relative_time = next_period - now;
soundio_os_cond_timed_wait(isd->cond, nullptr, relative_time); soundio_os_cond_timed_wait(isd->cond, nullptr, relative_time);
int fill_bytes = soundio_ring_buffer_fill_count(&isd->ring_buffer);
int free_bytes = soundio_ring_buffer_capacity(&isd->ring_buffer) - fill_bytes;
int fill_frames = fill_bytes / instream->bytes_per_frame;
int free_frames = free_bytes / instream->bytes_per_frame;
double total_time = soundio_os_get_time() - start_time;
long total_frames = total_time * instream->sample_rate;
int frames_to_kill = total_frames - frames_consumed;
int write_count = min(frames_to_kill, free_frames);
int byte_count = write_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count);
frames_consumed += write_count;
if (frames_to_kill > free_frames) {
// TODO overflow callback
frames_consumed = 0;
start_time = soundio_os_get_time();
}
if (fill_frames > 0) {
instream->read_callback(instream, fill_frames);
}
} }
} }
@ -160,11 +162,6 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
osd->buffer_frame_count = actual_capacity / outstream->bytes_per_frame; osd->buffer_frame_count = actual_capacity / outstream->bytes_per_frame;
outstream->buffer_duration = osd->buffer_frame_count / (double) outstream->sample_rate; outstream->buffer_duration = osd->buffer_frame_count / (double) outstream->sample_rate;
if (outstream->prebuf_duration == -1.0)
outstream->prebuf_duration = outstream->buffer_duration;
outstream->prebuf_duration = min(outstream->prebuf_duration, outstream->buffer_duration);
osd->prebuf_frame_count = ceil(outstream->prebuf_duration * (double) outstream->sample_rate);
osd->cond = soundio_os_cond_create(); osd->cond = soundio_os_cond_create();
if (!osd->cond) { if (!osd->cond) {
outstream_destroy_dummy(si, os); outstream_destroy_dummy(si, os);
@ -231,20 +228,12 @@ static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
int byte_count = frame_count * outstream->bytes_per_frame; int byte_count = frame_count * outstream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count); soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
if (osd->prebuf_frames_left > 0) {
osd->prebuf_frames_left -= frame_count;
if (osd->prebuf_frames_left <= 0) {
osd->playback_start_time = soundio_os_get_time();
osd->frames_consumed = 0;
}
}
return 0; return 0;
} }
static int outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { static int outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamDummy *osd = &os->backend_data.dummy; SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
soundio_ring_buffer_clear(&osd->ring_buffer); soundio_ring_buffer_clear(&osd->ring_buffer);
osd->prebuf_frames_left = osd->prebuf_frame_count;
return 0; return 0;
} }
@ -329,13 +318,6 @@ static int instream_begin_read_dummy(SoundIoPrivate *si,
assert(*frame_count >= 0); assert(*frame_count >= 0);
assert(*frame_count <= isd->buffer_frame_count); assert(*frame_count <= isd->buffer_frame_count);
if (isd->hole_size > 0) {
*out_areas = nullptr;
isd->read_frame_count = min(isd->hole_size, *frame_count);
*frame_count = isd->read_frame_count;
return 0;
}
int fill_byte_count = soundio_ring_buffer_fill_count(&isd->ring_buffer); int fill_byte_count = soundio_ring_buffer_fill_count(&isd->ring_buffer);
int fill_frame_count = fill_byte_count / instream->bytes_per_frame; int fill_frame_count = fill_byte_count / instream->bytes_per_frame;
isd->read_frame_count = min(*frame_count, fill_frame_count); isd->read_frame_count = min(*frame_count, fill_frame_count);
@ -357,14 +339,8 @@ static int instream_begin_read_dummy(SoundIoPrivate *si,
static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamDummy *isd = &is->backend_data.dummy; SoundIoInStreamDummy *isd = &is->backend_data.dummy;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
if (isd->hole_size > 0) {
isd->hole_size -= isd->read_frame_count;
return 0;
}
int byte_count = isd->read_frame_count * instream->bytes_per_frame; int byte_count = isd->read_frame_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count); soundio_ring_buffer_advance_read_ptr(&isd->ring_buffer, byte_count);
return 0; return 0;
} }
@ -493,7 +469,7 @@ int soundio_dummy_init(SoundIoPrivate *si) {
device->ref_count = 1; device->ref_count = 1;
device->soundio = soundio; device->soundio = soundio;
device->name = strdup("dummy-in"); device->name = strdup("dummy-in");
device->description = strdup("Dummy input device"); device->description = strdup("Dummy Input Device");
if (!device->name || !device->description) { if (!device->name || !device->description) {
soundio_device_unref(device); soundio_device_unref(device);
destroy_dummy(si); destroy_dummy(si);

View file

@ -31,9 +31,6 @@ struct SoundIoOutStreamDummy {
atomic_flag abort_flag; atomic_flag abort_flag;
int buffer_frame_count; int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer; struct SoundIoRingBuffer ring_buffer;
int prebuf_frame_count;
int prebuf_frames_left;
long frames_consumed;
double playback_start_time; double playback_start_time;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
@ -45,7 +42,6 @@ struct SoundIoInStreamDummy {
int read_frame_count; int read_frame_count;
int buffer_frame_count; int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer; struct SoundIoRingBuffer ring_buffer;
int hole_size;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };

View file

@ -428,7 +428,6 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
outstream->buffer_duration = 0.0; outstream->buffer_duration = 0.0;
outstream->period_duration = device->period_duration_current; outstream->period_duration = device->period_duration_current;
outstream->prebuf_duration = 0.0;
osj->period_size = sij->period_size; osj->period_size = sij->period_size;
jack_status_t status; jack_status_t status;
@ -573,8 +572,6 @@ static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOut
} }
static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
// JACK does not support `prebuf` which is the same as a `prebuf` value of 0,
// which means that clearing the buffer is always successful and does nothing.
return 0; return 0;
} }

View file

@ -637,7 +637,7 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
ospa->buffer_attr.maxlength = UINT32_MAX; ospa->buffer_attr.maxlength = UINT32_MAX;
ospa->buffer_attr.tlength = UINT32_MAX; ospa->buffer_attr.tlength = UINT32_MAX;
ospa->buffer_attr.prebuf = UINT32_MAX; ospa->buffer_attr.prebuf = 0;
ospa->buffer_attr.minreq = UINT32_MAX; ospa->buffer_attr.minreq = UINT32_MAX;
ospa->buffer_attr.fragsize = UINT32_MAX; ospa->buffer_attr.fragsize = UINT32_MAX;
@ -649,12 +649,6 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
ospa->buffer_attr.maxlength = buffer_length; ospa->buffer_attr.maxlength = buffer_length;
ospa->buffer_attr.tlength = buffer_length; ospa->buffer_attr.tlength = buffer_length;
} }
if (outstream->prebuf_duration >= 0.0) {
int prebuf_length = outstream->bytes_per_frame *
ceil(outstream->prebuf_duration * bytes_per_second / (double)outstream->bytes_per_frame);
ospa->buffer_attr.prebuf = prebuf_length;
}
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
@ -684,8 +678,6 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ospa->stream); const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ospa->stream);
outstream->buffer_duration = (attr->maxlength / outstream->buffer_duration = (attr->maxlength /
(double)outstream->bytes_per_frame) / (double)outstream->sample_rate; (double)outstream->bytes_per_frame) / (double)outstream->sample_rate;
outstream->prebuf_duration = (attr->prebuf /
(double)outstream->bytes_per_frame) / (double)outstream->sample_rate;
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
@ -842,7 +834,7 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
ispa->buffer_attr.maxlength = UINT32_MAX; ispa->buffer_attr.maxlength = UINT32_MAX;
ispa->buffer_attr.tlength = UINT32_MAX; ispa->buffer_attr.tlength = UINT32_MAX;
ispa->buffer_attr.prebuf = UINT32_MAX; ispa->buffer_attr.prebuf = 0;
ispa->buffer_attr.minreq = UINT32_MAX; ispa->buffer_attr.minreq = UINT32_MAX;
ispa->buffer_attr.fragsize = UINT32_MAX; ispa->buffer_attr.fragsize = UINT32_MAX;

View file

@ -359,8 +359,6 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device)
outstream->error_callback = default_outstream_error_callback; outstream->error_callback = default_outstream_error_callback;
outstream->underflow_callback = default_underflow_callback; outstream->underflow_callback = default_underflow_callback;
outstream->prebuf_duration = -1.0;
return outstream; return outstream;
} }

View file

@ -357,14 +357,6 @@ struct SoundIoOutStream {
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`. // sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
double period_duration; double period_duration;
// How many seconds need to be in the buffer before playback will commence.
// If a buffer underflow occurs, this prebuffering will be again enabled.
// This value defaults to being the same as `buffer_duration`.
// After you call `soundio_outstream_open` this value is replaced with the
// actual `prebuf_duration`, as near to this value as possible.
// JACK does not support prebuffering; `prebuf_duration` is effectively 0.
double prebuf_duration;
// Defaults to NULL. Put whatever you want here. // Defaults to NULL. Put whatever you want here.
void *userdata; void *userdata;
// In this callback, you call `soundio_outstream_begin_write` and // In this callback, you call `soundio_outstream_begin_write` and
@ -619,12 +611,15 @@ bool soundio_device_supports_layout(struct SoundIoDevice *device,
// Allocates memory and sets defaults. Next you should fill out the struct fields // Allocates memory and sets defaults. Next you should fill out the struct fields
// and then call `soundio_outstream_open`. // and then call `soundio_outstream_open`.
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
int soundio_outstream_open(struct SoundIoOutStream *outstream);
// You may not call this function from the `write_callback` thread context. // You may not call this function from the `write_callback` thread context.
void soundio_outstream_destroy(struct SoundIoOutStream *outstream); void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
// After you call this function, `buffer_duration` and `period_duration` are
// set to the correct values, if available.
// The next thing to do is call `soundio_instream_start`.
int soundio_outstream_open(struct SoundIoOutStream *outstream);
// After you call this function, `write_callback` will be called.
int soundio_outstream_start(struct SoundIoOutStream *outstream); int soundio_outstream_start(struct SoundIoOutStream *outstream);
// Call this function when you are ready to begin writing to the device buffer. // Call this function when you are ready to begin writing to the device buffer.
@ -646,7 +641,7 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
// You must call this function only from the `write_callback` thread context. // You must call this function only from the `write_callback` thread context.
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count); int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count);
// Clears the output stream buffer and the stream goes into prebuffering mode. // Clears the output stream buffer.
// You must call this function only from the `write_callback` thread context. // You must call this function only from the `write_callback` thread context.
int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
@ -663,11 +658,15 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
// Allocates memory and sets defaults. Next you should fill out the struct fields // Allocates memory and sets defaults. Next you should fill out the struct fields
// and then call `soundio_instream_open`. // and then call `soundio_instream_open`.
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device); struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device);
// You must not call this function from `read_callback`. // You may not call this function from `read_callback`.
void soundio_instream_destroy(struct SoundIoInStream *instream); void soundio_instream_destroy(struct SoundIoInStream *instream);
// After you call this function, `buffer_duration` and `period_duration` are
// set to the correct values, if available.
// The next thing to do is call `soundio_instream_start`.
int soundio_instream_open(struct SoundIoInStream *instream); int soundio_instream_open(struct SoundIoInStream *instream);
// After you call this function, `read_callback` will be called.
int soundio_instream_start(struct SoundIoInStream *instream); int soundio_instream_start(struct SoundIoInStream *instream);
// Call this function when you are ready to begin reading from the device // Call this function when you are ready to begin reading from the device