Skip to main content

chromaprint/
lib.rs

1//! Ultra-high performance audio fingerprinting library.
2//!
3//! Rust port of [Chromaprint](https://acoustid.org/chromaprint), producing compact
4//! 32-bit integer fingerprint arrays for audio identification via AcoustID.
5//!
6//! # Example
7//!
8//! ```no_run
9//! use chromaprint::{Fingerprinter, Algorithm};
10//!
11//! let mut fp = Fingerprinter::new(Algorithm::default());
12//! fp.start(44100, 2).unwrap();
13//! // fp.feed(&pcm_samples).unwrap();
14//! fp.finish().unwrap();
15//! let raw = fp.fingerprint();
16//! let encoded = fp.encode();
17//! ```
18
19pub mod audio;
20pub mod chroma;
21pub mod codec;
22pub mod config;
23pub mod error;
24pub mod fft;
25pub mod fingerprint;
26pub mod pipeline;
27pub mod simd;
28pub mod types;
29
30pub use error::{Error, Result};
31pub use types::{Algorithm, Fingerprint};
32
33use pipeline::Pipeline;
34
35/// Main entry point for audio fingerprinting.
36///
37/// Provides a streaming push-based API: create, start, feed samples, finish,
38/// then retrieve the fingerprint.
39pub struct Fingerprinter {
40    algorithm: Algorithm,
41    pipeline: Pipeline,
42    started: bool,
43}
44
45impl Fingerprinter {
46    /// Create a new fingerprinter for the given algorithm.
47    pub fn new(algorithm: Algorithm) -> Self {
48        Self {
49            algorithm,
50            pipeline: Pipeline::new(algorithm),
51            started: false,
52        }
53    }
54
55    /// Start processing a new audio stream.
56    pub fn start(&mut self, sample_rate: u32, num_channels: u16) -> Result<()> {
57        self.pipeline.start(sample_rate, num_channels)?;
58        self.started = true;
59        Ok(())
60    }
61
62    /// Feed interleaved PCM i16 samples into the fingerprinter.
63    pub fn feed(&mut self, samples: &[i16]) -> Result<()> {
64        if !self.started {
65            return Err(Error::NotStarted);
66        }
67        self.pipeline.feed(samples);
68        Ok(())
69    }
70
71    /// Finish processing and flush remaining data.
72    pub fn finish(&mut self) -> Result<()> {
73        if !self.started {
74            return Err(Error::NotStarted);
75        }
76        self.pipeline.finish();
77        Ok(())
78    }
79
80    /// Get the raw fingerprint (array of 32-bit sub-fingerprints).
81    pub fn fingerprint(&self) -> &[u32] {
82        self.pipeline.fingerprint()
83    }
84
85    /// Get the compressed base64-encoded fingerprint string.
86    pub fn encode(&self) -> String {
87        fingerprint::compressor::compress(self.pipeline.fingerprint(), self.algorithm)
88    }
89
90    /// Get the SimHash of the fingerprint.
91    pub fn hash(&self) -> u32 {
92        fingerprint::simhash::simhash(self.pipeline.fingerprint())
93    }
94
95    /// Get the algorithm used by this fingerprinter.
96    pub fn algorithm(&self) -> Algorithm {
97        self.algorithm
98    }
99}
100
101/// Convenience function: fingerprint audio in one shot.
102pub fn fingerprint_audio(
103    samples: &[i16],
104    sample_rate: u32,
105    num_channels: u16,
106    algorithm: Algorithm,
107) -> Result<Fingerprint> {
108    let mut fp = Fingerprinter::new(algorithm);
109    fp.start(sample_rate, num_channels)?;
110    fp.feed(samples)?;
111    fp.finish()?;
112    Ok(Fingerprint::new(fp.fingerprint().to_vec(), algorithm))
113}
114
115/// Encode a raw fingerprint to a compressed base64 string.
116pub fn encode_fingerprint(raw: &[u32], algorithm: Algorithm) -> String {
117    fingerprint::compressor::compress(raw, algorithm)
118}
119
120/// Decode a compressed base64 fingerprint string to raw sub-fingerprints.
121pub fn decode_fingerprint(encoded: &str) -> Result<(Vec<u32>, Algorithm)> {
122    fingerprint::decompressor::decompress(encoded)
123}
124
125/// Compute the SimHash of a raw fingerprint.
126pub fn hash_fingerprint(raw: &[u32]) -> u32 {
127    fingerprint::simhash::simhash(raw)
128}
129
130/// Batch fingerprint multiple audio inputs in parallel (requires "parallel" feature).
131#[cfg(feature = "parallel")]
132pub fn fingerprint_batch(
133    inputs: &[(&[i16], u32, u16)],
134    algorithm: Algorithm,
135) -> Vec<Result<Fingerprint>> {
136    use rayon::prelude::*;
137
138    inputs
139        .par_iter()
140        .map(|&(samples, sample_rate, num_channels)| {
141            fingerprint_audio(samples, sample_rate, num_channels, algorithm)
142        })
143        .collect()
144}