sim_lib_audio_dsp/
oversampling.rs1use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
2
3use crate::common::{input_sample, prepare_channels, prepared_output_channels};
4
5pub trait NonlinearSampleProcessor: Clone + Send {
7 fn reset(&mut self);
9 fn process_sample(&mut self, input: f32) -> f32;
11}
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct TanhClipper {
16 drive: f32,
17}
18
19impl TanhClipper {
20 pub fn new(drive: f32) -> Self {
22 Self {
23 drive: drive.max(0.0),
24 }
25 }
26}
27
28impl NonlinearSampleProcessor for TanhClipper {
29 fn reset(&mut self) {}
30
31 fn process_sample(&mut self, input: f32) -> f32 {
32 (input * self.drive).tanh()
33 }
34}
35
36#[derive(Clone, Debug, PartialEq)]
39pub struct OversamplingWrapper<P: NonlinearSampleProcessor> {
40 prototype: P,
41 processors: Vec<P>,
42 previous_inputs: Vec<f32>,
43 factor: u8,
44}
45
46impl<P: NonlinearSampleProcessor> OversamplingWrapper<P> {
47 pub fn new(processor: P, factor: u8) -> Self {
50 Self {
51 prototype: processor,
52 processors: Vec::new(),
53 previous_inputs: Vec::new(),
54 factor: factor.clamp(1, 16),
55 }
56 }
57
58 fn process_channel_sample(&mut self, channel: usize, input: f32) -> f32 {
59 let previous = self.previous_inputs[channel];
60 let mut output = 0.0;
61 for step in 1..=self.factor {
62 let t = step as f32 / self.factor as f32;
63 let upsampled = previous + (input - previous) * t;
64 output = self.processors[channel].process_sample(upsampled);
65 }
66 self.previous_inputs[channel] = input;
67 output
68 }
69
70 #[cfg(all(test, not(debug_assertions)))]
71 pub(crate) fn realtime_state_snapshot(&self) -> Vec<usize> {
72 vec![self.processors.capacity(), self.previous_inputs.capacity()]
73 }
74}
75
76impl OversamplingWrapper<TanhClipper> {
77 pub fn soft_clipper(drive: f32, factor: u8) -> Self {
79 Self::new(TanhClipper::new(drive), factor)
80 }
81}
82
83impl<P: NonlinearSampleProcessor> Processor for OversamplingWrapper<P> {
84 fn prepare(&mut self, cfg: PrepareConfig) {
85 prepare_channels(
86 &mut self.processors,
87 cfg.out_channels as usize,
88 self.prototype.clone(),
89 );
90 prepare_channels(&mut self.previous_inputs, cfg.out_channels as usize, 0.0);
91 }
92
93 fn reset(&mut self) {
94 self.previous_inputs.fill(0.0);
95 for processor in &mut self.processors {
96 processor.reset();
97 }
98 }
99
100 fn process(&mut self, block: &mut ProcessBlock<'_>) {
101 let channels =
102 prepared_output_channels(block, self.processors.len(), "OversamplingWrapper");
103 let frames = block.frames as usize;
104 for channel in 0..channels {
105 for frame in 0..frames {
106 let input = input_sample(block, channel, frame);
107 block.out_audio[channel][frame] = self.process_channel_sample(channel, input);
108 }
109 }
110 }
111}
112
113pub type OversampledSoftClipper = OversamplingWrapper<TanhClipper>;