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
34const 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 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 }
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 }
204}