Skip to content
Sign in

/docs · VoiceLab · Deep

What envelope decay after speech actually measures

We built a cheap reverb proxy from the decay after each phrase, then tested it against 24 measured rooms. It does not measure reverb. Here are the numbers and what we changed.

Updated Sep 27, 2026


RT60 wants you to inject a known stimulus into a room. You cannot do that on a recording someone already made, so for voice QA we built a cheap proxy instead: measure how much energy is left just after each phrase ends, relative to the phrase itself. A live room rings on, a treated one does not.

On 27 September 2026 we tested that proxy against rooms whose reverberation time had actually been measured. It failed. This page is the measurement, the test, and what we changed, because a negative result you can reproduce is worth more than a threshold table that reads well.

The measurement

Detect the speech-to-silence transitions with a simple energy gate, then read the envelope in the tail after each one against the level at the transition.

function decayRatio(envelope, mask, hopSec) {
  const decays = [];
  for (let i = 1; i < mask.length - 30; i++) {
    if (mask[i - 1] && !mask[i]) {
      const peak = envelope[i - 1];
      if (peak < 1e-5) continue;
      const window = Math.min(30, Math.floor(0.6 / hopSec));
      let tail = 0;
      for (let k = 0; k < window; k++) tail += envelope[i + k];
      tail /= window;
      decays.push(tail / peak);
    }
  }
  return decays.reduce((a, b) => a + b, 0) / decays.length;
}

Nothing is wrong with the code. The problem is what the number was claimed to mean.

What we tested it against

  • Real rooms, no added noise. Five clean sentences through 24 measured impulse responses, with reverberation times from 0.18 s to 1.71 s.
  • Noise with and without reverb. 1280 recordings, four environments, signal-to-noise ratios of 20, 10, 5 and 0 dB, each with and without 0.5 s of reverb.
  • A rebuild that matched the engine to 0.0000 on all 1760 files, so the results apply to the code that ships and not to an approximation of it.

What the numbers said

QuestionRequirementResult
Does it track the measured RT60 of real rooms? (Spearman)0.70 or better0.06
Does it tell reverb from dry at 20 dB SNR? (AUC)0.75 or better0.52
Same question at 10 dB SNR0.75 or better0.55
Is it unmoved by noise alone? (AUC)between 0.35 and 0.650.68

An AUC of 0.5 is a coin flip. The clearest single number: a room with 1.71 s of reverberation scored 0.175, and a booth with 0.18 s scored 0.186. The reverberant room scored lower than the booth.

The one thing the measurement does do reliably is rise with background noise, which is the opposite of useful when the label says “room”.

Why it fails

Three reasons, each fixable on its own and not enough together:

  1. The noise floor is the 10th percentile of the whole file. When the background varies, the noise inside a pause sits well above that percentile, and the difference gets counted as a tail.
  2. The tail is not checked for being inside the pause. The window runs from 60 to 420 ms after the phrase ends, and after short gaps are closed up, many pauses are shorter than that. The measurement then reads the start of the next word.
  3. The scale is wrong even without those two. A real RT60 of 0.5 s works out to a ratio of about 0.09 in theory, which is below the 0.15 boundary that used to mean “Dry”. Moderate reverberation was always going to be called dry.

With a 20 ms step and the jumps of speech itself, the decay after a phrase is too coarse to read reverberation out of. That is a research question, not a small repair.

What changed on 27 September 2026

  • The Dry / Tight / Live / Reverberant label is gone from the engine output, not set to a default. A field that is always empty is not a measurement to ship.
  • The plain-language card advising you to treat your room is gone, and so is the penalty this number applied to the intelligibility score. That penalty was charging a noisy recording twice.
  • The number stays, under the name of what it measures: energy in the pauses after speech, noise or reverb, and it does not separate the two. Read it as “how much is left in the gaps”, never as a verdict on the room.
  • In the API and the MCP server the field is still called roomEchoScore. Renaming a published field breaks saved history and existing integrations, so that is a separate decision rather than part of this correction. The description says what it measures.

What would actually answer the question

If you need a reverberation figure from a recording alone, the honest candidates are a modulation-spectrum measure such as SRMR, or a small model trained on measured impulse responses. Both are testable against the same 24 rooms.

The rule we set for ourselves: a new measure may only be called reverb once it passes that test. Until then it gets a name that describes the arithmetic.

Next in VoiceLab docs

Measure how long a room rings with one clap →

The room profile reads the reverberation time per octave band from one clap, a balloon pop, an impulse response or a sweep. How to record it, how to read the answer, and how far the numbers are tested.

More in VoiceLab docs