audio_processor_utility/
pan.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_processor_traits::{AudioBuffer, AudioProcessor};
24use audio_processor_traits::{AudioContext, Float};
25
26/// An `AudioProcessor` that applies panning on its input.
27///
28/// Does not perform any bounds checking.
29pub struct PanProcessor<SampleType> {
30    /// A number between -1 and 1
31    /// -1 represents using the left channel only, 1 represents using the right channel only.
32    panning: SampleType,
33}
34
35impl<SampleType: Float> Default for PanProcessor<SampleType> {
36    fn default() -> Self {
37        Self::new(SampleType::from(0.0).unwrap())
38    }
39}
40
41impl<SampleType: Float> PanProcessor<SampleType> {
42    /// Create a processor with panning.
43    /// -1 represents using the left channel only, 1 represents using the right channel only.
44    pub fn new(panning: SampleType) -> Self {
45        PanProcessor { panning }
46    }
47
48    /// -1 represents using the left channel only, 1 represents using the right channel only.
49    pub fn panning(&self) -> SampleType {
50        self.panning
51    }
52
53    /// Set the panning.
54    ///
55    /// -1 represents using the left channel only, 1 represents using the right channel only.
56    pub fn set_panning(&mut self, panning: SampleType) {
57        self.panning = panning;
58    }
59}
60
61impl<SampleType> AudioProcessor for PanProcessor<SampleType>
62where
63    SampleType: Float + Sync + Send,
64{
65    type SampleType = SampleType;
66
67    fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<SampleType>) {
68        for sample_num in 0..buffer.num_samples() {
69            let zero = SampleType::zero();
70            let one = SampleType::one();
71            let panning = self.panning;
72
73            let left_input = *buffer.get(0, sample_num);
74            let right_input = *buffer.get(0, sample_num);
75
76            if panning > zero {
77                let left_output = left_input * (one - panning);
78                let right_output = right_input + left_input * panning;
79
80                buffer.set(0, sample_num, left_output);
81                buffer.set(1, sample_num, right_output);
82            } else if panning < zero {
83                let left_output = left_input + right_input * (-panning);
84                let right_output = right_input * (one + panning);
85
86                buffer.set(0, sample_num, left_output);
87                buffer.set(1, sample_num, right_output);
88            }
89        }
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use audio_processor_testing_helpers::assert_f_eq;
96    use audio_processor_traits::AudioBuffer;
97
98    use super::*;
99
100    #[test]
101    fn test_pan_noop() {
102        let mut pan = PanProcessor::default();
103        let samples = [1., 1., 1., 1., 1., 1.];
104        let mut input = AudioBuffer::from_interleaved(2, &samples);
105        let mut context = AudioContext::default();
106
107        pan.process(&mut context, &mut input);
108
109        for sample_num in 0..input.num_samples() {
110            for channel_num in 0..input.num_channels() {
111                let sample = input.get(channel_num, sample_num);
112                assert_f_eq!(*sample, 1.);
113            }
114        }
115    }
116
117    #[test]
118    fn test_hard_pan_to_left() {
119        let mut pan = PanProcessor::new(-1.0);
120        let samples = [1., 1., 1., 1., 1., 1.];
121        let mut input = AudioBuffer::from_interleaved(2, &samples);
122        let mut context = AudioContext::default();
123
124        pan.process(&mut context, &mut input);
125
126        for sample_index in 0..input.num_samples() {
127            let left = *input.get(0, sample_index);
128            let right = *input.get(1, sample_index);
129            assert_f_eq!(left, 2.0);
130            assert_f_eq!(right, 0.0);
131        }
132    }
133
134    #[test]
135    fn test_hard_pan_to_right() {
136        let mut pan = PanProcessor::new(1.0);
137        let samples = [1., 1., 1., 1., 1., 1.];
138        let mut input = AudioBuffer::from_interleaved(2, &samples);
139        let mut context = AudioContext::default();
140
141        pan.process(&mut context, &mut input);
142
143        for sample_index in 0..input.num_samples() {
144            let left = *input.get(0, sample_index);
145            let right = *input.get(1, sample_index);
146            assert_f_eq!(right, 2.0);
147            assert_f_eq!(left, 0.0);
148        }
149    }
150}