Skip to main content

chromaprint/
config.rs

1use crate::fingerprint::classifier::Classifier;
2use crate::fingerprint::filter::HaarFilter;
3use crate::fingerprint::quantizer::Quantizer;
4
5/// Default internal sample rate.
6pub const DEFAULT_SAMPLE_RATE: u32 = 11025;
7
8/// Default FFT frame size.
9pub const DEFAULT_FRAME_SIZE: usize = 4096;
10
11/// Default FFT frame overlap.
12pub const DEFAULT_FRAME_OVERLAP: usize = DEFAULT_FRAME_SIZE - DEFAULT_FRAME_SIZE / 3;
13
14/// Minimum frequency for chroma extraction (Hz).
15pub const MIN_FREQ: u32 = 28;
16
17/// Maximum frequency for chroma extraction (Hz).
18pub const MAX_FREQ: u32 = 3520;
19
20/// Default chroma filter coefficients (5-tap symmetric FIR).
21pub const CHROMA_FILTER_COEFFICIENTS: &[f64] = &[0.25, 0.75, 1.0, 0.75, 0.25];
22
23/// Configuration for a fingerprinting algorithm variant.
24#[derive(Debug, Clone)]
25pub struct Config {
26    pub classifiers: &'static [Classifier],
27    pub filter_coefficients: &'static [f64],
28    pub interpolate: bool,
29    pub remove_silence: bool,
30    pub silence_threshold: i16,
31    pub frame_size: usize,
32    pub frame_overlap: usize,
33    pub sample_rate: u32,
34}
35
36impl Config {
37    #[inline]
38    pub fn frame_increment(&self) -> usize {
39        self.frame_size - self.frame_overlap
40    }
41
42    pub fn max_filter_width(&self) -> usize {
43        self.classifiers
44            .iter()
45            .map(|c| c.filter.width as usize)
46            .max()
47            .unwrap_or(0)
48    }
49}
50
51static CLASSIFIERS_TEST1: [Classifier; 16] = [
52    Classifier { filter: HaarFilter { kind: 0, y: 0, height: 3, width: 15 }, quantizer: Quantizer { t0: 2.10543, t1: 2.45354, t2: 2.69414 } },
53    Classifier { filter: HaarFilter { kind: 1, y: 0, height: 4, width: 14 }, quantizer: Quantizer { t0: -0.345922, t1: 0.0463746, t2: 0.446251 } },
54    Classifier { filter: HaarFilter { kind: 1, y: 4, height: 4, width: 11 }, quantizer: Quantizer { t0: -0.392132, t1: 0.0291077, t2: 0.443391 } },
55    Classifier { filter: HaarFilter { kind: 3, y: 0, height: 4, width: 14 }, quantizer: Quantizer { t0: -0.192851, t1: 0.00583535, t2: 0.204053 } },
56    Classifier { filter: HaarFilter { kind: 2, y: 8, height: 2, width: 4 }, quantizer: Quantizer { t0: -0.0771619, t1: -0.00991999, t2: 0.0575406 } },
57    Classifier { filter: HaarFilter { kind: 5, y: 6, height: 2, width: 15 }, quantizer: Quantizer { t0: -0.710437, t1: -0.518954, t2: -0.330402 } },
58    Classifier { filter: HaarFilter { kind: 1, y: 9, height: 2, width: 16 }, quantizer: Quantizer { t0: -0.353724, t1: -0.0189719, t2: 0.289768 } },
59    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 2, width: 10 }, quantizer: Quantizer { t0: -0.128418, t1: -0.0285697, t2: 0.0591791 } },
60    Classifier { filter: HaarFilter { kind: 3, y: 9, height: 2, width: 16 }, quantizer: Quantizer { t0: -0.139052, t1: -0.0228468, t2: 0.0879723 } },
61    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 3, width: 6 }, quantizer: Quantizer { t0: -0.133562, t1: 0.00669205, t2: 0.155012 } },
62    Classifier { filter: HaarFilter { kind: 3, y: 3, height: 6, width: 2 }, quantizer: Quantizer { t0: -0.0267, t1: 0.00804829, t2: 0.0459773 } },
63    Classifier { filter: HaarFilter { kind: 2, y: 8, height: 1, width: 10 }, quantizer: Quantizer { t0: -0.0972417, t1: 0.0152227, t2: 0.129003 } },
64    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 4, width: 14 }, quantizer: Quantizer { t0: -0.141434, t1: 0.00374515, t2: 0.149935 } },
65    Classifier { filter: HaarFilter { kind: 5, y: 4, height: 2, width: 15 }, quantizer: Quantizer { t0: -0.64035, t1: -0.466999, t2: -0.285493 } },
66    Classifier { filter: HaarFilter { kind: 5, y: 9, height: 2, width: 3 }, quantizer: Quantizer { t0: -0.322792, t1: -0.254258, t2: -0.174278 } },
67    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 8, width: 4 }, quantizer: Quantizer { t0: -0.0741375, t1: -0.00590933, t2: 0.0600357 } },
68];
69
70static CLASSIFIERS_TEST2: [Classifier; 16] = [
71    Classifier { filter: HaarFilter { kind: 0, y: 4, height: 3, width: 15 }, quantizer: Quantizer { t0: 1.98215, t1: 2.35817, t2: 2.63523 } },
72    Classifier { filter: HaarFilter { kind: 4, y: 4, height: 6, width: 15 }, quantizer: Quantizer { t0: -1.03809, t1: -0.651211, t2: -0.282167 } },
73    Classifier { filter: HaarFilter { kind: 1, y: 0, height: 4, width: 16 }, quantizer: Quantizer { t0: -0.298702, t1: 0.119262, t2: 0.558497 } },
74    Classifier { filter: HaarFilter { kind: 3, y: 8, height: 2, width: 12 }, quantizer: Quantizer { t0: -0.105439, t1: 0.0153946, t2: 0.135898 } },
75    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 4, width: 8 }, quantizer: Quantizer { t0: -0.142891, t1: 0.0258736, t2: 0.200632 } },
76    Classifier { filter: HaarFilter { kind: 4, y: 0, height: 3, width: 5 }, quantizer: Quantizer { t0: -0.826319, t1: -0.590612, t2: -0.368214 } },
77    Classifier { filter: HaarFilter { kind: 1, y: 2, height: 2, width: 9 }, quantizer: Quantizer { t0: -0.557409, t1: -0.233035, t2: 0.0534525 } },
78    Classifier { filter: HaarFilter { kind: 2, y: 7, height: 3, width: 4 }, quantizer: Quantizer { t0: -0.0646826, t1: 0.00620476, t2: 0.0784847 } },
79    Classifier { filter: HaarFilter { kind: 2, y: 6, height: 2, width: 16 }, quantizer: Quantizer { t0: -0.192387, t1: -0.029699, t2: 0.215855 } },
80    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 3, width: 2 }, quantizer: Quantizer { t0: -0.0397818, t1: -0.00568076, t2: 0.0292026 } },
81    Classifier { filter: HaarFilter { kind: 5, y: 10, height: 1, width: 15 }, quantizer: Quantizer { t0: -0.53823, t1: -0.369934, t2: -0.190235 } },
82    Classifier { filter: HaarFilter { kind: 3, y: 6, height: 2, width: 10 }, quantizer: Quantizer { t0: -0.124877, t1: 0.0296483, t2: 0.139239 } },
83    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 1, width: 14 }, quantizer: Quantizer { t0: -0.101475, t1: 0.0225617, t2: 0.231971 } },
84    Classifier { filter: HaarFilter { kind: 3, y: 5, height: 6, width: 4 }, quantizer: Quantizer { t0: -0.0799915, t1: -0.00729616, t2: 0.063262 } },
85    Classifier { filter: HaarFilter { kind: 1, y: 9, height: 2, width: 12 }, quantizer: Quantizer { t0: -0.272556, t1: 0.019424, t2: 0.302559 } },
86    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 2, width: 14 }, quantizer: Quantizer { t0: -0.164292, t1: -0.0321188, t2: 0.0846339 } },
87];
88
89// TEST3 uses the same classifier values as TEST2.
90static CLASSIFIERS_TEST3: [Classifier; 16] = [
91    Classifier { filter: HaarFilter { kind: 0, y: 4, height: 3, width: 15 }, quantizer: Quantizer { t0: 1.98215, t1: 2.35817, t2: 2.63523 } },
92    Classifier { filter: HaarFilter { kind: 4, y: 4, height: 6, width: 15 }, quantizer: Quantizer { t0: -1.03809, t1: -0.651211, t2: -0.282167 } },
93    Classifier { filter: HaarFilter { kind: 1, y: 0, height: 4, width: 16 }, quantizer: Quantizer { t0: -0.298702, t1: 0.119262, t2: 0.558497 } },
94    Classifier { filter: HaarFilter { kind: 3, y: 8, height: 2, width: 12 }, quantizer: Quantizer { t0: -0.105439, t1: 0.0153946, t2: 0.135898 } },
95    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 4, width: 8 }, quantizer: Quantizer { t0: -0.142891, t1: 0.0258736, t2: 0.200632 } },
96    Classifier { filter: HaarFilter { kind: 4, y: 0, height: 3, width: 5 }, quantizer: Quantizer { t0: -0.826319, t1: -0.590612, t2: -0.368214 } },
97    Classifier { filter: HaarFilter { kind: 1, y: 2, height: 2, width: 9 }, quantizer: Quantizer { t0: -0.557409, t1: -0.233035, t2: 0.0534525 } },
98    Classifier { filter: HaarFilter { kind: 2, y: 7, height: 3, width: 4 }, quantizer: Quantizer { t0: -0.0646826, t1: 0.00620476, t2: 0.0784847 } },
99    Classifier { filter: HaarFilter { kind: 2, y: 6, height: 2, width: 16 }, quantizer: Quantizer { t0: -0.192387, t1: -0.029699, t2: 0.215855 } },
100    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 3, width: 2 }, quantizer: Quantizer { t0: -0.0397818, t1: -0.00568076, t2: 0.0292026 } },
101    Classifier { filter: HaarFilter { kind: 5, y: 10, height: 1, width: 15 }, quantizer: Quantizer { t0: -0.53823, t1: -0.369934, t2: -0.190235 } },
102    Classifier { filter: HaarFilter { kind: 3, y: 6, height: 2, width: 10 }, quantizer: Quantizer { t0: -0.124877, t1: 0.0296483, t2: 0.139239 } },
103    Classifier { filter: HaarFilter { kind: 2, y: 1, height: 1, width: 14 }, quantizer: Quantizer { t0: -0.101475, t1: 0.0225617, t2: 0.231971 } },
104    Classifier { filter: HaarFilter { kind: 3, y: 5, height: 6, width: 4 }, quantizer: Quantizer { t0: -0.0799915, t1: -0.00729616, t2: 0.063262 } },
105    Classifier { filter: HaarFilter { kind: 1, y: 9, height: 2, width: 12 }, quantizer: Quantizer { t0: -0.272556, t1: 0.019424, t2: 0.302559 } },
106    Classifier { filter: HaarFilter { kind: 3, y: 4, height: 2, width: 14 }, quantizer: Quantizer { t0: -0.164292, t1: -0.0321188, t2: 0.0846339 } },
107];
108
109pub static CONFIG_TEST1: Config = Config {
110    classifiers: &CLASSIFIERS_TEST1,
111    filter_coefficients: CHROMA_FILTER_COEFFICIENTS,
112    interpolate: false,
113    remove_silence: false,
114    silence_threshold: 0,
115    frame_size: DEFAULT_FRAME_SIZE,
116    frame_overlap: DEFAULT_FRAME_OVERLAP,
117    sample_rate: DEFAULT_SAMPLE_RATE,
118};
119
120pub static CONFIG_TEST2: Config = Config {
121    classifiers: &CLASSIFIERS_TEST2,
122    filter_coefficients: CHROMA_FILTER_COEFFICIENTS,
123    interpolate: false,
124    remove_silence: false,
125    silence_threshold: 0,
126    frame_size: DEFAULT_FRAME_SIZE,
127    frame_overlap: DEFAULT_FRAME_OVERLAP,
128    sample_rate: DEFAULT_SAMPLE_RATE,
129};
130
131pub static CONFIG_TEST3: Config = Config {
132    classifiers: &CLASSIFIERS_TEST3,
133    filter_coefficients: CHROMA_FILTER_COEFFICIENTS,
134    // NOTE: C sets interpolate(true) in config, but fingerprinter.cpp never calls
135    // m_chroma->set_interpolate(true) (it's commented out on line 32), so interpolate
136    // is effectively always false. We match that behavior here.
137    interpolate: false,
138    remove_silence: false,
139    silence_threshold: 0,
140    frame_size: DEFAULT_FRAME_SIZE,
141    // NOTE: C's FingerprinterConfigurationTest3 constructor never calls set_frame_overlap(),
142    // so it stays at the base class default of 0. We match that bug here.
143    frame_overlap: 0,
144    sample_rate: DEFAULT_SAMPLE_RATE,
145};
146
147pub static CONFIG_TEST4: Config = Config {
148    classifiers: &CLASSIFIERS_TEST2,
149    filter_coefficients: CHROMA_FILTER_COEFFICIENTS,
150    interpolate: false,
151    remove_silence: true,
152    silence_threshold: 50,
153    frame_size: DEFAULT_FRAME_SIZE,
154    frame_overlap: DEFAULT_FRAME_OVERLAP,
155    sample_rate: DEFAULT_SAMPLE_RATE,
156};
157
158pub static CONFIG_TEST5: Config = Config {
159    classifiers: &CLASSIFIERS_TEST2,
160    filter_coefficients: CHROMA_FILTER_COEFFICIENTS,
161    interpolate: false,
162    remove_silence: false,
163    silence_threshold: 0,
164    frame_size: DEFAULT_FRAME_SIZE / 2,
165    frame_overlap: DEFAULT_FRAME_SIZE / 2 - DEFAULT_FRAME_SIZE / 4,
166    sample_rate: DEFAULT_SAMPLE_RATE,
167};
168
169/// Get the configuration for a given algorithm.
170pub fn get_config(algorithm: crate::types::Algorithm) -> &'static Config {
171    match algorithm {
172        crate::types::Algorithm::Test1 => &CONFIG_TEST1,
173        crate::types::Algorithm::Test2 => &CONFIG_TEST2,
174        crate::types::Algorithm::Test3 => &CONFIG_TEST3,
175        crate::types::Algorithm::Test4 => &CONFIG_TEST4,
176        crate::types::Algorithm::Test5 => &CONFIG_TEST5,
177    }
178}