mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:05:37 +00:00
dummy: implement prebuffering
This commit is contained in:
parent
69764e1afa
commit
511fcafc3b
|
@ -231,7 +231,6 @@ view `coverage/index.html` in a browser.
|
|||
|
||||
## Roadmap
|
||||
|
||||
0. pipe record to playback example working with dummy osx, windows
|
||||
0. pipe record to playback example working with ALSA linux
|
||||
0. expose prebuf
|
||||
0. why does pulseaudio microphone use up all the CPU?
|
||||
|
@ -248,9 +247,7 @@ view `coverage/index.html` in a browser.
|
|||
0. Integrate into libgroove and test with Groove Basin
|
||||
0. look at microphone example and determine if fewer memcpys can be done
|
||||
with the audio data
|
||||
- pulseaudio has peek() drop() which sucks, but what if libsoundio lets you
|
||||
specify how much to peek() and if you don't peek all of it, save the
|
||||
unused to a buffer for you.
|
||||
- test that sending the frame count to begin read works with PulseAudio
|
||||
0. add len arguments to APIs that have char *
|
||||
0. Test in an app that needs to synchronize video to test the
|
||||
latency/synchronization API.
|
||||
|
|
|
@ -70,6 +70,11 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
}
|
||||
}
|
||||
|
||||
static void underflow_callback(struct SoundIoOutStream *outstream) {
|
||||
static int count = 0;
|
||||
fprintf(stderr, "underflow %d\n", count++);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *exe = argv[0];
|
||||
enum SoundIoBackend backend = SoundIoBackendNone;
|
||||
|
@ -109,6 +114,7 @@ int main(int argc, char **argv) {
|
|||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||
outstream->format = SoundIoFormatFloat32NE;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underflow_callback = underflow_callback;
|
||||
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
panic("unable to open device: %s", soundio_strerror(err));
|
||||
|
|
|
@ -1029,6 +1029,8 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
outstream->period_duration = clamp(device->period_duration_min,
|
||||
outstream->buffer_duration / 2.0, device->period_duration_max);
|
||||
}
|
||||
if (outstream->prebuf_duration == -1.0)
|
||||
outstream->prebuf_duration = outstream->buffer_duration;
|
||||
|
||||
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
||||
if (!osa) {
|
||||
|
@ -1144,7 +1146,8 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params_set_start_threshold(osa->handle, swparams, buffer_size_frames)) < 0) {
|
||||
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, prebuf_frames)) < 0) {
|
||||
outstream_destroy_alsa(si, os);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ struct SoundIoOutStreamDummy {
|
|||
atomic_flag abort_flag;
|
||||
int buffer_frame_count;
|
||||
struct SoundIoRingBuffer ring_buffer;
|
||||
int prebuf_frame_count;
|
||||
int prebuf_frames_left;
|
||||
long frames_consumed;
|
||||
double playback_start_time;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
@ -46,35 +50,47 @@ static void playback_thread_run(void *arg) {
|
|||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
long frames_consumed = 0;
|
||||
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 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;
|
||||
osd->prebuf_frames_left = osd->prebuf_frame_count - fill_frames;
|
||||
outstream->write_callback(outstream, free_frames);
|
||||
|
||||
while (osd->abort_flag.test_and_set()) {
|
||||
double now = soundio_os_get_time();
|
||||
double total_time = now - start_time;
|
||||
long total_frames = total_time * outstream->sample_rate;
|
||||
int frames_to_kill = total_frames - frames_consumed;
|
||||
int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
||||
int frames_in_buffer = fill_count / outstream->bytes_per_frame;
|
||||
int read_count = min(frames_to_kill, frames_in_buffer);
|
||||
int frames_left = frames_to_kill - read_count;
|
||||
int byte_count = read_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
|
||||
frames_consumed += read_count;
|
||||
|
||||
if (frames_left > 0) {
|
||||
outstream->underflow_callback(outstream);
|
||||
// TODO delete this and simulate prebuf
|
||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer,
|
||||
soundio_ring_buffer_free_count(&osd->ring_buffer));
|
||||
} else if (read_count > 0) {
|
||||
outstream->write_callback(outstream, read_count);
|
||||
}
|
||||
now = soundio_os_get_time();
|
||||
double time_passed = now - start_time;
|
||||
double next_period = start_time +
|
||||
ceil(time_passed / outstream->period_duration) * outstream->period_duration;
|
||||
double relative_time = next_period - now;
|
||||
soundio_os_cond_timed_wait(osd->cond, nullptr, relative_time);
|
||||
|
||||
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
||||
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;
|
||||
if (osd->prebuf_frames_left > 0) {
|
||||
outstream->write_callback(outstream, free_frames);
|
||||
continue;
|
||||
}
|
||||
|
||||
double total_time = soundio_os_get_time() - osd->playback_start_time;
|
||||
long total_frames = total_time * outstream->sample_rate;
|
||||
int frames_to_kill = total_frames - osd->frames_consumed;
|
||||
int read_count = min(frames_to_kill, fill_frames);
|
||||
int byte_count = read_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
|
||||
osd->frames_consumed += read_count;
|
||||
|
||||
if (frames_to_kill > read_count) {
|
||||
osd->prebuf_frames_left = osd->prebuf_frame_count;
|
||||
outstream->underflow_callback(outstream);
|
||||
} else if (free_frames > 0) {
|
||||
outstream->write_callback(outstream, free_frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,13 +109,12 @@ static void capture_thread_run(void *arg) {
|
|||
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 = frames_to_kill - write_count;
|
||||
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;
|
||||
|
||||
if (frames_left > 0)
|
||||
isd->hole_size += frames_left;
|
||||
isd->hole_size += frames_left;
|
||||
if (write_count > 0)
|
||||
instream->read_callback(instream, write_count);
|
||||
now = soundio_os_get_time();
|
||||
|
@ -194,6 +209,11 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
osd->buffer_frame_count = actual_capacity / outstream->bytes_per_frame;
|
||||
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();
|
||||
if (!osd->cond) {
|
||||
outstream_destroy_dummy(si, os);
|
||||
|
@ -225,11 +245,6 @@ static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStr
|
|||
}
|
||||
|
||||
static int outstream_start_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
// TODO delete this and simulate prebuf
|
||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, soundio_ring_buffer_free_count(&osd->ring_buffer));
|
||||
|
||||
return outstream_pause_dummy(si, os, false);
|
||||
}
|
||||
|
||||
|
@ -265,6 +280,13 @@ static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate
|
|||
SoundIoOutStream *outstream = &os->pub;
|
||||
int byte_count = frame_count * outstream->bytes_per_frame;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -696,14 +696,20 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
ospa->buffer_attr.minreq = UINT32_MAX;
|
||||
ospa->buffer_attr.fragsize = UINT32_MAX;
|
||||
|
||||
int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
|
||||
if (outstream->buffer_duration > 0.0) {
|
||||
int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
|
||||
int buffer_length = outstream->bytes_per_frame *
|
||||
ceil(outstream->buffer_duration * bytes_per_second / (double)outstream->bytes_per_frame);
|
||||
|
||||
ospa->buffer_attr.maxlength = 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);
|
||||
|
||||
|
@ -733,6 +739,8 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ospa->stream);
|
||||
outstream->buffer_duration = (attr->maxlength /
|
||||
(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);
|
||||
|
||||
|
@ -903,10 +911,6 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
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.maxlength = UINT32_MAX;
|
||||
ispa->buffer_attr.tlength = UINT32_MAX;
|
||||
ispa->buffer_attr.prebuf = UINT32_MAX;
|
||||
ispa->buffer_attr.minreq = UINT32_MAX;
|
||||
ispa->buffer_attr.fragsize = buffer_length;
|
||||
}
|
||||
|
||||
|
|
|
@ -351,6 +351,8 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device)
|
|||
outstream->error_callback = default_outstream_error_callback;
|
||||
outstream->underflow_callback = default_underflow_callback;
|
||||
|
||||
outstream->prebuf_duration = -1.0;
|
||||
|
||||
return outstream;
|
||||
}
|
||||
|
||||
|
|
|
@ -327,6 +327,11 @@ struct SoundIoOutStream {
|
|||
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
||||
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`.
|
||||
double prebuf_duration;
|
||||
|
||||
// Defaults to NULL. Put whatever you want here.
|
||||
void *userdata;
|
||||
// In this callback, you call `soundio_outstream_begin_write` and
|
||||
|
|
Loading…
Reference in a new issue