Skip to main content

audiofp/
fp.rs

1//! Fingerprinter traits.
2//!
3//! Two traits cover the two ways `audiofp` produces fingerprints:
4//!
5//! - [`Fingerprinter`] — feed a whole [`AudioBuffer`] and get its full
6//!   output. Suited to enrolment / batch jobs.
7//! - [`StreamingFingerprinter`] — push samples as they arrive and receive
8//!   fingerprints whenever the algorithm has enough material. Suited to
9//!   live capture.
10//!
11//! Concrete implementations live in feature-gated modules
12//! (`fp_classical::Wang`, `neural::ResonaFp`, …).
13//!
14//! [`AudioBuffer`]: crate::AudioBuffer
15
16use alloc::vec::Vec;
17
18use crate::{AudioBuffer, Result, TimestampMs};
19
20/// Offline (whole-buffer) fingerprinter.
21///
22/// Implementations are stateful between calls only insofar as they may
23/// cache scratch buffers — the fingerprint of `extract(a)` does not depend
24/// on any previous call.
25///
26/// # Example
27///
28/// ```
29/// use audiofp::{AudioBuffer, Fingerprinter, Result, SampleRate};
30///
31/// /// A toy fingerprinter that just sums absolute samples.
32/// struct Energy;
33///
34/// impl Fingerprinter for Energy {
35///     type Output = f32;
36///     type Config = ();
37///
38///     fn name(&self) -> &'static str { "energy-v0" }
39///     fn config(&self) -> &Self::Config { &() }
40///     fn required_sample_rate(&self) -> u32 { 16_000 }
41///     fn min_samples(&self) -> usize { 16_000 }
42///     fn extract(&mut self, audio: AudioBuffer<'_>) -> Result<Self::Output> {
43///         Ok(audio.samples.iter().map(|s| s.abs()).sum())
44///     }
45/// }
46///
47/// let mut fp = Energy;
48/// let samples = vec![0.0_f32; 16_000];
49/// let buf = AudioBuffer { samples: &samples, rate: SampleRate::HZ_16000 };
50/// assert_eq!(fp.extract(buf).unwrap(), 0.0);
51/// ```
52pub trait Fingerprinter {
53    /// The fingerprint produced by this extractor (e.g. `Vec<WangHash>`).
54    type Output;
55
56    /// Per-instance configuration this fingerprinter exposes to callers.
57    type Config: Clone + Send + Sync;
58
59    /// Stable identifier for the algorithm and version, e.g. `"wang-v1"`.
60    /// Useful when persisting fingerprints alongside the producer name.
61    fn name(&self) -> &'static str;
62
63    /// Borrow the configuration this instance was built with.
64    fn config(&self) -> &Self::Config;
65
66    /// Sample rate, in hertz, the fingerprinter expects its input at.
67    /// Resampling is the caller's responsibility.
68    fn required_sample_rate(&self) -> u32;
69
70    /// Minimum buffer length, in samples, required to extract anything.
71    /// Calls with shorter inputs return [`AfpError::AudioTooShort`].
72    ///
73    /// [`AfpError::AudioTooShort`]: crate::AfpError::AudioTooShort
74    fn min_samples(&self) -> usize;
75
76    /// Compute the fingerprint of `audio`.
77    fn extract(&mut self, audio: AudioBuffer<'_>) -> Result<Self::Output>;
78}
79
80/// Streaming fingerprinter that emits zero-or-more frames per push.
81///
82/// Implementations must be **non-blocking** and **bounded-allocation**:
83/// any buffers needed for sustained operation are allocated at construction,
84/// not inside [`StreamingFingerprinter::push`]. This makes them suitable
85/// for invocation from realtime audio callbacks (when invoked through
86/// `audiofp`'s streaming orchestrator).
87///
88/// # Example
89///
90/// ```
91/// use audiofp::{StreamingFingerprinter, TimestampMs};
92///
93/// struct EveryThird { count: usize }
94///
95/// impl StreamingFingerprinter for EveryThird {
96///     type Frame = u32;
97///     fn push(&mut self, samples: &[f32]) -> Vec<(TimestampMs, u32)> {
98///         let mut out = Vec::new();
99///         for s in samples {
100///             self.count += 1;
101///             if self.count % 3 == 0 {
102///                 out.push((TimestampMs(self.count as u64), s.to_bits()));
103///             }
104///         }
105///         out
106///     }
107///     fn flush(&mut self) -> Vec<(TimestampMs, u32)> { Vec::new() }
108///     fn latency_ms(&self) -> u32 { 0 }
109/// }
110///
111/// let mut fp = EveryThird { count: 0 };
112/// assert_eq!(fp.push(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]).len(), 2);
113/// ```
114pub trait StreamingFingerprinter {
115    /// One unit of fingerprint material the stream emits.
116    type Frame;
117
118    /// Feed PCM samples and return any fingerprints that became available
119    /// during this push.
120    ///
121    /// Must not block and must not allocate beyond the per-instance
122    /// budget set at construction.
123    fn push(&mut self, samples: &[f32]) -> Vec<(TimestampMs, Self::Frame)>;
124
125    /// Drain any pending fingerprint material at end-of-stream.
126    fn flush(&mut self) -> Vec<(TimestampMs, Self::Frame)>;
127
128    /// Conservative upper bound on emission latency: from the time a
129    /// sample enters [`push`] to the time the fingerprint covering it
130    /// is returned.
131    ///
132    /// [`push`]: StreamingFingerprinter::push
133    fn latency_ms(&self) -> u32;
134}