Implement STOI/ESTOI (Short-Time Objective Intelligibility) measure in Qlatt as a quality metric for synthesized speech.
- Taal et al. 2011 —
papers/Taal_2011_STOI/— Full STOI algorithm, all 8 equations, constants, logistic mapping. 12 pages read. - Jensen & Taal 2016 —
papers/Jensen_2016_ESTOI/— ESTOI extension, row-column normalization, spectral correlation. 14 pages read.
- pystoi at
~/src/pystoi— full report atreports/stoi-scout-pystoi.md - ~200 lines Python total
- Key insight: OBM matrix is static (15x257 binary), can be hardcoded
- Key insight: resampling can be avoided by using WebAudio OfflineAudioContext
- Key insight: FFT is the only DSP primitive needed beyond basic linear algebra
- Resample both signals to 10 kHz
- Remove silent frames (40 dB dynamic range threshold, based on clean signal only)
- STFT: 256-sample Hann window, 50% overlap, 512-point FFT
- Apply 15-band 1/3 octave matrix (150 Hz to ~4.3 kHz) → (15, T) envelopes
- Extract N=30 frame segments (384 ms) → (S, 15, 30)
- Energy-normalize y segments to match x (per-band, per-segment)
- Clip to SDR floor: β = -15 dB
- Compute Pearson correlation per band per segment
- Average → scalar d
- Row normalize (per-band: zero-mean, unit-variance) both x and y
- Column normalize (per-frame: zero-mean, unit-variance) both x and y
- Inner product of normalized columns, average → scalar d
| Constant | Value |
|---|---|
| FS | 10000 Hz |
| N_FRAME | 256 samples |
| NFFT | 512 |
| NUMBAND | 15 |
| MINFREQ | 150 Hz |
| N | 30 frames (~384 ms) |
| BETA | -15 dB |
| DYN_RANGE | 40 dB |
- FFT library (fft.js or similar) for rfft
- Hanning window (trivial to implement)
- Matrix multiply, norms, means (manual on Float64Array)
- Resampling: either port Octave-compatible Kaiser filter OR use OfflineAudioContext
- Papers processed (Taal 2011, Jensen 2016)
- Reference implementation scouted (pystoi)
- Implementation plan drafted
- Core module implemented (
src/metrics/stoi.ts) - CLI tool implemented (
scripts/stoi-eval.ts) - Unit tests implemented (
test/metrics/stoi.test.ts) — 22/22 passing - package.json updated (fft.js dep, stoi script)
- tsconfig files updated (core + scripts)
- Typecheck clean (no new errors; pre-existing engine.ts error unrelated)
- Full test suite: no regressions introduced