meter.rs

crates/veilvoice-cli/src/meter.rs

veilvoice-cli · 259 lines · read the source here · or on GitHub

Level meters for veilvoice live, on a scale that means something.

Why the old one was wrong

The first meter was linear: a peak of 0.5 filled half the bar. That is arithmetically fine and useless as a meter, because loudness is not linear. Ordinary speech recorded at a sensible level peaks around -12 dBFS, which is 0.25 linear, so three of twelve blocks. Somebody speaking normally saw a meter that looked like near-silence, and the only way to fill the bar was to be clipping.

Every real meter is logarithmic for that reason, and this one is too: -60 dBFS at the left, 0 dBFS at the right. Speech now sits in the middle of the bar where a person can see it move.

What it measures, and what it does not

Sample peak, since the last read. veilvoice-audio keeps the largest absolute sample seen since the meter was last looked at and resets it on read, so nothing between two reads is missed.

It is not a loudness meter. RMS, LUFS and everything else that correlates with how loud a thing sounds need a window and a weighting curve, and they answer a different question: this one is for "am I being recorded, and am I clipping", which is a peak question.

It also cannot see an inter-sample peak, a waveform that passes above full scale between two samples and clips in a converter or an encoder without any single sample exceeding 1.0. Catching those needs oversampling. The meter says CLIP when a sample actually reaches full scale, and says nothing about the ones it cannot see, which is the honest half of a true peak meter rather than a claim to be one.

Peak hold

A bar that only shows the current moment cannot show a transient: the loud syllable is gone before a human eye finishes moving. The highest level of the last HOLD is kept and drawn as a single marker, and it decays rather than sticking, so the bar stays honest about what is happening now while still showing what just happened.

In plain words

Draws the input and output level meters during a live session.

The bar and the decibel number beside it are worked out from the same piece of arithmetic the window uses, so the two halves of VeilVoice cannot disagree about the same reading. They did once, and a meter you have caught contradicting itself is a meter you stop believing.

WHAT THIS FILE CONTAINS

259 lines defining 4 functions (3 public), 1 type and 2 constants. Everything below is read out of the source, so it cannot disagree with the code.

The types it owns.

  • struct Channel line 129 · One channel's meter, keeping the peak between reads.

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.

  • Channel::update line 147 · Take a new reading and give back the meter to print.
    reaches render
  • Channel::has_clipped line 168 · Whether this channel has clipped at any point in the session.

WHAT CALLS WHAT

render line 74 Channel::default line 136 Channel::update line 147 Channel::has_clipped line 168 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_render["render<br/>line 74"]
    n_default["Channel::default<br/>line 136"]
    n_update(["Channel::update<br/>line 147"])
    n_has_clipped(["Channel::has_clipped<br/>line 168"])
    n_update --> n_render
    click n_render href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-cli/src/meter.rs#L74" "open the source"
    click n_default href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-cli/src/meter.rs#L136" "open the source"
    click n_update href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-cli/src/meter.rs#L147" "open the source"
    click n_has_clipped href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-cli/src/meter.rs#L168" "open the source"
    classDef entry fill:#1f2335,stroke:#7aa2f7,color:#c0caf5
    class n_update,n_has_clipped entry
    classDef api fill:#1f2335,stroke:#7dcfff,color:#c0caf5
    class n_render api
    classDef helper fill:#1f2335,stroke:#bb9af7,color:#c0caf5
    class n_default 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
HOLD pub const62How long a peak marker is held before it falls back.
EIGHTHS const69The eighth-block characters, so a bar of n characters has 8n steps.
render pub fn74One meter: the bar, the peak marker, and the number.
Channel pub struct129One channel's meter, keeping the peak between reads.
Channel::default fn136
Channel::update pub fn147Take a new reading and give back the meter to print.
Channel::has_clipped pub fn168Whether this channel has clipped at any point in the session.