Systems and subsystems

Audio loader and probe

One decode path for every file: FFmpeg turns any container or codec into 16 kHz mono float32, once, and the header is read separately. Nothing downstream ever sees the original format.

In plain words

Every audio file, whatever it is, becomes the same thing before any detector looks at it: a single channel of samples at 16 kHzsource: README.md, section how-it-decides, as floating-point numbers, with silence trimmed where a detector asks for it. The header (codec, rate, tags) is read on its own and never mixed into the audio. This is what closes the containerWhat the file header says: codec, sample rate, bit depth, encoder tags. Read for routing; never learned from, because on our training data the container is the label. shortcutA difference between training and test data that a model can exploit instead of listening to the voice: peak level, leading silence, clip length, container fields, a missing high band. Each one found was closed before it could be learned.: training real speech is FLAC or 22 kHz WAV and some training fakes are MP3, while every test file is 16 kHz PCM, so a model that could see the format would learn the format.

What goes in and comes out

  • load_audio(path) → a 1-D float32 array at 16 kHz mono.
  • probe_audio(path) → a dict of header facts: format, codec, sample rate, channels, duration, bit rate; on failure an error entry, never an exception.
  • trim_silence(x) → the array with quiet edges removed, relative to its loudest frame.
  • windows(x, win, hop) → fixed-length windows, tiling clips shorter than one window.

Where it lives

src/hearsay/audio.py: DecodeError, load_audio, probe_audio, windows, trim_silence. The sample rate constant is in the package's __init__.

Parameters and where each is set

Status

Built and tested; unchanged since the M0 rung. Invariant 1 of the architecture document ("one audio path").

Numbers

None of its own. Its effect shows up as the closed container shortcut on What did not work.

Known limits

FFmpeg's channel downmix averages channels; a stereo file with out-of-phase channels would lose signal. The loader keeps a partial decode of a truncated file rather than failing it, so a damaged file scores on what could be read (and the runner flags decode failures separately).

Tests that pin it

tests/test_audio_and_submission.py: any format to 16 kHz mono float32 (stereo 44.1k WAV, 8k mono, 22 kHz, CBR and VBR MP3, AAC in m4a and mp4, FLAC, Opus), determinism, empty and garbage files raise, a truncated WAV decodes what it can, NaN samples are sanitized, silence loads as zeros, no loudness normalization, windows cover the clip, silence trim is level-independent. tests/test_detector_contract.py pins that a clip is decoded once and that a decode failure is cached.

Sources

Architecture, invariant 1; the shortcut ledger; the code map.