Skip to content
fix.fmaudio repair
FIX.FM / ABOUT / HOW WE TEST

How we test audio repair

Every check and every fix on this site is proven on real speech that we damaged on purpose, so we know the clean answer. This page shows the recordings, the recipes, the measurements, the pass marks, and the cases the fixes do not solve. Every number here is produced by a test that runs before each change ships.

The short version

Automated tests can prove three things: that a check flags a problem we built into a recording, that it stays quiet on a clean one, and that the fix moves the measurement back toward clean. They cannot prove that every recording in the world will read the same way. Where a check is not sure, it says so in the result instead of guessing, and the fix for that row is left off.

Your audio and the limits

Checks and fixes run in your browser in a Web Worker. Nothing is uploaded, which is also why the test suite can run the same code in Node: the Worker and the tests call the same functions on the same decoded samples. The privacy policy lists the little we do measure.

  • Fixer pages accept files up to 200 MB and 60 min long.
  • The audio editor loads up to 10 min per file and runs a selection repair on up to 30 seconds at a time.
  • Decoding uses the browser, so the formats that play in your browser are the ones that open.

Where the voices come from

Two clean readings from LibriVox Short Story Collection Vol. 107, which is in the public domain: 20 seconds from track 3 (Voice A) and 20 seconds from track 4 (Voice B), both 48 kHz, 16-bit, mono. Each sample on the site is a 10 second cut from one of them, degraded by one recipe, and shipped as the file you hear when you press Try a sample. Every damage below is added in software, so the clean original is always available for comparison. None of these is a real bad microphone or a real room, and we say so wherever a page uses them.

How each problem is made

SampleVoiceRecipe
echo.wavVoice BSchroeder reverb (four combs, two all-passes) with a 1.6 s RT60
noise.wavVoice BSteady pink noise mixed 10 dB under the speech
muffled.wavVoice AFourth-order low-pass at 3 kHz
distorted.wavVoice BGain +18 dB, then hard-clipped at full scale
quiet.wavVoice AGain -22 dB
one-ear.wavVoice BStereo file with the right channel all zeros
hum.wavVoice A60 Hz plus four harmonics falling at 1/k, 18 dB under the speech
wind.wavVoice BGusting brown-noise rumble below 150 Hz, 4 dB under the speech
quiet-hiss.wavVoice BWhite noise 15 dB under the speech, then gain -18 dB

The recipes live in lib/audio/fixtures/degrade.ts and recipes.ts. A test rebuilds each sample from its recipe and compares it byte for byte with the shipped file, so the file you hear is the file the numbers below were measured on.

What each check measures

Each check turns the recording into one number, then grades it mild, moderate or severe against fixed pass marks. The sentence you see first is written from the grade; the number sits next to it. These are the marks in use today.

CheckMeasureMildModerateSevere
Echos decay, higher is worse0.450.71.1
NoisedB SNR, lower is worse302012
MuffleddB highs, lower is worse-34-40-50
Distorted% clipped, higher is worse0.05%0.3%1.5%
Too quietLUFS, lower is worse-27-31-38
One eardB gap, higher is worse61020
HumdB hum, higher is worse-40-30-20
Wind% windy, higher is worse5%15%35%
  • Noise is speech level minus the level in the pauses, in dB. With no pauses to listen in, the check reports that it could not judge instead of a number.
  • Echo is how long the last 10 dB of each fall into a pause takes, in seconds. On synthetic rooms it tracks RT60 within about 15%.
  • Muffled is energy from 4 to 10 kHz relative to 300 Hz to 4 kHz, in dB. Clean read speech sits near -24 and some voices read down to about -32, which is why mild starts at -34.
  • Distorted is the share of audible samples sitting in flat-topped runs.
  • Too quiet is integrated loudness as the LUFS meter reports it.
  • One ear, hum and wind are a level gap between channels, hum power relative to loud speech, and the share of the recording with rumble on the microphone.

What counts as passing

Three rules, and the suite fails if any of them breaks:

  1. Each sample is flagged for exactly the problems its recipe adds. Flagging anything else on it is a failure, which keeps the checks from crying wolf on each other.
  2. Both clean readings pass all eight checks untouched, with nothing marked uncertain. Voice A reads 63.5 dB SNR, -23.6 dB highs and -25.1 LUFS; Voice B reads 65.7 dB, -23.6 dB and -24.7 LUFS.
  3. After the fix chain runs at full amount, every flagged problem reads fine. A fix marked beta only has to improve the grade.

The chain runs in a fixed order, and each step measures the audio it receives from the step before: one ear, distorted, hum, wind, noise, echo (beta), muffled, too quiet. Channels first, then the waveform, then narrow-band problems, then noise, room and tone, and level last so it reads the finished sound.

Before and after, today

SampleCheckBeforeAfter the fix
echo.wavEchosevere (1.4 s decay)fine (n/a)
noise.wavNoisemoderate (15.9 dB SNR)fine (75.2 dB SNR)
muffled.wavMuffledsevere (-54.7 dB highs)fine (-20.9 dB highs)
distorted.wavDistortedsevere (10.01 % clipped)fine (0 % clipped)
quiet.wavToo quietsevere (-47 LUFS)fine (-16.5 LUFS)
one-ear.wavOne earsevere (98.7 dB gap)fine (0 dB gap)
hum.wavHummoderate (-21.5 dB hum)fine (none found)
wind.wavWindsevere (50.9 % windy)fine (0 % windy)
quiet-hiss.wavNoisemoderate (17.6 dB SNR)fine (78.7 dB SNR)
quiet-hiss.wavToo quietsevere (-42.5 LUFS)fine (-16.2 LUFS)

Read the noise rows with care. 75.2 dB SNR after the fix is what steady synthetic noise allows: the noise never changes, so the estimate is nearly perfect. A real room with a fridge cycling, traffic and chair creaks will land far lower. The one-ear, hum and wind rows read none left because the fix removes the thing being measured rather than reducing it.

What the fixes do not solve

A page that only shows passes is not a test page, so here is the case we keep in the suite because it fails. Voice A low-passed at 1.5 kHz reads -78.7 dB highs. The muffled fixer brings it to -57.3 dB, still severe. Once the highs are gone, EQ can only lift what is left, and most of what is left above 4 kHz is noise. The 3 kHz sample on this site passes because there is still something there to lift.

  • Echo is beta. On the sample it clears, on real rooms with changing distance it can leave a tail, and the tool says beta on the row for that reason.
  • Clipping that has been through a lossy encoder loses its flat tops, so the share can read low on a file that still sounds distorted. Clipping vs limiting shows this with waveforms.
  • Uncertain findings stay at fine and the fix is not offered: noise with no pauses to listen in, echo masked by a high noise floor, and muffled on files below 16 kHz where there is no band to measure.
  • Level fixes read the whole file. Two speakers at different levels are not balanced by it, as this measured example shows.

Reproduce it

The repository is TypeScript with Vitest. pnpm test runs the whole suite, including the fixture tests that rebuild each sample and recompute every figure on this page from lib/audio/fixtures/how-we-test.test.ts. pnpm samples rewrites the sample files from their recipes. The listening examples in the learn articles are built the same way, each by its own fixture test that also checks the prose quotes the measured numbers.

The numbers on this page come from Node running the same TypeScript the browser Worker runs. The site is built on every change and the suite runs before merging. We check pages by hand in a current Chrome and do not publish a browser compatibility matrix we have not run.

Changes

  • October 5, 2026: first version of this page, with the pass marks and results of the current chain.