Skip to main content

RoundTripLatencySession

Struct RoundTripLatencySession 

Source
pub struct RoundTripLatencySession;
Expand description

Self-contained round-trip latency measurement session.

RoundTripLatencySession has no fields; it acts as a namespace for the run function. All state lives on the stack inside that call, making the session automatically torn down when it returns — there is nothing to clean up manually.

§Thread safety

run is a blocking call designed to execute on a dedicated thread. The caller (measure_round_trip_latency Tauri command) clones the handler reference, releases the Mutex<AudioService> lock, and then spawns a thread that calls this function. This means the main audio engine remains fully operational during the measurement.

Implementations§

Source§

impl RoundTripLatencySession

Source

pub fn run( handler: &dyn AudioHandlerTrait, per_impulse_timeout: Duration, stream_warmup: Duration, ) -> Result<f64, String>

Runs a complete round-trip latency measurement and returns the average in milliseconds.

§What this function does
  1. Determines a safe ring-buffer size from the handler’s configured buffer frames (falling back to 256 if BufferSize::Default is in use), then multiplies by 4 to give the streams room to breathe during warmup and calibration.
  2. Creates a dedicated input ring buffer (i_produceri_consumer) and a dedicated output ring buffer (o_producero_consumer), both completely separate from the main loopback ring buffers.
  3. Opens a CPAL input stream that pushes captured samples into i_producer and a CPAL output stream that drains processed samples from o_consumer, then starts both.
  4. Sleeps for stream_warmup to let the OS audio scheduler and hardware settle.
  5. Drains all samples accumulated during warmup from i_consumer so that calibration begins with fresh, stable ambient data.
  6. Enters the main sample-processing loop, feeding each incoming sample to RoundTripMeasurementState::tick until a terminal outcome is reached or the overall_deadline expires.

The overall_deadline is set to per_impulse_timeout × IMPULSE_COUNT + 2 s to account for calibration time and inter-impulse gaps while still guaranteeing the function cannot block indefinitely.

§Arguments
  • handler — Audio I/O factory. Used only to size ring buffers and build streams; it is not the same handler instance that the main loopback uses concurrently.
  • per_impulse_timeout — Maximum time to wait for a single echo after the impulse is emitted. Recommended: 10 s for real hardware, shorter for unit tests.
  • stream_warmup — How long to sleep after starting streams before beginning calibration. Recommended: 1–2 s to allow ASIO/WASAPI buffers to stabilise.
§Returns
  • Ok(latency_ms) — Averaged round-trip latency across all IMPULSE_COUNT cycles.
  • Err(message) — Human-readable failure reason; either a timeout, an undetectable echo (signal too quiet or output not routed to input), or an overall deadline breach.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Any for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

§

fn type_name(&self) -> &'static str

§

impl<T> AnySync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<S> FromSample<S> for S

§

fn from_sample_(s: S) -> S

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,