/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 | | } |