A Rust-based audio fingerprinting CLI inspired by Shazam-style matching. It stores a reference track as a set of hashed spectral fingerprints, then identifies unknown clips by voting on the timing offset where the most fingerprints agree.
- Store reference songs into a local SQLite fingerprint database
- Recognize an unknown clip against everything stored
- Record the microphone (
listen) and identify what is playing - Show ranked candidates for a clip (
list-top-matches) - Manage the database from the CLI (
list-songs,remove-song,db-stats) - Layered TOML config (
/etc, user config, local config), with CLI flags overriding all of it - Optional clipping for reference indexing (
--clip-start,--clip-duration,--auto-clip) - Cross-platform: Linux and Windows (PowerShell helper scripts included)
- Rust (edition 2024, needs rustc 1.85+)
- SQLite via
rusqlite(bundled, no system SQLite library required) - Audio capture via
cpal(microphone input forlisten) - FFT via
rustfft - WAV I/O via
hound - TOML config via
serde+toml
- Read WAV samples
- Optionally clip to a sub-range of the track
- STFT spectrogram
- Peak extraction (constellation points)
- Pair each peak with nearby peaks to generate
(hash, anchor_time_ms)fingerprints - Insert song metadata + fingerprints into SQLite
- Read WAV samples
- STFT spectrogram
- Peak extraction
- Fingerprint generation (same hashing as above)
- Look up each hash in the database and record the time offset between query and match
- Rank candidate songs by whichever offset gets the most votes: a real match produces one dominant, consistent offset
choco install resonanceid-cliparu -S resonanceid-cli
# or
yay -S resonanceid-cliRequires Rust 1.85 or newer (edition 2024). CI uses the stable toolchain. Works
on Linux, macOS and Windows — SQLite is compiled in via rusqlite's bundled
feature, so no system SQLite development library is needed.
git clone https://github.com/rugbedbugg/ResonanceID-cli.git
cd ResonanceID-cli
cargo build --releaseThe binary is target/release/resonanceid-cli (resonanceid-cli.exe on Windows). During development, run it via cargo run --:
cargo run -- --helpArgs after
--go to the program, not to cargo.
resonanceid-cli store <wav_path> [name] [options]Aliases: remember, index.
The song name defaults to the full filename stem of <wav_path>; pass [name] to override it.
resonanceid-cli recognize <wav_path> [options]Prints the best match (if any), then every candidate ranked by score.
resonanceid-cli list-top-matches <wav_path> [options]Same options as recognize, just prints the ranked list without singling out a "best" match.
resonanceid-cli listen [--duration <seconds>] [options]Aliases: mic, record.
Records from the default input device, downmixes to mono, normalizes to 44.1 kHz, then recognizes against the database — same output as recognize. Recording length defaults to 10s; 10–15s works best since matching quality drops on very short clips.
resonanceid-cli import <folder> [options]Aliases: bulk.
Indexes every supported audio file directly inside <folder> (non-recursive): mp3, wav, flac, m4a, ogg, opus, wma, aac. Non-WAV files are converted via ffmpeg into a throwaway temp folder that is deleted when the run finishes — original files are never modified. The song name is the full filename stem. Unreadable files are skipped with a warning instead of aborting the run.
resonanceid-cli list-songs [--db <db_path>]
resonanceid-cli remove-song <song_id> [--db <db_path>]
resonanceid-cli db-stats [--db <db_path>]Every command also accepts --help for its own usage summary.
| Flag | Description |
|---|---|
--db <path> |
SQLite database file. Default: resonanceid-cli.db in the working directory. |
--config <path> |
Load config from this exact file instead of the default search paths. |
--no-config |
Skip config files entirely and use built-in defaults. |
| Flag | Default | Description |
|---|---|---|
--window-size <n> |
1024 |
STFT window size, in samples. |
--hop-size <n> |
512 |
STFT hop size, in samples. |
--anchor-window <n> |
5 |
How many following peaks each anchor peak pairs with. |
--threshold-db <f32> |
-20.0 |
Minimum peak magnitude to keep. |
| Flag | Default | Description |
|---|---|---|
--min-match-score <n> |
2 |
Minimum offset-vote count for a candidate to count as a match. |
--dynamic-gate-scale <f32> |
0.3 |
Scales the match-score gate relative to query size, so short clips aren't held to the same bar as long ones. |
--small-query-threshold <n> |
1000 |
Fingerprint count below which a query is treated as "small" for gating purposes. |
--max-results <n> |
5 |
How many ranked candidates to return. |
| Flag | Description |
|---|---|
--clip-start <seconds> |
Start offset into the source file. |
--clip-duration <seconds> |
Length of the clip to index. |
--auto-clip |
Center a clip in the middle of the track instead of indexing the whole thing (20s by default, or --clip-duration if given). Overrides --clip-start. |
Indexing less than 15 seconds of audio prints a warning, matching quality gets unreliable below that.
Without --config, these paths are checked in order and merged: later files override fields set by earlier ones.
Linux/macOS:
/etc/resonanceid-cli/config.toml$HOME/.config/resonanceid-cli/config.toml./resonanceid-cli.toml
Windows:
%APPDATA%\resonanceid-cli\config.toml.\resonanceid-cli.toml
Precedence overall: CLI flags > config file(s) > built-in defaults.
Copy resonanceid-cli.toml.example to get started:
[fingerprint]
window_size = 1024
hop_size = 512
anchor_window = 5
threshold_db = -20.0
[recognition]
min_match_score = 2
dynamic_gate_scale = 0.3
small_query_threshold = 1000
max_results = 5bash:
# 1) Convert to WAV: mono, 44.1kHz, 16-bit PCM
ffmpeg -y -i input.mp3 -ac 1 -ar 44100 -sample_fmt s16 input.wav
# 2) Store a reference track
resonanceid-cli store input.wav
# 3) Recognize a clip against it
resonanceid-cli recognize clip.wavPowerShell:
# 1) Convert to WAV: mono, 44.1kHz, 16-bit PCM
ffmpeg -y -i input.mp3 -ac 1 -ar 44100 -sample_fmt s16 input.wav
# 2) Store a reference track
.\target\release\resonanceid-cli.exe store input.wav
# 3) Recognize a clip against it
.\target\release\resonanceid-cli.exe recognize clip.wav
# ...or just play the song out loud and let the mic hear it
.\target\release\resonanceid-cli.exe listen --duration 12Or use the bundled helper scripts (scripts/):
# Convert any audio file to the required WAV format
.\scripts\Convert-ToWav.ps1 -InputFile input.mp3
# Convert + store + recognize in one go
.\scripts\Invoke-ResonanceDemo.ps1 -Reference input.mp3 -Clip clip.wav- Input must be 16-bit integer PCM WAV. Anything else (float WAV, 24-bit, compressed formats) is rejected outright, convert with
ffmpegfirst. - WAV samples are read as a flat, single-channel stream. A stereo file will produce garbage fingerprints unless you downmix to mono first (
-ac 1above). - For stable matching, reference clips of roughly 20–45 seconds work best; the tool will warn if you index under 15 seconds.
With mise and rustup installed, use the same tasks as CI:
mise trust
mise run setup
mise run checkmise run check runs formatting, build, Clippy and tests in order. Individual
checks are mise run format, mise run build, mise run lint and mise run test.
Rustup retains ownership of the existing stable toolchain. Build, lint and test
commands use Cargo.lock with --locked; no dependency constraints change.
CI checks Linux and Windows on pull requests, main/ci branch pushes and manual
runs. Releases reuse that validation, smoke-test the built release binaries,
then publish and verify their downloaded checksums. Only publication receives
write permission. Manual release runs on branches validate and build artifacts
without publishing; publishing requires a matching v version tag.
The pipeline baseline documents the shared structure.
Chocolatey metadata is maintained under SUBMISSIONS/chocolatey; merging it
does not submit a package. The package revision there retains the approved
1.0.0 binary independently of the application's development version.
The tests cover CLI argument parsing, config loading/layering, clip-range resolution, hashing, and database integration.
MIT, see LICENSE.
Chocolatey, Winget and AUR packages are prepared from the same published release and validated automatically. Publication is an explicit manual workflow choice; Winget opens an upstream PR and Chocolatey submissions undergo moderation. See packaging instructions for validation, release selection and required publishing credentials.