1use crate::consts::{MAX_OVERLAPPING_NOTES, NUM_CHANNELS, NUM_INSTRUMENTS, PATTERN_LENGTH};
2use crate::song::{Envelope, Filter, Instrument, Song, Waveform};
3use arrayvec::ArrayVec;
4use core::{f32::consts::PI, num::Wrapping as w};
5use randomize::{Gen32 as _, PCG32};
6
7#[derive(Debug)]
14pub struct Synth<'a> {
15 song: &'a Song,
16 random: PCG32,
17 sample_rate: f32,
18 sample_ratio: f32,
19 quarter_note_length: u32,
20 eighth_note_length: u32,
21
22 seq_count: usize,
26 note_count: usize,
27 sample_count: u32,
28 tracks: [TrackState; NUM_INSTRUMENTS],
29}
30
31#[derive(Debug)]
33struct TrackState {
34 env: Envelope,
35
36 notes: [Note; MAX_OVERLAPPING_NOTES],
38
39 delay_samples: u32,
40 delay_count: u32,
41
42 pan_freq: f32,
44 lfo_freq: f32,
45}
46
47#[derive(Debug)]
51struct Note {
52 pitch: u8,
53 sample_count: u32,
54 volume: f32,
55 swap_stereo: bool,
56
57 osc_freq: [f32; 2],
59 osc_time: [f32; 2],
60 low: f32,
61 band: f32,
62}
63
64fn osc_sin(value: f32) -> f32 {
66 libm::sinf((value + 0.5) * PI * 2.0)
67}
68
69fn osc_square(value: f32) -> f32 {
71 if osc_sin(value) < 0.0 {
72 -1.0
73 } else {
74 1.0
75 }
76}
77
78fn osc_saw(value: f32) -> f32 {
80 let fract = value - libm::truncf(value);
81 (1.0 - fract) - 0.5
82}
83
84fn osc_tri(value: f32) -> f32 {
86 let fract = value - libm::truncf(value);
87 let v2 = fract * 4.0;
88
89 if v2 < 2.0 {
90 v2 - 1.0
91 } else {
92 3.0 - v2
93 }
94}
95
96fn get_frequency(ref_freq: f32, semitone: f32, note: u8, ref_pitch: u8) -> f32 {
100 ref_freq * libm::powf(semitone, f32::from(note) - f32::from(ref_pitch))
101}
102
103fn get_note_frequency(note: u8) -> f32 {
105 const SEMITONE: f32 = 1.059_463_1; get_frequency(1.0 / 256.0, SEMITONE, note, 128)
107}
108
109fn get_osc_output(waveform: &Waveform, t: f32) -> f32 {
111 match waveform {
112 Waveform::Sine => osc_sin(t),
113 Waveform::Square => osc_square(t),
114 Waveform::Saw => osc_saw(t),
115 Waveform::Triangle => osc_tri(t),
116 }
117}
118
119impl TrackState {
120 fn new() -> Self {
121 let mut notes = ArrayVec::new();
122 for _ in 0..MAX_OVERLAPPING_NOTES {
123 notes.push(Note::new(0, 0, 0.0, false));
124 }
125 let notes = notes.into_inner().unwrap();
126
127 Self {
128 env: Envelope {
129 attack: 0,
130 sustain: 0,
131 release: 0,
132 master: 0.0,
133 },
134 notes,
135 delay_samples: 0,
136 delay_count: 0,
137 pan_freq: 0.0,
138 lfo_freq: 0.0,
139 }
140 }
141}
142
143impl Note {
144 fn new(pitch: u8, sample_count: u32, volume: f32, swap_stereo: bool) -> Self {
145 Self {
146 pitch,
147 sample_count,
148 volume,
149 swap_stereo,
150 osc_freq: [0.0; 2],
151 osc_time: [0.0; 2],
152 low: 0.0,
153 band: 0.0,
154 }
155 }
156}
157
158impl<'a> Synth<'a> {
159 #[must_use]
185 pub fn new(song: &'a Song, seed: (u64, u64), sample_rate: f32) -> Self {
186 let random = PCG32::new(seed.0, seed.1);
187 let sample_ratio = sample_rate / 44100.0;
188 let quarter_note_length = (sample_ratio * song.quarter_note_length as f32) as u32;
189 let eighth_note_length = quarter_note_length / 2;
190
191 let mut synth = Synth {
192 song,
193 random,
194 sample_rate,
195 sample_ratio,
196 quarter_note_length,
197 eighth_note_length,
198 seq_count: 0,
199 sample_count: 0,
200 note_count: 0,
201 tracks: Self::load_tracks(
202 song,
203 sample_ratio,
204 quarter_note_length as f32,
205 eighth_note_length as f32,
206 ),
207 };
208 synth.load_notes();
209
210 synth
211 }
212
213 fn load_tracks(
215 song: &Song,
216 sample_ratio: f32,
217 quarter_note_length: f32,
218 eighth_note_length: f32,
219 ) -> [TrackState; NUM_INSTRUMENTS] {
220 let mut tracks = ArrayVec::<_, NUM_INSTRUMENTS>::new();
221 for _ in 0..NUM_INSTRUMENTS {
222 tracks.push(TrackState::new());
223 }
224 let mut tracks = tracks.into_inner().unwrap();
225
226 for (i, inst) in song.instruments.iter().enumerate() {
227 tracks[i].env.attack = (inst.env.attack as f32 * sample_ratio) as u32;
229 tracks[i].env.sustain = (inst.env.sustain as f32 * sample_ratio) as u32;
230 tracks[i].env.release = (inst.env.release as f32 * sample_ratio) as u32;
231
232 tracks[i].delay_samples = (f32::from(inst.fx.delay_time) * eighth_note_length) as u32;
234 tracks[i].delay_count = if inst.fx.delay_amount == 0.0 {
235 0
237 } else if libm::fabsf(inst.fx.delay_amount - 1.0) < f32::EPSILON {
238 u32::MAX
240 } else if tracks[i].delay_samples == 0 {
241 1
243 } else {
244 let base = libm::logf(1.0 / inst.fx.delay_amount);
247 (libm::logf(256.0) / base) as u32
248 };
249
250 tracks[i].lfo_freq = get_frequency(1.0, 2.0, inst.lfo.freq, 8) / quarter_note_length;
252 tracks[i].pan_freq = get_frequency(1.0, 2.0, inst.fx.pan_freq, 8) / quarter_note_length;
253 }
254
255 tracks
256 }
257
258 fn load_notes(&mut self) {
260 let seq_count = self.seq_count;
261 if seq_count > self.song.seq_length {
262 return;
263 }
264
265 for i in 0..self.song.instruments.len() {
266 let note_count = self.note_count;
268 self.add_note(i, seq_count, note_count, 1.0, false);
269 }
270 }
271
272 fn load_delayed_notes(&mut self) {
274 for (i, inst) in self.song.instruments.iter().enumerate() {
275 for round in 1..=self.tracks[i].delay_count {
276 let delay = self.tracks[i].delay_samples * round;
278 if delay > self.sample_count {
279 continue;
280 }
281
282 let position = self.sample_count - delay;
284 if position % self.quarter_note_length != 0 {
285 continue;
286 }
287
288 let pattern_length = self.quarter_note_length * PATTERN_LENGTH as u32;
290 let seq_count = (position / pattern_length) as usize;
291 if seq_count > self.song.seq_length {
292 continue;
293 }
294 let note_count = ((position % pattern_length) / self.quarter_note_length) as usize;
295
296 let volume = libm::powf(inst.fx.delay_amount, round as f32);
298 self.add_note(i, seq_count, note_count, volume, round % 2 == 1);
299 }
300 }
301 }
302
303 fn get_note_slot(notes: &[Note]) -> usize {
305 if let Some((i, _)) = notes.iter().enumerate().find(|(_, x)| x.pitch == 0) {
307 i
308 } else {
309 let iter = notes.iter().enumerate();
310 iter.min_by_key(|(_, x)| x.sample_count).unwrap().0
311 }
312 }
313
314 fn add_note(
316 &mut self,
317 i: usize,
318 seq_count: usize,
319 note_count: usize,
320 volume: f32,
321 swap_stereo: bool,
322 ) {
323 let inst = &self.song.instruments[i];
324
325 let p = inst.seq[seq_count];
327 if p == 0 {
328 return;
329 }
330
331 let pattern = &inst.pat[p - 1];
333
334 let pitch = pattern.notes[note_count];
336 if pitch == 0 {
337 return;
338 }
339
340 let j = Self::get_note_slot(&self.tracks[i].notes);
342 self.tracks[i].notes[j] = Note::new(pitch, self.sample_count, volume, swap_stereo);
343
344 let pitch = w(self.tracks[i].notes[j].pitch);
346 for o in 0..2 {
347 let pitch = (pitch + w(inst.osc[o].octave) + w(inst.osc[o].detune_freq)).0;
348 self.tracks[i].notes[j].osc_freq[o] =
349 get_note_frequency(pitch) * inst.osc[o].detune / self.sample_ratio;
350 }
351 }
352
353 fn env(position: u32, inst_env: &Envelope) -> Option<(f32, f32)> {
355 let attack = inst_env.attack;
356 let sustain = inst_env.sustain;
357 let release = inst_env.release;
358
359 let mut env = 1.0;
360
361 if position < attack {
362 env = position as f32 / attack as f32;
363 } else if position >= attack + sustain + release {
364 return None;
365 } else if position >= attack + sustain {
366 let pos = (position - attack - sustain) as f32;
367 env -= pos / release as f32;
368 }
369
370 Some((env, env * env))
371 }
372
373 fn osc0(&mut self, inst: &Instrument, i: usize, j: usize, lfo: f32, env_sq: f32) -> f32 {
375 let r = get_osc_output(&inst.osc[0].waveform, self.tracks[i].notes[j].osc_time[0]);
376 let mut t = self.tracks[i].notes[j].osc_freq[0];
377
378 if inst.lfo.osc0_freq {
379 t += lfo;
380 }
381 if inst.osc[0].envelope {
382 t *= env_sq;
383 }
384 self.tracks[i].notes[j].osc_time[0] += t;
385
386 r * inst.osc[0].volume
387 }
388
389 fn osc1(&mut self, inst: &Instrument, i: usize, j: usize, env_sq: f32) -> f32 {
391 let r = get_osc_output(&inst.osc[1].waveform, self.tracks[i].notes[j].osc_time[1]);
392 let mut t = self.tracks[i].notes[j].osc_freq[1];
393
394 if inst.osc[1].envelope {
395 t *= env_sq;
396 }
397 self.tracks[i].notes[j].osc_time[1] += t;
398
399 r * inst.osc[1].volume
400 }
401
402 fn filters(&mut self, inst: &Instrument, i: usize, j: usize, lfo: f32, sample: f32) -> f32 {
404 let mut f = inst.fx.freq * self.sample_ratio;
405
406 if inst.lfo.fx_freq {
407 f *= lfo;
408 }
409 f = libm::sinf(f * PI / self.sample_rate) * 1.5;
410
411 let low = libm::fmaf(f, self.tracks[i].notes[j].band, self.tracks[i].notes[j].low);
412 let high = inst.fx.resonance * (sample - self.tracks[i].notes[j].band) - low;
413 let band = libm::fmaf(f, high, self.tracks[i].notes[j].band);
414
415 self.tracks[i].notes[j].low = low;
416 self.tracks[i].notes[j].band = band;
417
418 let sample = match inst.fx.filter {
419 Filter::None => sample,
420 Filter::HighPass => high,
421 Filter::LowPass => low,
422 Filter::BandPass => band,
423 Filter::Notch => low + high,
424 };
425
426 sample * inst.env.master
427 }
428
429 fn generate_samples(
431 &mut self,
432 inst: &Instrument,
433 i: usize,
434 j: usize,
435 position: f32,
436 ) -> Option<[f32; NUM_CHANNELS]> {
437 let note_sample_count = self.tracks[i].notes[j].sample_count;
439 let (env, env_sq) = Self::env(self.sample_count - note_sample_count, &self.tracks[i].env)?;
440
441 let lfo_freq = self.tracks[i].lfo_freq;
443 let lfo = libm::fmaf(
444 get_osc_output(&inst.lfo.waveform, lfo_freq * position),
445 inst.lfo.amount * self.sample_ratio,
446 0.5,
447 );
448
449 let mut sample = self.osc0(inst, i, j, lfo, env_sq);
451
452 sample += self.osc1(inst, i, j, env_sq);
454
455 sample += osc_sin(self.random.next_f32_unit()) * inst.noise_fader * env;
457
458 sample *= env * self.tracks[i].notes[j].volume;
460
461 sample += self.filters(inst, i, j, lfo, sample);
463
464 let pan_freq = self.tracks[i].pan_freq;
465 let pan_t = libm::fmaf(
466 osc_sin(pan_freq * position),
467 inst.fx.pan_amount * self.sample_ratio,
468 0.5,
469 );
470
471 if self.tracks[i].notes[j].swap_stereo {
472 Some([sample * (1.0 - pan_t), sample * pan_t])
473 } else {
474 Some([sample * pan_t, sample * (1.0 - pan_t)])
475 }
476 }
477
478 fn update(&mut self) -> [f32; NUM_CHANNELS] {
481 let amplitude = f32::from(i16::MAX);
482 let position = self.sample_count as f32;
483
484 let mut samples = [0.0; NUM_CHANNELS];
486
487 for (i, inst) in self.song.instruments.iter().enumerate() {
488 for j in 0..self.tracks[i].notes.len() {
489 if self.tracks[i].notes[j].pitch == 0 {
490 continue;
491 }
492
493 if let Some(note_samples) = self.generate_samples(inst, i, j, position) {
494 for (i, sample) in samples.iter_mut().enumerate() {
496 *sample += note_samples[i];
497 }
498 } else {
499 self.tracks[i].notes[j] = Note::new(0, 0, 0.0, false);
501 }
502 }
503 }
504
505 for sample in &mut samples {
507 *sample = (*sample / amplitude).clamp(-1.0, 1.0);
508 }
509
510 samples
511 }
512}
513
514impl<'a> Iterator for Synth<'a> {
515 type Item = [f32; NUM_CHANNELS];
516
517 fn next(&mut self) -> Option<Self::Item> {
518 if self.seq_count > self.song.seq_length
520 && !self
521 .tracks
522 .iter()
523 .flat_map(|x| x.notes.iter())
524 .any(|x| x.pitch != 0)
525 {
526 return None;
527 }
528
529 let samples = self.update();
531
532 self.sample_count += 1;
534 let sample_in_quarter_note = self.sample_count % self.quarter_note_length;
535 if sample_in_quarter_note == 0 {
536 self.note_count += 1;
538 if self.note_count >= PATTERN_LENGTH {
539 self.note_count = 0;
540
541 self.seq_count += 1;
543 }
544
545 self.load_delayed_notes();
547 self.load_notes();
548 } else if sample_in_quarter_note == self.eighth_note_length {
549 self.load_delayed_notes();
551 }
552
553 Some(samples)
554 }
555}