live.rs

crates/veilvoice-audio/src/live.rs

veilvoice-audio · 859 lines · read the source here · or on GitHub

Live microphone scrambling.

Structure

Capture and playback run as two independent callbacks driven by the audio hardware, joined by a lock-free SPSC ring buffer. The de-identification runs inside the output callback, which is the shortest path: adding a worker thread would mean a second buffer and a second scheduling delay for no benefit, and veilvoice_core::Deidentifier::process is explicitly allocation-free and safe to call from an audio callback.

Rules the callbacks follow

An audio callback that blocks produces a dropout, so neither callback ever allocates, locks, or waits. Statistics are published through a mutex the callback only ever tries to take: if the UI thread happens to hold it, the update is skipped rather than the audio stalling.

Latency

Total latency is the input buffer, plus the ring backlog, plus the engine's one-frame group delay (~21 ms at the defaults), plus the output buffer. The ring is intentionally short, enough to absorb jitter between two clocks that are not synchronised, not enough to accumulate a delay the user would notice while speaking.

In plain words

This is live mode: your microphone in one end, a voice that is not yours out the other, fast enough to hold a conversation.

Sound arrives from the microphone in small pieces, and each one has to be dealt with before the next arrives. There is no room to be late. So the veiling happens on the same short path the sound is already travelling, with nothing queued up behind it and nothing that could pause to allocate memory or wait for another part of the program.

If the computer ever cannot keep up, that is counted and shown rather than hidden. A gap in the sound you can see explained is far better than one you cannot.

WHAT THIS FILE CONTAINS

859 lines defining 12 functions (6 public), 7 types and 1 constant. Everything below is read out of the source, so it cannot disagree with the code.

The types it owns.

  • enum Side line 56 · Which side of the engine something happened to.
  • struct Interference line 86 · Something that happened to the audio path while it was running.
  • struct LiveStats line 104 · A snapshot of what the live path is doing, safe to read from the UI.
  • struct Keeping line 133 · Which sides of the engine a session keeps.
  • struct Kept line 156 · The recorders a session was asked for, one per side of Keeping.
  • struct LiveSession line 164 · A running live-scramble session.
  • struct Shared line 172

What happens when it runs. These are the ways in: public, and nothing else in this file calls them, so they are what an outside caller reaches first.

  • Side::word line 65 · The word for this side, as a person reading a warning would meet it.
  • Keeping::is_anything line 144 · Whether anything at all is being kept.
  • LiveSession::start line 410 · Start scrambling from input into output.
    reaches start_recording, agree_on_a_rate, agree_on_a_rate_for, at_rate, input_ranges, rate_they_agree_on
  • LiveSession::stats line 624 · Read the current statistics, resetting the peak meters.
  • LiveSession::interference line 643 · What the platform last reported about either stream.

WHAT CALLS WHAT

Side::word line 65 Keeping::is_anything line 144 Shared::report line 190 rate_they_agree_on line 223 input_ranges line 255 at_rate line 270 agree_on_a_rate line 301 agree_on_a_rate_for line 315 LiveSession::start line 410 LiveSession::start_recording line 452 LiveSession::stats line 624 LiveSession::interference line 643 entry: a way in: public, and nothing in this file calls it api: public, and also used inside this file helper: private to this file dashed: a call that goes back up, or across a wrapped rank The functions this file defines, and the calls between them. An edge means the callee's name appears, called, inside the caller's body. This is a syntactic reading, not a type-resolved one.

The functions this file defines, and the calls between them. An edge means the callee's name appears, called, inside the caller's body. This is a syntactic reading, not a type-resolved one.

The same graph as Mermaid source
%%{init: {"theme":"base","themeVariables":{"background":"#1a1b26","primaryColor":"#1f2335","primaryTextColor":"#c0caf5","primaryBorderColor":"#7aa2f7","secondaryColor":"#16161e","tertiaryColor":"#16161e","lineColor":"#737aa2","textColor":"#c0caf5","mainBkg":"#1f2335","nodeBorder":"#7aa2f7","clusterBkg":"#16161e","clusterBorder":"#2f3549","fontFamily":"ui-monospace, SFMono-Regular, Consolas, monospace","fontSize":"14px"}}}%%
flowchart TD
    n_word(["Side::word<br/>line 65"])
    n_is_anything(["Keeping::is_anything<br/>line 144"])
    n_report["Shared::report<br/>line 190"]
    n_rate_they_agree_on["rate_they_agree_on<br/>line 223"]
    n_input_ranges["input_ranges<br/>line 255"]
    n_at_rate["at_rate<br/>line 270"]
    n_agree_on_a_rate["agree_on_a_rate<br/>line 301"]
    n_agree_on_a_rate_for["agree_on_a_rate_for<br/>line 315"]
    n_start(["LiveSession::start<br/>line 410"])
    n_start_recording["LiveSession::start_recording<br/>line 452"]
    n_stats(["LiveSession::stats<br/>line 624"])
    n_interference(["LiveSession::interference<br/>line 643"])
    n_agree_on_a_rate --> n_agree_on_a_rate_for
    n_agree_on_a_rate_for --> n_at_rate
    n_agree_on_a_rate_for --> n_input_ranges
    n_agree_on_a_rate_for --> n_rate_they_agree_on
    n_start --> n_start_recording
    n_start_recording --> n_agree_on_a_rate
    click n_word href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L65" "open the source"
    click n_is_anything href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L144" "open the source"
    click n_report href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L190" "open the source"
    click n_rate_they_agree_on href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L223" "open the source"
    click n_input_ranges href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L255" "open the source"
    click n_at_rate href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L270" "open the source"
    click n_agree_on_a_rate href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L301" "open the source"
    click n_agree_on_a_rate_for href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L315" "open the source"
    click n_start href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L410" "open the source"
    click n_start_recording href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L452" "open the source"
    click n_stats href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L624" "open the source"
    click n_interference href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-audio/src/live.rs#L643" "open the source"
    classDef entry fill:#1f2335,stroke:#7aa2f7,color:#c0caf5
    class n_word,n_is_anything,n_start,n_stats,n_interference entry
    classDef api fill:#1f2335,stroke:#7dcfff,color:#c0caf5
    class n_start_recording api
    classDef helper fill:#1f2335,stroke:#bb9af7,color:#c0caf5
    class n_report,n_rate_they_agree_on,n_input_ranges,n_at_rate,n_agree_on_a_rate,n_agree_on_a_rate_for helper

This site loads no third-party script, so it cannot run Mermaid; the diagram above is the same nodes and edges drawn by the generator instead. GitHub renders the source below directly.

ITEMS

ItemLineDocumentation
RING_MILLIS pub(crate) const52How much jitter the ring absorbs before it starts dropping samples.
Side pub enum56Which side of the engine something happened to.
Side::word pub fn65The word for this side, as a person reading a warning would meet it.
Interference pub struct86Something that happened to the audio path while it was running.
LiveStats pub struct104A snapshot of what the live path is doing, safe to read from the UI.
Keeping pub struct133Which sides of the engine a session keeps.
Keeping::is_anything pub fn144Whether anything at all is being kept.
Kept pub struct156The recorders a session was asked for, one per side of Keeping.
LiveSession pub struct164A running live-scramble session.
Shared struct172
Shared::report fn190Record what the platform said about a stream.
rate_they_agree_on fn223Which sample rate a set of devices can all run at, if any.
input_ranges pub(crate) fn255The ranges a device reports, as plain numbers.
at_rate fn270One of a device's configurations at rate, preferring f32.
agree_on_a_rate fn301A microphone and an output, configured to one rate.
agree_on_a_rate_for pub(crate) fn315The same, for any number of microphones.
LiveSession::start pub fn410Start scrambling from input into output.
LiveSession::start_recording pub fn452Start scrambling, keeping the sides of it Keeping asks for.
LiveSession::stats pub fn624Read the current statistics, resetting the peak meters.
LiveSession::interference pub fn643What the platform last reported about either stream.