adk_audio/traits/processor.rs
1//! Audio processor trait and FxChain composition.
2
3use async_trait::async_trait;
4
5use crate::error::AudioResult;
6use crate::frame::AudioFrame;
7
8/// Trait for stateless or stateful DSP transforms on audio frames.
9///
10/// Implementors include normalizers, resamplers, noise suppressors,
11/// compressors, and the `FxChain` itself (enabling nested chains).
12#[async_trait]
13pub trait AudioProcessor: Send + Sync {
14 /// Process a single audio frame, returning the transformed result.
15 async fn process(&self, frame: &AudioFrame) -> AudioResult<AudioFrame>;
16}
17
18/// An ordered chain of `AudioProcessor` stages applied in series.
19///
20/// The output of stage N becomes the input to stage N+1.
21/// An empty chain returns the input frame unchanged.
22///
23/// # Example
24///
25/// ```ignore
26/// let chain = FxChain::new()
27/// .push(normalizer)
28/// .push(resampler);
29/// let output = chain.process(&input).await?;
30/// ```
31pub struct FxChain {
32 stages: Vec<Box<dyn AudioProcessor>>,
33}
34
35impl FxChain {
36 /// Create an empty FxChain.
37 pub fn new() -> Self {
38 Self { stages: Vec::new() }
39 }
40
41 /// Append a processing stage to the chain.
42 pub fn push(mut self, processor: impl AudioProcessor + 'static) -> Self {
43 self.stages.push(Box::new(processor));
44 self
45 }
46
47 /// Returns the number of stages in the chain.
48 pub fn len(&self) -> usize {
49 self.stages.len()
50 }
51
52 /// Returns true if the chain has no stages.
53 pub fn is_empty(&self) -> bool {
54 self.stages.is_empty()
55 }
56}
57
58impl Default for FxChain {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64#[async_trait]
65impl AudioProcessor for FxChain {
66 async fn process(&self, frame: &AudioFrame) -> AudioResult<AudioFrame> {
67 let mut current = frame.clone();
68 for stage in &self.stages {
69 current = stage.process(¤t).await?;
70 }
71 Ok(current)
72 }
73}