Coverage Report

Created: 2026-08-08 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/adhd/audio_processor/src/processor.rs
Line
Count
Source
1
// Copyright 2022 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use crate::shape::Format;
6
use crate::MultiSlice;
7
use crate::Sample;
8
9
#[derive(thiserror::Error, Debug)]
10
pub enum Error {
11
    #[error(
12
        "{}: want {want_channels}x{want_frames}; got {got_channels}x{got_frames}",
13
        "invalid channels x frames"
14
    )]
15
    InvalidShape {
16
        want_channels: usize,
17
        want_frames: usize,
18
        got_channels: usize,
19
        got_frames: usize,
20
    },
21
    #[error("error in hound: {0:?}")]
22
    Wav(hound::Error),
23
    #[error("error in plugin: {0}")]
24
    Plugin(#[from] crate::processors::PluginError),
25
    #[error("unrecoverable error: {0:#}")]
26
    Unrecoverable(anyhow::Error),
27
}
28
29
impl From<anyhow::Error> for Error {
30
0
    fn from(value: anyhow::Error) -> Self {
31
0
        Self::Unrecoverable(value)
32
0
    }
33
}
34
35
pub type Result<T> = std::result::Result<T, Error>;
36
37
/// A ByteProcessor processes multiple slices of bytes.
38
/// Each iteration the `process_bytes` function is called.
39
pub trait ByteProcessor {
40
    /// Process audio pointed by `input`. Return the result.
41
    ///
42
    /// To implement an in-place processor, modify the input directly and return
43
    /// it.
44
    ///
45
    /// To implement an non in-place processor, store the output on memory owned
46
    /// by the processor itself, then return a [`MultiSlice`] referencing the memory
47
    /// owned by the processor.
48
    fn process_bytes<'a>(&'a mut self, input: MultiSlice<'a, u8>) -> Result<MultiSlice<'a, u8>>;
49
}
50
51
/// Convenience trait to ease implementing [`ByteProcessor`]s.
52
/// The input and output types are casted from `u8` to `I`, and `O` to `u8` automatically.
53
///
54
/// Prefer implementing [`AudioProcessor`] when the input and output types are
55
/// known at compile time. Otherwise implement [`ByteProcessor`].
56
pub trait AudioProcessor {
57
    /// Input type.
58
    type I: Sample;
59
    /// Output type.
60
    type O: Sample;
61
62
    /// Process audio pointed by `input`. Return the result.
63
    /// See also [`ByteProcessor::process_bytes`].
64
    fn process<'a>(&'a mut self, input: MultiSlice<'a, Self::I>)
65
        -> Result<MultiSlice<'a, Self::O>>;
66
67
0
    fn process_bytes<'a>(&'a mut self, input: MultiSlice<'a, u8>) -> Result<MultiSlice<'a, u8>> {
68
0
        self.process(input.into_typed()).map(|x| x.into_bytes())
Unexecuted instantiation: <audio_processor::processors::speex::SpeexResampler as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::negate::InPlaceNegateAudioProcessor<f32> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::peer::managed::ManagedBlockingSeqPacketProcessor as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::chunk_wrapper::ChunkWrapper<audio_processor::pipeline::Pipeline, f32> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::profile::Profile<audio_processor::processors::plugin::dynamic::DynamicPluginProcessor> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::shuffle_channels::ShuffleChannels as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::plugin::dynamic::DynamicPluginProcessor as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::wav::WavSink<std::io::buffered::bufwriter::BufWriter<std::fs::File>> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <cras_processor::CrasProcessor as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::check_shape::CheckShape<f32> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::thread::ThreadedProcessor<cras_processor::CrasProcessor> as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
Unexecuted instantiation: <audio_processor::processors::plugin::processor::PluginProcessor as audio_processor::processor::AudioProcessor>::process_bytes::{closure#0}
69
0
    }
Unexecuted instantiation: <audio_processor::processors::speex::SpeexResampler as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::negate::InPlaceNegateAudioProcessor<f32> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::peer::managed::ManagedBlockingSeqPacketProcessor as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::chunk_wrapper::ChunkWrapper<audio_processor::pipeline::Pipeline, f32> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::profile::Profile<audio_processor::processors::plugin::dynamic::DynamicPluginProcessor> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::shuffle_channels::ShuffleChannels as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::plugin::dynamic::DynamicPluginProcessor as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::wav::WavSink<std::io::buffered::bufwriter::BufWriter<std::fs::File>> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <cras_processor::CrasProcessor as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::check_shape::CheckShape<f32> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::thread::ThreadedProcessor<cras_processor::CrasProcessor> as audio_processor::processor::AudioProcessor>::process_bytes
Unexecuted instantiation: <audio_processor::processors::plugin::processor::PluginProcessor as audio_processor::processor::AudioProcessor>::process_bytes
70
71
    /// Get the `Format` of the `process`ed output.
72
    fn get_output_format(&self) -> Format;
73
}
74
75
impl<T> ByteProcessor for T
76
where
77
    T: AudioProcessor,
78
{
79
0
    fn process_bytes<'a>(&'a mut self, input: MultiSlice<'a, u8>) -> Result<MultiSlice<'a, u8>> {
80
0
        self.process_bytes(input)
81
0
    }
82
}
83
84
#[cfg(test)]
85
mod tests {
86
    use crate::processors;
87
    use crate::ByteProcessor;
88
    use crate::Format;
89
    use crate::MultiBuffer;
90
91
    #[test]
92
    fn simple_pipeline() {
93
        let format = Format {
94
            channels: 2,
95
            block_size: 4,
96
            frame_rate: 48000,
97
        };
98
        // Test a simple pipeline using a Vec of ByteProcessor.
99
        let mut p1 = processors::InPlaceNegateAudioProcessor::<f32>::new(format);
100
        let mut p2 = processors::NegateAudioProcessor::<f32>::new(format);
101
        let mut pipeline: Vec<&mut dyn ByteProcessor> = vec![&mut p1, &mut p2];
102
103
        let mut bufs = MultiBuffer::<f32>::from(vec![vec![1., 2., 3., 4.], vec![5., 6., 7., 8.]]);
104
        let mut slices = bufs.as_multi_slice().into_bytes();
105
106
        for p in pipeline.iter_mut() {
107
            slices = p.process_bytes(slices).unwrap();
108
        }
109
110
        // Y = -(-X) = X
111
        assert_eq!(
112
            slices.into_typed::<f32>().into_raw(),
113
            [[1., 2., 3., 4.], [5., 6., 7., 8.]]
114
        );
115
116
        // Y = -X; p2 does not modify the input data
117
        assert_eq!(bufs.to_vecs(), [[-1., -2., -3., -4.], [-5., -6., -7., -8.]]);
118
    }
119
}