sim_lib_audio_dsp/
oversampling.rs1use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
2
3use crate::common::{input_sample, output_channels, prepare_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
71impl OversamplingWrapper<TanhClipper> {
72 pub fn soft_clipper(drive: f32, factor: u8) -> Self {
74 Self::new(TanhClipper::new(drive), factor)
75 }
76}
77
78impl<P: NonlinearSampleProcessor> Processor for OversamplingWrapper<P> {
79 fn prepare(&mut self, cfg: PrepareConfig) {
80 prepare_channels(
81 &mut self.processors,
82 cfg.out_channels as usize,
83 self.prototype.clone(),
84 );
85 prepare_channels(&mut self.previous_inputs, cfg.out_channels as usize, 0.0);
86 }
87
88 fn reset(&mut self) {
89 self.previous_inputs.fill(0.0);
90 for processor in &mut self.processors {
91 processor.reset();
92 }
93 }
94
95 fn process(&mut self, block: &mut ProcessBlock<'_>) {
96 let channels = output_channels(block);
97 if self.processors.len() < channels {
98 self.processors.resize(channels, self.prototype.clone());
99 self.previous_inputs.resize(channels, 0.0);
100 }
101 let frames = block.frames as usize;
102 for channel in 0..channels {
103 for frame in 0..frames {
104 let input = input_sample(block, channel, frame);
105 block.out_audio[channel][frame] = self.process_channel_sample(channel, input);
106 }
107 }
108 }
109}
110
111pub type OversampledSoftClipper = OversamplingWrapper<TanhClipper>;