chord_detector

Crates.iochord_detector
lib.rschord_detector
version0.1.0
created_at2025-07-25 02:23:53.861191+00
updated_at2025-07-25 02:23:53.861191+00
descriptionStream-based 12-bin chromagram computation and real-time chord detection.
homepage
repositoryhttps://github.com/vannrr/chord_detector
max_upload_size
id1767116
size72,673
VannRR (VannRR)

documentation

README

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.

Commit count: 0

cargo fmt