augmented_oscillator/
wavetable.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 crate::Oscillator;
24
25/// Calculate the cursor step increment between samples.
26///
27/// This is the fraction of a length which will go through each sample tick
28pub fn get_cursor_step(frequency: f32, sample_rate: f32, table_len: f32) -> f32 {
29    table_len * frequency / sample_rate
30}
31
32/// Fetches a sample from the wave-table by performing linear interpolation
33pub fn get_interpolated(cursor: f32, table: &[f32]) -> f32 {
34    let c1 = cursor;
35    let diff = c1 - c1.floor();
36    let c1 = c1 as usize;
37    let mut c2 = c1 + 1;
38    if c2 >= table.len() {
39        c2 = 0;
40    }
41
42    let v1 = table[c1];
43    let v2 = table[c2];
44    v1 + diff * (v2 - v1)
45}
46
47pub struct WaveTableOscillator {
48    cursor: f32,
49    cursor_step: f32,
50    table: Vec<f32>,
51    table_len: f32,
52    sample_rate: f32,
53    frequency: f32,
54}
55
56impl WaveTableOscillator {
57    pub fn from_oscillator(mut oscillator: Oscillator<f32>, table_len: usize) -> Self {
58        let frequency = oscillator.get_frequency();
59        let sample_rate = oscillator.sample_rate;
60
61        oscillator.set_frequency(sample_rate / table_len as f32);
62
63        let table: Vec<f32> = (0..table_len).map(|_| oscillator.next_sample()).collect();
64
65        let mut result = Self::new(table);
66        result.set_sample_rate(sample_rate);
67        result.set_frequency(frequency);
68        result
69    }
70
71    pub fn new(table: Vec<f32>) -> Self {
72        let frequency = 440.0;
73        let sample_rate = 44100.0;
74        Self {
75            cursor: 0.0,
76            cursor_step: get_cursor_step(frequency, sample_rate, table.len() as f32),
77            sample_rate,
78            frequency,
79            table_len: table.len() as f32,
80            table,
81        }
82    }
83
84    pub fn frequency(&self) -> f32 {
85        self.frequency
86    }
87
88    pub fn sample_rate(&self) -> f32 {
89        self.sample_rate
90    }
91
92    pub fn table(&self) -> &[f32] {
93        &self.table
94    }
95
96    pub fn table_mut(&mut self) -> &mut [f32] {
97        &mut self.table
98    }
99
100    pub fn set_sample_rate(&mut self, value: f32) {
101        self.sample_rate = value;
102        self.update();
103    }
104
105    pub fn set_frequency(&mut self, value: f32) {
106        self.frequency = value;
107        self.update();
108    }
109
110    fn update(&mut self) {
111        let frequency = self.frequency;
112        let sample_rate = self.sample_rate;
113        let table_len = self.table_len;
114        let cursor_step = get_cursor_step(frequency, sample_rate, table_len);
115        self.cursor_step = cursor_step
116    }
117
118    pub fn tick(&mut self) {
119        let cursor = &mut self.cursor;
120        *cursor += self.cursor_step;
121        while *cursor >= self.table_len {
122            *cursor -= self.table_len;
123        }
124    }
125
126    pub fn tick_n(&mut self, samples: f32) {
127        let cursor = &mut self.cursor;
128        *cursor += self.cursor_step * samples;
129        while *cursor >= self.table_len {
130            *cursor -= self.table_len;
131        }
132    }
133
134    pub fn get(&self) -> f32 {
135        let cursor = self.cursor;
136        let table = &self.table;
137
138        get_interpolated(cursor, table)
139    }
140
141    pub fn next_sample(&mut self) -> f32 {
142        let result = self.get();
143        self.tick();
144        result
145    }
146}
147
148#[cfg(test)]
149mod test {
150    use crate::test_utils::generate_plot;
151
152    use super::*;
153
154    #[test]
155    fn test_get_cursor_step() {
156        let step = get_cursor_step(220.0, 44100.0, 512.0);
157        assert!((step - 2.554_195).abs() < f32::EPSILON);
158    }
159
160    #[test]
161    fn test_generate_plots() {
162        let root_path = format!("{}/src/wavetable.rs", env!("CARGO_MANIFEST_DIR"));
163        let mut oscillator = Oscillator::sine(44100.0);
164        oscillator.set_frequency(440.0);
165        let mut wave_table = WaveTableOscillator::from_oscillator(oscillator.clone(), 1000);
166
167        generate_plot(&root_path, || wave_table.next_sample(), "wave_table");
168        generate_plot(&root_path, || oscillator.next_sample(), "sine_oscillator");
169    }
170
171    #[test]
172    fn test_cursor_step_is_properly_set() {
173        let mut oscillator = Oscillator::sine(44100.0);
174        oscillator.set_frequency(440.0);
175        let mut wave_table = WaveTableOscillator::from_oscillator(oscillator.clone(), 1000);
176        wave_table.set_sample_rate(44100.0);
177        wave_table.set_frequency(440.0);
178        assert!((wave_table.cursor - 0.0).abs() < f32::EPSILON);
179        assert!((wave_table.frequency - 440.0).abs() < f32::EPSILON);
180        assert!((wave_table.sample_rate - 44100.0).abs() < f32::EPSILON);
181
182        let cursor_step = wave_table.cursor_step;
183        assert!(
184            (cursor_step - 9.977_324_5_f32).abs() < f32::EPSILON,
185            "{}",
186            cursor_step
187        );
188    }
189
190    #[test]
191    fn test_smoke_test_wave_table_error() {
192        let mut oscillator = Oscillator::sine(44100.0);
193        oscillator.set_frequency(440.0);
194        let mut wave_table = WaveTableOscillator::from_oscillator(oscillator.clone(), 4000);
195        wave_table.set_sample_rate(44100.0);
196        wave_table.set_frequency(440.0);
197        let oscillator_result: Vec<f32> = (0..44100).map(|_| oscillator.next_sample()).collect();
198        let wave_table_result: Vec<f32> = (0..44100).map(|_| wave_table.next_sample()).collect();
199        for (o, w) in oscillator_result.iter().zip(wave_table_result.iter()) {
200            assert!((o - w).abs() < 0.01)
201        }
202    }
203}