MASMATIXInnovations
← All insights

Experiment · Speech deepfake detection

My fake-voice detector called my own WhatsApp voice note AI-generated. What it gets right on phone audio, and what it does not

I built a detector for AI-generated voices, tested it on simulated phone lines, and put it in the browser. The benchmark numbers looked good. My own voice notes told a different story.

By Muzammal Hussain · 9 November 2026 · 9 min read

Voice cloning now takes a few seconds of someone's speech, and the calls that use it arrive over phone lines and messaging apps, not in studio quality. VoiceGuard is my research project on detecting fake voices in exactly those conditions. This first stage uses one well-known dataset, a self-supervised speech model and a small classifier, and asks two questions: does the detector survive a phone line, and when it says "95% sure", can you believe it? Along the way I found a bug in my own feature extraction, and a real-world failure that the benchmark did not predict.

Key findings

  • Frozen WavLM Base+ features from layer 6, with a small neural network on top, kept a mean equal error rate (EER) of 1.7% on clean audio, 2.2% over a simulated phone line and 2.1% after low-bitrate Opus compression, over three training seeds.
  • Which layer you take matters more than which classifier you train. With noise added to the phone line, layers 3 and 12 rose to 24-27% EER while layers 6 and 9 stayed at 8-9%. A classic MFCC baseline was close to chance on the phone line (41%).
  • The scores still ranked clips well under noise, but the confidence did not transfer: in 12 of 40 model and condition pairs, the calibrated scores were worse than always answering "I don't know" (Cllr above 1).
  • Zero padding in batched feature extraction had silently changed the features of short clips. For clips under 2 seconds, the score moved by 2.98 on average (in log-likelihood ratio); for clips that were never padded, by 0.01. I fixed it and re-ran everything.
  • In my own informal tests with the first browser demo, my voice recorded on a laptop scored 0.3% "AI voice", but a real English WhatsApp voice note scored 97.4%. Simulated Opus compression did not reproduce this failure.

The setup

The data is ASVspoof 2019 Logical Access, a standard benchmark of real and synthetic English speech. Its training set has 25,380 clips made with six spoofing systems (A01-A06); its evaluation set uses thirteen systems (A07-A19), most of them unseen in training, and different speakers. I trained on all training clips and tested on a fixed sample of 9,800 evaluation clips: 2,000 real and 600 for each attack.

Every test clip was scored in four conditions: clean; a simulated phone line (8 kHz, the 300-3400 Hz telephone band and G.711-style companding); the same line with white noise at 15 dB signal-to-noise ratio; and a round trip through Opus at 16 kbps, the codec family used by many voice-note and VoIP apps. Training used clean audio only, so every other condition is a shift the model never saw.

For features I used WavLM Base+, a self-supervised speech model, frozen, and took the average of its hidden states from layers 3, 6, 9 and 12 over the first 4 seconds of each clip. On top sits either logistic regression or a small neural network (one hidden layer). Scores are calibrated on the development set into log-likelihood ratios, so a score can be read as evidence for "fake" or "real". Each neural network was trained with three random seeds.

Which layer survives the phone line

Grouped bar chart of equal error rate for five feature sets with a neural network classifier, in four conditions. MFCC: 16.3% clean, 40.6% phone line, 50.8% phone line with noise, 13.4% Opus. WavLM layer 3: 2.1, 6.9, 23.6, 2.5. Layer 6: 1.7, 2.2, 8.1, 2.1. Layer 9: 3.5, 5.8, 8.9, 3.7. Layer 12: 5.6, 6.0, 27.0, 5.6.
Figure 1. Equal error rate on the 9,800 evaluation clips, mean of three seeds; error bars are one standard deviation across seeds. Neural network classifier on each feature set.
Features (neural network)CleanPhone linePhone line + noiseOpus 16 kbps
MFCC16.340.650.813.4
WavLM layer 32.16.923.62.5
WavLM layer 61.72.28.12.1
WavLM layer 93.55.88.93.7
WavLM layer 125.66.027.05.6

EER in percent: the error rate at the threshold where real voices wrongly flagged and fake voices wrongly passed are equally frequent. Layers 3 and 6 are almost equal on clean audio, but only layer 6 holds up when the channel changes. The noisy phone line is the hardest condition for every model, and it is where single training runs become unreliable: across seeds, layer 12 ranged from 23.1% to 32.6%, while layer 6 stayed between 6.8% and 9.5%. Three attacks did most of the damage for layer 6 with noise (seed 42): A18 rose from 3.2% to 26.3%, A17 from 1.1% to 20.9% and A19 from 4.6% to 18.0%.

When it says 95%, is it right?

A detector that ranks clips well can still be badly overconfident. To separate the two, I used Cllr, a cost from speaker recognition that scores the evidence itself: 0 is perfect, and 1 is what you get by always answering "I don't know". Its partner, minCllr, is the same cost after the best possible recalibration, so the gap between them is the cost of being wrong about your own confidence.

On clean audio the calibrated scores behaved. With noise on the phone line they did not: in 12 of 40 model and condition pairs Cllr was above 1, including five of the eight WavLM models; only layer 6 (both classifiers) and layer 3 with the neural network (0.97) stayed below it. For layer 12 with the neural network, Cllr was 3.49 while minCllr was 0.64. The ranking was still usable; the confidence, fitted on clean audio, was far too high. For layer 6 the noisy-line Cllr was 0.63 against a minCllr of 0.34: worse than clean, but still informative. One practical consequence: when the model is allowed to abstain on its least confident clips, layer 6 with noise drops from 9.0% errors on all clips to 4.1% on the 80% it is most sure about, while layer 3 with logistic regression stays at 45-51% at every coverage. It is confidently wrong.

A bug in my own pipeline: zero padding

To build the browser demo, I checked the browser model against the experiment pipeline clip by clip, and they disagreed. The cause was in the experiments, not the browser. To process clips in batches, shorter clips had been padded with silence up to the longest clip in each batch. WavLM Base+ normalises its first layer over the whole input, silence included, so the padding changed the features of short clips. The browser processes one clip at a time and never pads.

Bar chart of the mean absolute difference in log-likelihood ratio between the browser model and the experiment pipeline, by clip length. Before the fix: 2.98 under 2 s, 1.07 for 2-3 s, 0.41 for 3-4 s and 0.01 for 4 s clips that were never padded. After the fix: 0.01 in every group.
Figure 2. Mean absolute difference in log-likelihood ratio between the browser model and the experiment pipeline on 920 evaluation clips, by clip length, before and after the fix.

The pattern was clean: the more padding a clip had received, the further its score moved, and clips cut at exactly 4 seconds, which were never padded, matched to 0.01. For clips under 2 seconds the verdict changed on 11% of them. I changed the pipeline so that each batch holds only clips of equal length, re-extracted every feature and re-ran every experiment; the numbers in this article are from the fixed run. The conclusions did not change, but the headline did move (layer 6, clean, seed 42: 1.8% before, 2.1% after). After the fix, browser and pipeline agree on 100% of verdicts in every length group, with identical EER on the check clips. If you batch audio through a self-supervised model, check whether padding changes your features.

The browser demo, and my own voice notes

The demo runs the whole detector, from waveform to calibrated score, as one ONNX model in the browser, so the audio never leaves the device; it took about 0.75 seconds per clip on my laptop. Then I tried it on my own voice, with the first version of the demo (before the padding fix):

Clip (my own voice, all real)VerdictProbability of AI voiceLog-likelihood ratio
Recorded on a laptop microphoneLikely real0.3%-5.79
English WhatsApp voice note, uploaded as a fileLikely AI-generated97.4%+3.64
The same voice note, played through a speaker and recorded againLikely AI-generated99.9%+7.52
A different voice note, in Urdu, uploaded as a fileUncertain5.3%-2.87

A real English voice note, sent through WhatsApp, was called AI-generated with 97.4% confidence. That is exactly the false alarm a tool like this must not raise. My first guess was the codec, so I added the Opus condition to the experiments. It did not explain it: at 16 kbps, Opus barely moved the error rate or the calibration of the layer 6 model (Cllr 0.13). Whatever pushed my voice note over the line, whether the phone's microphone and processing, the app's settings, or simply a voice and room unlike the studio recordings of ASVspoof 2019, a clean simulation did not capture it. These are four clips, not a benchmark, but the lesson is the same as for the calibration results: a detector trained on one corpus should be tested on real recordings from the channel where it will be used.

What I take from it

Limitations

Reproduce the experiment, or try the detector

The repository has the pipeline as a tested Python package, both Kaggle notebooks, the clip IDs and every table in this article. The browser demo states its limitations next to every result, and it is a research demo, not a forensic tool: never use it alone to decide about a person, a payment or a call.

Data: ASVspoof 2019 database (Wang et al., Computer Speech & Language, 2020), University of Edinburgh, Open Data Commons Attribution License. Features: WavLM (Chen et al., IEEE Journal of Selected Topics in Signal Processing, 2022). Cllr: Brümmer and du Preez, Computer Speech & Language, 2006. Opus: RFC 6716 (Valin et al., 2012). This is a small engineering experiment, not a peer-reviewed paper.