audio_processor_utility/
gain.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23use audio_garbage_collector::{make_shared, Shared};
24use std::marker::PhantomData;
25use std::ops::Mul;
26
27use audio_processor_traits::simple_processor::MonoAudioProcessor;
28use audio_processor_traits::{AtomicF32, AudioContext, Float};
29
30pub struct GainProcessorHandle {
31    gain: AtomicF32,
32}
33
34impl GainProcessorHandle {
35    pub fn new(gain: impl Into<f32>) -> Self {
36        Self {
37            gain: AtomicF32::new(gain.into()),
38        }
39    }
40
41    pub fn set_gain(&self, gain: impl Into<f32>) {
42        self.gain.set(gain.into());
43    }
44
45    pub fn gain(&self) -> f32 {
46        self.gain.get()
47    }
48}
49
50/// An `AudioProcessor` which applies gain to an input signal
51pub struct GainProcessor<SampleType> {
52    handle: Shared<GainProcessorHandle>,
53    phantom: PhantomData<SampleType>,
54}
55
56impl<SampleType> Default for GainProcessor<SampleType> {
57    /// Construct a `GainProcessor` with 1.0 gain
58    fn default() -> Self {
59        Self::new(1.0)
60    }
61}
62
63impl<SampleType> GainProcessor<SampleType> {
64    /// Construct a `GainProcessor` with gain
65    pub fn new(gain: impl Into<f32>) -> Self {
66        Self::new_with_handle(make_shared(GainProcessorHandle::new(gain)))
67    }
68
69    /// Construct a `GainProcessor` with a certain `GainProcessorHandle`
70    pub fn new_with_handle(handle: Shared<GainProcessorHandle>) -> Self {
71        Self {
72            handle,
73            phantom: PhantomData::default(),
74        }
75    }
76
77    /// Change the gain
78    pub fn set_gain(&self, gain: impl Into<f32>) {
79        self.handle.set_gain(gain)
80    }
81
82    /// Get the gain
83    pub fn gain(&self) -> f32 {
84        self.handle.gain()
85    }
86
87    pub fn handle(&self) -> &Shared<GainProcessorHandle> {
88        &self.handle
89    }
90}
91
92impl<SampleType> MonoAudioProcessor for GainProcessor<SampleType>
93where
94    SampleType: Float + Send + Sync + Mul<Output = SampleType>,
95{
96    type SampleType = SampleType;
97
98    fn m_process(&mut self, _context: &mut AudioContext, sample: SampleType) -> SampleType {
99        SampleType::from(self.gain()).unwrap() * sample
100    }
101}
102
103#[cfg(test)]
104mod test {
105    use audio_processor_testing_helpers::assert_f_eq;
106    use audio_processor_traits::{simple_processor, AudioBuffer};
107
108    use super::*;
109
110    #[test]
111    fn test_gain_does_its_thing() {
112        let mut gain = GainProcessor::new(0.8);
113        let input = [1., 1., 1., 1., 1., 1.];
114        let mut input = AudioBuffer::from_interleaved(1, &input);
115
116        let mut context = AudioContext::default();
117        simple_processor::process_buffer(&mut context, &mut gain, &mut input);
118
119        for sample in input.channel(0) {
120            assert_f_eq!(*sample, 0.8);
121        }
122    }
123
124    #[test]
125    fn test_gain_can_be_changed() {
126        let mut gain = GainProcessor::default();
127        gain.set_gain(0.8);
128        let input = [1., 1., 1., 1., 1., 1.];
129        let mut input = AudioBuffer::from_interleaved(1, &input);
130
131        let mut context = AudioContext::default();
132        simple_processor::process_buffer(&mut context, &mut gain, &mut input);
133
134        for sample in input.channel(0) {
135            assert_f_eq!(*sample, 0.8);
136        }
137    }
138}