diff --git a/src/alsa.cpp b/src/alsa.cpp index a0285e0..ec9e84b 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -1468,7 +1468,9 @@ static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStre return 0; } -static int outstream_get_latency_alsa(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { +static int outstream_get_latency_alsa(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, + double *out_latency) +{ soundio_panic("TODO"); } @@ -1742,7 +1744,9 @@ static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStream return 0; } -static int instream_get_latency_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { +static int instream_get_latency_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, + double *out_latency) +{ soundio_panic("TODO"); } diff --git a/src/coreaudio.cpp b/src/coreaudio.cpp index e13850a..d89c06c 100644 --- a/src/coreaudio.cpp +++ b/src/coreaudio.cpp @@ -1052,7 +1052,9 @@ static int outstream_clear_buffer_ca(struct SoundIoPrivate *si, struct SoundIoOu return SoundIoErrorIncompatibleBackend; } -static int outstream_get_latency_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { +static int outstream_get_latency_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, + double *out_latency) +{ soundio_panic("TODO"); } @@ -1308,7 +1310,9 @@ static int instream_end_read_ca(struct SoundIoPrivate *si, struct SoundIoInStrea return 0; } -static int instream_get_latency_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { +static int instream_get_latency_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, + double *out_latency) +{ soundio_panic("TODO"); } diff --git a/src/dummy.cpp b/src/dummy.cpp index 11e8ac1..5ffd6f8 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -253,8 +253,13 @@ static int outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPriv return 0; } -static int outstream_get_latency_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { - soundio_panic("TODO"); +static int outstream_get_latency_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, double *out_latency) { + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamDummy *osd = &os->backend_data.dummy; + int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer); + + *out_latency = (fill_bytes / outstream->bytes_per_frame) / (double)outstream->sample_rate; + return 0; } static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { @@ -361,8 +366,13 @@ static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *i return 0; } -static int instream_get_latency_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { - soundio_panic("TODO"); +static int instream_get_latency_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, double *out_latency) { + SoundIoInStream *instream = &is->pub; + SoundIoInStreamDummy *osd = &is->backend_data.dummy; + int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer); + + *out_latency = (fill_bytes / instream->bytes_per_frame) / (double)instream->sample_rate; + return 0; } static int set_all_device_formats(SoundIoDevice *device) { diff --git a/src/jack.cpp b/src/jack.cpp index a08d0a2..ed7c777 100644 --- a/src/jack.cpp +++ b/src/jack.cpp @@ -572,7 +572,9 @@ static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIo return SoundIoErrorIncompatibleBackend; } -static int outstream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { +static int outstream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, + double *out_latency) +{ soundio_panic("TODO"); } @@ -775,7 +777,9 @@ static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStr return 0; } -static int instream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { +static int instream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, + double *out_latency) +{ soundio_panic("TODO"); } diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index 7f2a7eb..32ec2a0 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -809,7 +809,7 @@ static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, b return 0; } -static int outstream_get_latency_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { +static int outstream_get_latency_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, double *out_latency) { soundio_panic("TODO"); } @@ -1015,7 +1015,7 @@ static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, boo return 0; } -static int instream_get_latency_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { +static int instream_get_latency_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, double *out_latency) { soundio_panic("TODO"); } diff --git a/src/soundio.cpp b/src/soundio.cpp index 4b6491d..a1fcc20 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -537,11 +537,11 @@ int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream) { return si->outstream_clear_buffer(si, os); } -int soundio_outstream_get_latency(struct SoundIoOutStream *outstream) { +int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, double *out_latency) { SoundIo *soundio = outstream->device->soundio; SoundIoPrivate *si = (SoundIoPrivate *)soundio; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; - return si->outstream_get_latency(si, os); + return si->outstream_get_latency(si, os, out_latency); } static void default_instream_error_callback(struct SoundIoInStream *is, int err) { @@ -653,7 +653,7 @@ int soundio_instream_get_latency(struct SoundIoInStream *instream, double *out_l SoundIo *soundio = instream->device->soundio; SoundIoPrivate *si = (SoundIoPrivate *)soundio; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream; - return si->instream_get_latency(si, is); + return si->instream_get_latency(si, is, out_latency); } void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { diff --git a/src/soundio.hpp b/src/soundio.hpp index 42cc5e7..5751270 100644 --- a/src/soundio.hpp +++ b/src/soundio.hpp @@ -147,7 +147,7 @@ struct SoundIoPrivate { int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause); - int (*outstream_get_latency)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); + int (*outstream_get_latency)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, double *out_latency); int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); @@ -157,7 +157,7 @@ struct SoundIoPrivate { SoundIoChannelArea **out_areas, int *out_frame_count); int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause); - int (*instream_get_latency)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); + int (*instream_get_latency)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, double *out_latency); SoundIoBackendData backend_data; }; diff --git a/src/wasapi.cpp b/src/wasapi.cpp index 7b96f1b..32bbf00 100644 --- a/src/wasapi.cpp +++ b/src/wasapi.cpp @@ -1523,7 +1523,9 @@ static int outstream_clear_buffer_wasapi(struct SoundIoPrivate *si, struct Sound return 0; } -static int outstream_get_latency_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { +static int outstream_get_latency_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, + double *out_latency) +{ soundio_panic("TODO"); } @@ -1924,7 +1926,9 @@ static int instream_end_read_wasapi(struct SoundIoPrivate *si, struct SoundIoInS return 0; } -static int instream_get_latency_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { +static int instream_get_latency_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, + double *out_latency) +{ soundio_panic("TODO"); } diff --git a/test/latency.cpp b/test/latency.cpp index bd90e7d..6627234 100644 --- a/test/latency.cpp +++ b/test/latency.cpp @@ -47,12 +47,25 @@ static void write_sample_float64ne(char *ptr, double sample) { static void (*write_sample)(char *ptr, double sample); static int frames_until_pulse = 0; -static int pulse_frames_left = 0; +static int pulse_frames_left = -1; static const double PI = 3.14159265358979323846264338328; static double seconds_offset = 0.0; static SoundIoRingBuffer pulse_rb; +static void write_time(SoundIoOutStream *outstream) { + double latency; + int err; + if ((err = soundio_outstream_get_latency(outstream, &latency))) { + soundio_panic("getting latency: %s", soundio_strerror(err)); + } + double now = soundio_os_get_time(); + double audible_time = now + latency; + double *write_ptr = (double *)soundio_ring_buffer_write_ptr(&pulse_rb); + *write_ptr = audible_time; + soundio_ring_buffer_advance_write_ptr(&pulse_rb, sizeof(double)); +} + static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) { double float_sample_rate = outstream->sample_rate; double seconds_per_frame = 1.0f / float_sample_rate; @@ -77,13 +90,18 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m for (int frame = 0; frame < frame_count; frame += 1) { double sample; if (frames_until_pulse <= 0) { - if (pulse_frames_left <= 0) { - frames_until_pulse = (1.0 + (rand() / (double)RAND_MAX) * 3.0) * float_sample_rate; - pulse_frames_left = 0.05 * float_sample_rate; - sample = 0.0; - } else { + if (pulse_frames_left == -1) { + pulse_frames_left = 0.25 * float_sample_rate; + write_time(outstream); // announce beep start + } + if (pulse_frames_left > 0) { pulse_frames_left -= 1; sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second); + } else { + frames_until_pulse = (0.5 + (rand() / (double)RAND_MAX) * 2.0) * float_sample_rate; + pulse_frames_left = -1; + sample = 0.0; + write_time(outstream); // announce beep end } } else { frames_until_pulse -= 1; @@ -105,8 +123,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m } static void underflow_callback(struct SoundIoOutStream *outstream) { - static int count = 0; - fprintf(stderr, "underflow %d\n", count++); + soundio_panic("underflow\n"); } int main(int argc, char **argv) { @@ -186,6 +203,9 @@ int main(int argc, char **argv) { soundio_panic("No suitable device format available.\n"); } + if ((err = soundio_ring_buffer_init(&pulse_rb, 1024))) + soundio_panic("ring buffer init: %s", soundio_strerror(err)); + if ((err = soundio_outstream_open(outstream))) soundio_panic("unable to open device: %s", soundio_strerror(err)); @@ -195,8 +215,25 @@ int main(int argc, char **argv) { if ((err = soundio_outstream_start(outstream))) soundio_panic("unable to start device: %s", soundio_strerror(err)); - for (;;) - soundio_wait_events(soundio); + bool beep_on = true; + for (;;) { + int fill_count = soundio_ring_buffer_fill_count(&pulse_rb); + if (fill_count >= (int)sizeof(double)) { + double *read_ptr = (double *)soundio_ring_buffer_read_ptr(&pulse_rb); + double audible_time = *read_ptr; + while (audible_time > soundio_os_get_time()) { + // Burn the CPU while we wait for our precisely timed event. + } + if (beep_on) { + fprintf(stderr, "BEEP!\r"); + } else { + fprintf(stderr, " \r"); + } + fflush(stderr); + beep_on = !beep_on; + soundio_ring_buffer_advance_read_ptr(&pulse_rb, sizeof(double)); + } + } soundio_outstream_destroy(outstream); soundio_device_unref(device);