euphony_dsp/osc/
nes.rs

1use crate::prelude::*;
2use core::num::Wrapping;
3
4#[derive(Copy, Clone, Debug, Default)]
5pub struct Phase(Wrapping<u16>);
6
7impl Phase {
8    #[inline]
9    pub fn set(&mut self, phase: f64) {
10        self.0 = Wrapping((phase.fract().abs() * u16::MAX as f64) as u16);
11    }
12
13    #[inline(always)]
14    fn next(&mut self, freq: f64) -> u16 {
15        let value = self.0;
16        let step = (Rate::PERIOD * u16::MAX as f64 * freq) as u16;
17        self.0 += step.max(1);
18        value.0
19    }
20}
21
22#[derive(Debug, Clone, Copy, Default, Node)]
23#[node(id = 108, module = "osc::nes")]
24#[input(frequency, default = 440.0)]
25#[input(duty_cycle, default = 0.0)]
26#[input(decay, default = 0.001)]
27#[input(phase, trigger = set_phase)]
28pub struct Pulse {
29    phase: Phase,
30    is_positive: bool,
31    value: f64,
32}
33
34// From http://wiki.nesdev.com/w/index.php/APU_Pulse
35const SQUARE_DUTY_TABLE: [[u8; 8]; 4] = [
36    [0, 1, 0, 0, 0, 0, 0, 0],
37    [0, 1, 1, 0, 0, 0, 0, 0],
38    [0, 1, 1, 1, 1, 0, 0, 0],
39    [1, 0, 0, 1, 1, 1, 1, 1],
40];
41
42impl Pulse {
43    #[inline]
44    pub fn set_phase(&mut self, phase: f64) {
45        self.phase.set(phase);
46    }
47
48    #[inline(always)]
49    fn next(&mut self, freq: f64, duty_cycle: f64, decay: f64) -> f64 {
50        let sample = self.phase.next(freq);
51
52        if sample < 8 {
53            return f64::EQUILIBRIUM;
54        }
55
56        let mut duty_cycle = duty_cycle as usize;
57        duty_cycle %= SQUARE_DUTY_TABLE.len();
58        let duty_cycle = SQUARE_DUTY_TABLE[duty_cycle];
59
60        let counter = sample as usize / (u16::MAX as usize / 8 + 1);
61        let value = duty_cycle[counter];
62
63        let is_positive = value == 1;
64        // update the direction
65        if self.is_positive != is_positive {
66            self.is_positive = is_positive;
67            self.value = if is_positive { 1.0 } else { -1.0 };
68        }
69
70        let value = self.value;
71        let diff = 0.0 - value;
72        let samples = (decay * Rate::VALUE).round();
73        let step = diff / samples;
74
75        if value.abs() > step {
76            self.value += step;
77        } else {
78            self.value = 0.0;
79        }
80
81        value
82    }
83
84    #[inline]
85    pub fn render(
86        &mut self,
87        frequency: Input,
88        duty_cycle: Input,
89        decay: Input,
90        output: &mut [Sample],
91    ) {
92        for (freq, duty_cycle, decay, frame) in
93            (frequency, duty_cycle, decay, output.iter_mut()).zip()
94        {
95            *frame = self.next(freq, duty_cycle, decay);
96        }
97    }
98}
99
100#[derive(Debug, Clone, Copy, Default, Node)]
101#[node(id = 109, module = "osc::nes")]
102#[input(frequency, default = 440.0)]
103#[input(phase, trigger = set_phase)]
104pub struct Triangle {
105    phase: super::Phase,
106}
107
108impl Triangle {
109    #[inline]
110    pub fn set_phase(&mut self, phase: f64) {
111        self.phase.set(phase);
112    }
113
114    #[inline(always)]
115    fn next(&mut self, freq: f64) -> f64 {
116        const VAR_TABLE: [isize; 32] = {
117            let mut table = [0; 32];
118
119            let magnitude = 4;
120
121            let mut idx = 0;
122            while idx < 16 {
123                let i = match idx {
124                    0..=2 => -2,
125                    3..=5 => -1,
126                    10..=12 => 1,
127                    13..=15 => 2,
128                    _ => 0,
129                } * magnitude;
130
131                table[idx] += i;
132                table[idx + 16] += i;
133                idx += 1;
134            }
135
136            table
137        };
138
139        const TABLE: [f64; 512] = {
140            let mut table = [0.0; 512];
141
142            let mut idx = 0;
143            let mut phase = 8;
144            while idx < table.len() {
145                let mut sample = phase;
146                let entries = VAR_TABLE[sample as usize];
147                let mut entries = (entries + (table.len() / 32) as isize) as usize;
148
149                if sample & 0x10 == 0x10 {
150                    sample ^= 0x1f;
151                }
152
153                let value = (sample as f64 - 7.5) / 7.5;
154
155                while entries > 0 {
156                    table[idx] = value;
157                    idx += 1;
158                    entries -= 1;
159                }
160
161                phase += 1;
162                phase %= 32;
163            }
164
165            table
166        };
167
168        let phase = self.phase.next(freq);
169        let idx = (phase * TABLE.len() as f64) as usize;
170        TABLE[idx]
171    }
172
173    #[inline]
174    pub fn render(&mut self, frequency: Input, output: &mut [Sample]) {
175        for (freq, frame) in (frequency, output.iter_mut()).zip() {
176            *frame = self.next(freq);
177        }
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    #[test]
186    fn pulse_test() {
187        let mut osc = Pulse::new();
188        let mut samples = [0.0; 500];
189        osc.render(440.0.into(), 2.0.into(), 0.001.into(), &mut samples);
190
191        eprintln!("{:?}", samples);
192        //panic!();
193    }
194
195    #[test]
196    fn triangle_test() {
197        let mut osc = Triangle::new();
198        let mut samples = [0.0; 500];
199        osc.render((480.0).into(), &mut samples);
200
201        eprintln!("{:?}", samples);
202        //panic!();
203    }
204}