diff --git a/soundio/soundio.h b/soundio/soundio.h index 2f4994f..f3cb190 100644 --- a/soundio/soundio.h +++ b/soundio/soundio.h @@ -1004,9 +1004,12 @@ SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outst /// device does not support pausing/unpausing. SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); -/// Obtain the total number of seconds that the next frame you write to the -/// outstream will take to become audible. This includes both software and -/// hardware latency. +/// Obtain the total number of seconds that the next frame written after the +/// last frame written with ::soundio_outstream_end_write will take to become +/// audible. This includes both software and hardware latency. In other words, +/// if you call this function directly after calling ::soundio_outstream_end_write, +/// this gives you the number of seconds that the next frame written will take +/// to become audible. /// /// This function must be called only from within SoundIoOutStream::write_callback. SOUNDIO_EXPORT int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, diff --git a/src/jack.cpp b/src/jack.cpp index ed7c777..d085a3c 100644 --- a/src/jack.cpp +++ b/src/jack.cpp @@ -214,6 +214,7 @@ static int refresh_devices_bare(SoundIoPrivate *si) { djp->full_name = soundio_str_dupe(port->full_name, port->full_name_len); djp->full_name_len = port->full_name_len; djp->channel_id = port->channel_id; + djp->latency_range = port->latency_range; if (!djp->full_name) { jack_free(port_names); @@ -469,6 +470,8 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea jack_on_shutdown(osj->client, outstream_shutdown_callback, os); + jack_nframes_t max_port_latency = 0; + // register ports and map channels int connected_count = 0; for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { @@ -490,11 +493,13 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea osjp->dest_port_name = djp->full_name; osjp->dest_port_name_len = djp->full_name_len; connected_count += 1; + max_port_latency = max(max_port_latency, djp->latency_range.max); } } // If nothing got connected, channel layouts aren't working. Just send the // data in the order of the ports. if (connected_count == 0) { + max_port_latency = 0; outstream->layout_error = SoundIoErrorIncompatibleDevice; int ch_count = min(outstream->layout.channel_count, dj->port_count); @@ -503,9 +508,12 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea SoundIoDeviceJackPort *djp = &dj->ports[ch]; osjp->dest_port_name = djp->full_name; osjp->dest_port_name_len = djp->full_name_len; + max_port_latency = max(max_port_latency, djp->latency_range.max); } } + osj->hardware_latency = max_port_latency / (double)outstream->sample_rate; + return 0; } @@ -575,7 +583,9 @@ static int outstream_clear_buffer_jack(struct SoundIoPrivate *si, struct SoundIo static int outstream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, double *out_latency) { - soundio_panic("TODO"); + SoundIoOutStreamJack *osj = &os->backend_data.jack; + *out_latency = osj->hardware_latency; + return 0; } @@ -684,6 +694,8 @@ static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamP } jack_on_shutdown(isj->client, instream_shutdown_callback, is); + jack_nframes_t max_port_latency = 0; + // register ports and map channels int connected_count = 0; for (int ch = 0; ch < instream->layout.channel_count; ch += 1) { @@ -705,11 +717,13 @@ static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamP isjp->source_port_name = djp->full_name; isjp->source_port_name_len = djp->full_name_len; connected_count += 1; + max_port_latency = max(max_port_latency, djp->latency_range.max); } } // If nothing got connected, channel layouts aren't working. Just send the // data in the order of the ports. if (connected_count == 0) { + max_port_latency = 0; instream->layout_error = SoundIoErrorIncompatibleDevice; int ch_count = min(instream->layout.channel_count, dj->port_count); @@ -718,9 +732,12 @@ static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamP SoundIoDeviceJackPort *djp = &dj->ports[ch]; isjp->source_port_name = djp->full_name; isjp->source_port_name_len = djp->full_name_len; + max_port_latency = max(max_port_latency, djp->latency_range.max); } } + isj->hardware_latency = max_port_latency / (double)instream->sample_rate; + return 0; } @@ -780,7 +797,9 @@ static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStr static int instream_get_latency_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, double *out_latency) { - soundio_panic("TODO"); + SoundIoInStreamJack *isj = &is->backend_data.jack; + *out_latency = isj->hardware_latency; + return 0; } static void notify_devices_change(SoundIoPrivate *si) { diff --git a/src/jack.hpp b/src/jack.hpp index d535b41..13335ee 100644 --- a/src/jack.hpp +++ b/src/jack.hpp @@ -20,6 +20,7 @@ struct SoundIoDeviceJackPort { char *full_name; int full_name_len; SoundIoChannelId channel_id; + jack_latency_range_t latency_range; }; struct SoundIoDeviceJack { @@ -49,6 +50,7 @@ struct SoundIoOutStreamJack { int period_size; int frames_left; bool is_paused; + double hardware_latency; SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; @@ -63,6 +65,7 @@ struct SoundIoInStreamJack { jack_client_t *client; int period_size; int frames_left; + double hardware_latency; SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; char *buf_ptrs[SOUNDIO_MAX_CHANNELS]; diff --git a/test/latency.cpp b/test/latency.cpp index 36a1272..3019438 100644 --- a/test/latency.cpp +++ b/test/latency.cpp @@ -53,14 +53,14 @@ static double seconds_offset = 0.0; static SoundIoRingBuffer pulse_rb; -static void write_time(SoundIoOutStream *outstream) { +static void write_time(SoundIoOutStream *outstream, double extra) { 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 audible_time = now + latency + extra; 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)); @@ -92,7 +92,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m if (frames_until_pulse <= 0) { if (pulse_frames_left == -1) { pulse_frames_left = 0.25 * float_sample_rate; - write_time(outstream); // announce beep start + write_time(outstream, seconds_per_frame * frame); // announce beep start } if (pulse_frames_left > 0) { pulse_frames_left -= 1; @@ -101,7 +101,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m 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 + write_time(outstream, seconds_per_frame * frame); // announce beep end } } else { frames_until_pulse -= 1;