How to process 8 hours of audio on an iPhone without draining the battery
An iPhone on a nightstand recording all night sounds impossible from a battery standpoint. A naive AVAudioEngine setup recording 48 kHz stereo for eight hours would torch the battery and the CPU. Yet SleepTrace does exactly this routinely and consistently comes back above 20% battery. The trick is not a clever algorithm — it is a ruthless pipeline architecture.
Here is the field-tested stack that actually runs overnight on device.
Rule 1: never record at full fidelity all night
You do not need 48 kHz to detect a snore. The discriminative information for breathing, snoring, and grinding lives comfortably below 16 kHz. The pipeline:
- Input at hardware native (AVAudioSession shared model, no re-render), but immediately downmix to mono.
- Resample to 12.8 kHz in a fixed-size ring buffer. This preserves enough to detect snoring (roughly 200–1800 Hz with harmonics) and cuts the per-sample cost dramatically.
- Discard the raw PCM immediately after the FFT/window is emitted. Keep only the feature vector in memory.
Rule 2: batch, do not stream continuously
Continuous audio capture with a real-time callback keeps the CPU warm all night. Instead:
- Capture in 2-second windows, 50% overlap.
- Run each window through a lightweight feature extractor (spectral flux + zero-crossing + band energy).
- Sleep the process between windows using a background task assertion.
This yields a bursty CPU profile: ~250ms of work every 2 seconds, then sleep. Battery impact is negligible.
Rule 3: do the ML classification in bursts
The model itself is small (sub-10MB CoreML), but running it every window still costs. The actual architecture:
- Feature vectors are written to a ring buffer.
- Every fifth window, the ring is evaluated as a sequence with a temporal smoothing pass.
- Results are written to a compact log: timestamps + classification + confidence.
This is where the snore/apnea detection detail lives — the acoustic feature engineering is straightforward; the trick is not running it continuously.
Rule 4: guard the background task correctly
// request a background task that expires gracefully
let task = UIApplication.shared.beginBackgroundTask(withName: "audio-processing") {
// flush buffer, end cleanly
processor.flush()
}
Without this, iOS suspends the process within ~30 seconds of the screen locking. With it, the pipeline runs undisturbed for the full night.
Rule 5: verify, then forget the raw audio
Only the classification log (a few hundred kilobytes) survives the night. The raw ring buffers are overwritten the next morning. On-device only, always.
The whole pipeline fits under 50MB of RAM and uses roughly 10–14% of a single night's battery on modern iPhones. The constraint that solved it was not faster hardware — it was treating raw audio as ephemeral by design.
Top comments (0)