An audio identification engine. There are two ways to build one, and the point of the project is to build both and measure them against each other.
- ClassicalThe algorithm from Wang et al. (2003) — the paper behind Shazam. Spectrogram, keep the loudest points, hash their geometric relationships, look them up in an inverted index. No ML, no training data, and it holds up remarkably well.
- LearnedA later phase — run audio through a pretrained encoder for one vector per track and retrieve with approximate nearest-neighbor search.
The DSP is written out from NumPy and
SciPy rather than pulled from librosa — the STFT,
the windowing, and the peak-picking are all in plain sight, because the whole
exercise is being able to explain why each piece is there.
How the classical path works
Most of a song's spectrogram is redundant. What identifies it is a sparse set of standout points and how they're arranged.
- Spectrogram. Chop the signal into ~90 ms frames and transform each, giving a picture of frequency over time. Frame length is a trade-off: longer pins down pitch but blurs timing; 1024 samples at 11 kHz sits in the useful middle.
- Peaks. In each neighborhood, keep the loudest point and throw the rest away. These peaks survive noise, EQ, and MP3 compression — and reducing a dense image to a handful of points per second makes the rest cheap.
- Hashing. A single peak is a weak clue. A pair of peaks — two frequencies and the time between them — is far more specific and barely changes if the clip starts elsewhere. Each pair becomes an integer hash.
- Index & match. Every hash points back to its track and time in a SQLite table. To identify a clip, hash it the same way and check whether matches line up at one consistent time offset — a real match spikes; coincidences scatter and cancel.
Where it's at
Phase 1 — the classical fingerprinter — is complete and works end to end: index a directory of audio and identify clips against it from the command line. On a synthetic corpus of eight tracks, self-retrieval is 100%, and noisy 4-second excerpts still land on the right track.
Next up: a benchmark harness that degrades audio (noise, compression, pitch shift, time stretch) to measure where match rate falls off, then the embedding path, a hand-rolled HNSW index compared against FAISS, and streaming matching from multiple sources at once.
Explore
Read the code on GitHub ↗ — every parameter lives in one config.py with a note on why it's set the way it is.