| Crates.io | chord_detector |
| lib.rs | chord_detector |
| version | 0.1.0 |
| created_at | 2025-07-25 02:23:53.861191+00 |
| updated_at | 2025-07-25 02:23:53.861191+00 |
| description | Stream-based 12-bin chromagram computation and real-time chord detection. |
| homepage | |
| repository | https://github.com/vannrr/chord_detector |
| max_upload_size | |
| id | 1767116 |
| size | 72,673 |
A Rust crate for real-time audio analysis: compute 12-bin chromagrams and detect musical chords with minimal latency.
Streaming chromagram extraction via a builder API
Real-time chord detection from 12-bin chromagrams
Customizable bleed suppression for chord matching
Zero-allocation in the hot path after initialization
This library uses and modifies work from:
Add to your Cargo.toml:
[dependencies]
chord_detector = "0.1.0"
use chord_detector::{Chromagram, ChordDetector};
fn run() -> Result<(), Box<dyn std::error::Error>> {
// 1) Build a chromagram pipeline
let mut chroma = Chromagram::builder()
.frame_size(1024)
.sampling_rate(48_000)
.build()?;
// 2) Build a chord detector
let mut detector = ChordDetector::builder()
.bleed(0.15)
.build();
// 3) In your audio loop:
let audio_frame: Vec<f32> = vec![0.0; 1024]; // fill with actual samples
if let Some(chroma_bins) = chroma.next(&audio_frame)? {
let chord = detector.detect_chord(&chroma_bins)?;
println!(
"Detected {} {} chord with confidence {:.3}",
chord.root,
chord.kind,
chord.confidence
);
}
Ok(())
}
Compute 12-bin chromagrams from a stream of audio frames.
ChromagramBuilder::new() -> Self
.frame_size(usize) -> Self
.sampling_rate(usize) -> Self
.downsample_factor(usize) -> Self
.num_harmonics(usize) -> Self
.num_octaves(usize) -> Self
.search_width(usize) -> Self
.build() -> Result<Chromagram, ChromagramError>
Chromagram::builder() -> ChromagramBuilder
chromagram.next(frame: &[f32]) -> Result<Option<[f32; 12]>, ChromagramError>
Ok(None) until enough data accumulates (half FFT buffer)Ok(Some(chroma)) when a new chromagram is readyMatch 12-bin chromagrams to chord profiles.
ChordDetectorBuilder::new() -> Self
.bleed(f32) -> Self
.build() -> ChordDetector
ChordDetectorChordDetector::builder() -> ChordDetectorBuilder
ChordDetector::new() -> ChordDetector
detect_chord(chroma: &[f32]) -> Result<Chord, ChordError>
Err(ChordError::InvalidLength) if chroma.len() != SEMITONES.top_k(chroma: &[f32], k: usize) -> Result<Vec<Chord>, ChordError>
k chords from a chromagram slice.Err(InvalidLength) if chroma.len() != SEMITONES.Err(InvalidArgument) if k == 0.pub enum NoteName {
C, Cs, D, Ds, E, F, Fs, G, Gs, A, As, B, Unknown
}
pub enum ChordKind {
Major,
Minor,
Power,
DominantSeventh,
MajorSeventh,
MinorSeventh,
Diminished,
Augmented,
SuspendedSecond,
SuspendedFourth,
}
pub struct Chord {
pub root: NoteName,
pub kind: ChordKind,
pub confidence: f32, // lower is a better match
}
pub enum ChromagramError { /* frame size & config errors */ }
pub enum ChordError { /* invalid length & argument errors */ }
This crate is licensed under GNU General Public License v3.0. See the LICENSE file for full text.
Initial release
Streaming chromagram builder and real-time chord detector
Customizable builder APIs for both modules
The integration tests use the lewton crate to load .ogg files from the tests/chord-samples directory. Ensure the test files have been created with tests/generate_chords.py and run tests with cargo test.