Sonir/Features & the math

What we measure, and how.

FEATURES & MATH

We line up the very graphs that appear on Sonir’s measurement screens and explain what each one measures and how it’s computed. We’ve opened up the defining formulas and the calculation procedures as far as we can publish them. Not making it a black box is, we believe, what earns trust in a measurement tool.

A

Measurement

MEASURE

Core measurement you can run instantly with the built-in mic. All of this is open for free.

72.3 dB SPL · A · FAST

measure › SPL — the 270° arc gauge in viz/spl_arc.dart

SPL (sound pressure level)

Free
Shows loudness in dB. Weighting supports A / C / Z (flat), and time weighting supports Fast (125 ms) / Slow (1 s) / Impulse (35 ms). It also shows Leq, peak, and minimum at the same time. Before calibration it reads in dBFS; load a calibration file and it’s promoted to dB SPL.
Open the math
Lp Lp = 20·log10(p/p0),   p0 = 20 µPa
Procedure: take the RMS (root mean square) of the input waveform over a time window, then convert to dB with the formula above. A/C/Z weighting is implemented as a biquad cascade obtained by bilinear-transforming the analog poles of IEC 61672-1 (spl.rs). Time weighting is smoothed by the time constants (Fast/Slow/Impulse) of an exponential moving average. Conversion to dB SPL is just adding the calibration offset offset_db — to avoid exaggerated absolute values on uncalibrated devices, it stays in dBFS until calibrated.
201001k10k20k

measure › RTA — viz/fft_bars.dart (1/12-oct bins)

RTA / FFT spectrum

Free
Shows energy per frequency in real time. Window functions are Hann (default) / Hamming / Blackman, and band resolution switches between 1/3, 1/6, and 1/12 octave. Lows are tinted with the accent color and highs are darker for readability.
Open the math
X(k) X(k) = Σn=0N−1 w(n)·x(n)·e−j2πkn/N    20·log10|X(k)|
Procedure: apply the window w(n) to the input block (real-time 1024–4096 points) and take a real FFT (realfft). Convert each bin’s magnitude to dB and aggregate energy into fractional-octave bands for the bar display. The window is a trade-off between resolution (narrow main lobe) and leakage (low side lobes) — the default Hann balances the two. IR analysis uses long FFTs of 65536 points or more.
0-20-40-60 Direct sound
0ms150300450ms

measure › FR — ETC (energy-time curve). Vertical line = direct sound, the rest is reflections and reverberation

Sweep → impulse response → room acoustics

Free
Play and record a log sweep (Farina ESS), then obtain the room’s impulse response (IR) by inverse-filter convolution. From it we compute RT60 / EDT / C50 / D50 / frequency response / ETC all at once. Full-band (default 20 Hz–20 kHz) broadband analysis is free.
Open the math
ESS x(t) = sin( 2π·f1·T/ln R · [ Rt/T − 1 ] ),   R = f2/f1
Schroeder L(t) = 10·log10( ∫t h2(τ) dτ  /  ∫0 h2(τ) dτ )
C50 C50 = 10·log10( ∫050ms h2 dt  /  ∫50ms h2 dt )    D50 = ∫050ms h2 dt /0 h2 dt
Obtaining the IR: generate a Farina-method log sweep → sync playback and recording → correct latency via FFT cross-correlation of the recording and the reference chirp → convolve the inverse filter (time reversal + amplitude correction) to extract the IR (ir.rs). The graph above is that IR’s ETC (energy-time curve), showing the time structure of direct sound, reflections, and reverberation.
RT60: backward-integrate the squared IR into a Schroeder decay curve, truncate the noise tail, and fit a line. Extrapolate the slope of −5 dB to −35 dB (T30) or −5 to −25 dB (T20) out to 60 dB. EDT is the 0 to −10 dB slope × 6.
C50 / D50: take the energy ratio around 50 ms (from the peak, within the truncation) for clarity / definition. Bands with insufficient SNR or regression r² are flagged with a confidence warning.
0 t↓
201001k10k20k

measure › FR — CSD heatmap (X = frequency / Y = time ↓ / color = dB)

Waterfall (CSD) / ETC

Free
Slice the same IR along time to show “which frequencies’ reverberation lingers, and for how long” (cumulative spectral decay / CSD). Horizontal axis = frequency, vertical axis = time (0 at the top, progressing downward), color = energy (dB). Low-frequency modes and boxy ringing show up at a glance.
Open the math
CSD S(f, tk) = | FFT[ h(τ)·w(τ − tk) ] |  ,  tk = k·Δt
Procedure: shift a time window w gradually backward over the IR and FFT each time (short-time FFT). Stack each instant’s spectrum into depth to render. The further the window advances, the more the quickly-decaying components vanish, leaving only the lingering bands (standing waves, box ringing) trailing into the distance. ETC is the IR envelope |h(t)| shown in dB, used to read the arrival times of early reflections.
B

Recording and comparison

RECORD & COMPARE

Sonir’s core: aligning recordings to “the same conditions” and comparing them.

201001k10k20k
Rec A · Built-in mic Rec B · UMIK-2 calibrated

compare — two frequency responses overlaid at a 0 dB reference

Comparison + overlaid spectra

Pro
Level-match two recordings, switch instantly, and overlay the spectra to visualize the difference. The key is to remove the biggest illusion — a level difference — before comparing. Align the metadata of gear profile, room tags, and calibration file so you can line them up under “the same conditions” even later.
Open the math
level g = RMSref / RMSx    x′(n) = g·x(n)
Procedure: find the gain g that matches each recording’s RMS to the reference and align them (eliminating the equal-loudness illusion where “the louder one sounds better”). Switching crossfades with an equal-power ramp to avoid abrupt clicks. Spectra are overlaid on the same 1/12-oct bins, showing A in blue and B in orange to reveal the difference.
87 OVERALL
Clipping 98
Noise floor 86
Dynamic range 74
Freq. balance 90

record › score — ScoreRing (circular ring) + the breakdown of 4 axes

Recording score

Free
Score a single recording across four axes — clipping, noise floor, dynamic range, and frequency balance — and aggregate into a 0–100 score. Generate a comparison card you can post straight to social. The thresholds of each axis are public, and the weights are adjustable in settings.
Open the math
score Score = Σi wi·si,   si = clamp( linearly map each axis to 0–100 )
Definitions of the 4 axes (score.rs, thresholds adjustable):
  • Clipping: ratio of samples pinned at 0 dBFS. 100 at 0%, 0 at 1%.
  • Noise floor: A-weighted RMS of silent sections. 100 at −70 dBFS, 0 at −30 dBFS.
  • Dynamic range: DR Database method (peak − RMS of the top 20%). 100 at 14 dB, 0 at 4 dB.
  • Frequency balance: RMS deviation from a linear fit of 1/3-oct bands. 100 at 3 dB, 0 at 12 dB (the genre’s tilt is absorbed by the fit).
The weights wi are equal by default (0.25 × 4) and can be changed on the settings page.
0-30-60-90-120
Effective BW 38 kHz
201001k10k40k

inspect — max-hold spectrum + effective-bandwidth marker

File inspection (fake hi-res / lossy detection)

Free
Analyzes an audio file on its own (independent of mic, room, or speaker). The essence is a “contradiction between the declaration (container sample rate) and reality (the cutoff of the PCM spectrum).” If the effective bandwidth falls well below the declared Nyquist, fake hi-res is suspected; if there’s a steep wall at a standard fs, a lossy origin is suspected.
Open the math
bandwidth feff = max f  s.t.  Smaxhold(f) ≥ Speak − 60dB
Procedure (inspect.rs): native code decodes the file to a temporary WAV at native fs, and Rust does a streaming read (not loading large PCM onto the boundary). Build a max-hold spectrum taking each bin’s maximum with a Hann window at 50% overlap (so instantaneous HF from cymbals and the like isn’t crushed by averaging). Treat −60 dB from the peak as the “floor,” and set the highest frequency exceeding it as the effective bandwidth feff.
  • Fake hi-res: declared above 48k but feff stops at the CD band (~26 kHz) → upsampling suspected.
  • Lossy origin: high feff/Nyquist ratio with a steep ≥48 dB/oct brick wall at 16–20 kHz.
Thresholds are provisional, being calibrated against real-device samples.
C

Finishing and export

FINISH & EXPORT

Apply what you measured to your environment, and share it.

+60-6
2050100200300Hz
Measured Target Corrected

measure › FR/EQ — measured vs target + the corrected curve

Room EQ / auto PEQ

Pro
Overlay a calibrated measurement with a target curve and automatically propose a parametric EQ that minimizes the difference. The main battlefield is the low end where modes run wild (default 20–300 Hz). Measurement and the PEQ preview are free; exporting in REW / EqualizerAPO format is Pro. A hard gate requires a calibrated measurement.
Open the math
error error(f) = measured(f) − target(f)    min Σ |error(f)|2
peaking H(s) = ( s2 + s·(A/Q)·ω0 + ω02 ) / ( s2 + s·(1/(A·Q))·ω0 + ω02 )
Procedure (roomeq.rs): a greedy method (matching pursuit) that places a peaking biquad one at a time wherever the residual error(f) is largest. The biquad coefficients implement the RBJ Audio EQ Cookbook peaking EQ directly. It prefers cuts over boosts, clamps the maximum boost and cut separately to protect headroom, and auto-computes a negative preamp to cancel out the excess boost. Correction is limited to the modal region below the Schroeder frequency. The green curve in the graph above is the predicted curve after correction.

On transparency

The formulas and procedures shown here correspond to the actual implementation of the app’s DSP core (Rust). Some thresholds are provisional values being calibrated against real-device samples, and will be updated once finalized. Measurement completes on-device without going through a server; recordings, IRs, and calibration files are never sent outside.

CLOSING

Ready when you are.

Launch is in preparation. Bring reproducibility from your very first measurement.

iOS / Android — Coming soon