Skip to content

A Rust port of the real-time chord recognition algorithm described in Adam Stark's PhD thesis. This library provides functionality for chromagram estimation and chord detection from audio input.

License

Notifications You must be signed in to change notification settings

VannRR/chord_detector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chord_detector

A Rust crate for real-time audio analysis: compute 12-bin chromagrams and detect musical chords with minimal latency.

Features

  • 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

Credits

This library uses and modifies work from:

  • Chord Detection Algorithm: Based on "Real-Time Chord Recognition For Live Performance" by A. M. Stark and M. D. Plumbley, ICMC 2009, Montreal. Expanded in Adam Stark's PhD thesis: "Musicians and Machines: Bridging the Semantic Gap in Live Performance", Queen Mary University of London, 2011.

Installation

Add to your Cargo.toml:

[dependencies]
chord_detector = "0.1.0"

Quick Start

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(())
}

API Reference

Chromagram

Compute 12-bin chromagrams from a stream of audio frames.

ChromagramBuilder

  • ChromagramBuilder::new() -> Self

  • .frame_size(usize) -> Self

    • Set the frame size for audio processing
  • .sampling_rate(usize) -> Self

    • Set the sampling rate of the audio
  • .downsample_factor(usize) -> Self

    • Set the downsample factor for processing
  • .num_harmonics(usize) -> Self

    • Set the number of harmonics to consider for chord detection
  • .num_octaves(usize) -> Self

    • Set the number of octaves to consider for chord detection
  • .search_width(usize) -> Self

    • Set the search width for finding spectral peaks
  • .build() -> Result<Chromagram, ChromagramError>

    • Finalize and create the Chromagram

Chromagram

  • Chromagram::builder() -> ChromagramBuilder

    • Start customizing with a builder
  • chromagram.next(frame: &[f32]) -> Result<Option<[f32; 12]>, ChromagramError>

    • Returns Ok(None) until enough data accumulates (half FFT buffer)
    • Returns Ok(Some(chroma)) when a new chromagram is ready

ChordDetector

Match 12-bin chromagrams to chord profiles.

ChordDetectorBuilder

  • ChordDetectorBuilder::new() -> Self

    • Create a new builder with default bleed = 0.157
  • .bleed(f32) -> Self

    • Set the bleed suppression factor (0.0..1.0)
  • .build() -> ChordDetector

    • Build the ChordDetector

ChordDetector

  • ChordDetector::builder() -> ChordDetectorBuilder

    • Return a builder to customize bleed suppression factor
  • ChordDetector::new() -> ChordDetector

    • Create a detector with default bleed = 0.157
  • detect_chord(chroma: &[f32]) -> Result<Chord, ChordError>

    • Detect the single best chord from a chromagram slice.
    • Returns Err(ChordError::InvalidLength) if chroma.len() != SEMITONES.
  • top_k(chroma: &[f32], k: usize) -> Result<Vec<Chord>, ChordError>

    • Detect the top k chords from a chromagram slice.
    • Returns:
      • Err(InvalidLength) if chroma.len() != SEMITONES.
      • Err(InvalidArgument) if k == 0.

Data Types

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 */ }

Dependencies

  • rustfft: Fast Fourier Transform implementation
  • thiserror: Error handling utilities

License

This crate is licensed under GNU General Public License v3.0. See the LICENSE file for full text.

Version History

0.1.0 — 2025-07-24

  • Initial release

  • Streaming chromagram builder and real-time chord detector

  • Customizable builder APIs for both modules

Testing with Audio Files

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.

About

A Rust port of the real-time chord recognition algorithm described in Adam Stark's PhD thesis. This library provides functionality for chromagram estimation and chord detection from audio input.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published