DEV Community

SleepTrace
SleepTrace

Posted on

The audio feature that actually separates snoring from talking in the dark

The audio feature that actually separates snoring from talking in the dark

Distinguishing a snore from a sentence, or a gasp from a cough, is the core problem in acoustic sleep analysis — and the one that most sleep apps paper over. A naive classifier that fires on "loud breathing in the 200–3000 Hz band" will flag every partner conversation, dog bark, and car horn at 2am as "sleep-disordered breathing." That is why event counts from most apps are unreliable.

This is the signal-processing breakdown of the feature set that SleepTrace actually uses, and why it works on-device without a server.

The time-frequency signature of a real snore

A human snore is not a steady tone. It is an aperiodic burst with a strong low-frequency envelope (roughly 120–450 Hz), modulated by the soft palate, with spectral energy tapering by ~2kHz. A sentence has formant structure — clear peaks at 500, 1500, 2500 Hz corresponding to vowels — and a rhythm tied to phoneme timing.

The discriminator that matters:

  • Spectral tilt slope. Snoring energy falls off rapidly above 1kHz; speech energy stays flatter. A single tilt coefficient over the 0.5–4kHz window separates most snore/sentence pairs.
  • Zero-crossing density in the high band. Snoring is voiced and periodic at the glottal rate (~15–30 Hz, with harmonics). High-band zero-crossing density is low for snoring and high for sibilant speech ("s", "sh", "f").
  • Harmonic structure. Snoring harmonics sit on a single fundamental tied to vocal-fold vibration. Speech has a structured harmonic stack that tracks vowel formants.

Combining the tilt slope + high-band zero-crossing density gives a clean separator that runs in a 40-coefficient mel filterbank and costs ~90% of one CPU core on an iPhone SE for a 2-second window.

The breathing pause signature

A gasp or apnea-related pause is, by definition, the absence of a periodic signal. The trick is distinguishing "quiet because paused" from "quiet because the phone is far away." The feature: a cross-correlation of the current 4-second window against the trailing 30-second median breathing rhythm. A real pause shows a sharp drop in correlation that persists past the window length and is accompanied by a sub-baseline spectral flatness (the body is still trying to breathe, quietly).

Why this works without cloud compute

All of these — spectral tilt, zero-crossing density, harmonic-to-noise ratio — are cheap FFTs on 2-second windows. They map cleanly onto a small neural net (under 2MB) that runs entirely in CoreML on-device. No upload. No server-side feature store keyed off the microphone. No privacy question at all.

The full on-device pipeline and the exact feature coefficients are published on the SleepTrace engineering blog.

For builders: start with the discriminator, not the dataset

Most sleep-audio apps collect thousands of hours of labeled data and then ship a dense transformer that runs hot and uploads raw audio. Start instead with a three-feature linear separator (tilt slope, high-band zero crossings, correlation drop) and a threshold calibrated on-device. It will beat the transformer on battery life, privacy, and honesty — and you will have a product you can ship before the cloud bill arrives.

Top comments (0)