use alloc::collections::VecDeque;
use alloc::rc::Rc;
use alloc::vec::Vec;
use crate::constants::SAMPLE_RATE;
use crate::plugin::Plugin;
use crate::synth::{envelope, midi_to_hz, square, triangle};
use crate::{BassNote, MelodyNote, SongNote};
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct StemSample {
pub value: f32,
pub byte_index: u64,
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct SongSample {
pub square: StemSample,
pub triangle: StemSample,
pub voice: StemSample,
}
pub trait SampleStem {
fn sample_square(&self, sample_rate: u32) -> Vec<StemSample>;
fn sample_triangle(&self, sample_rate: u32) -> Vec<StemSample>;
}
pub trait Note {
fn midi(&self) -> f64;
fn duration(&self) -> f64;
fn byte_index(&self) -> u64;
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct SquareNote {
pub midi: f64,
pub duration: f64,
pub amp: f32,
pub duty: f64,
pub byte_index: u64,
}
impl Note for SquareNote {
fn midi(&self) -> f64 {
self.midi
}
fn duration(&self) -> f64 {
self.duration
}
fn byte_index(&self) -> u64 {
self.byte_index
}
}
impl SampleStem for SquareNote {
fn sample_square(&self, sample_rate: u32) -> Vec<StemSample> {
let sample_rate = sample_rate as f64;
let total_samples = (sample_rate * self.duration).floor() as usize;
if total_samples == 0 {
return Vec::new();
}
let dur_sec = total_samples as f64 / sample_rate;
let chunk = square(
sample_rate,
midi_to_hz(self.midi),
dur_sec,
self.amp,
self.duty,
);
let chunk = envelope(sample_rate, &chunk, 0.005, 0.05, 0.7, 0.05);
let mut samples = Vec::new();
for (i, &s) in chunk.iter().enumerate() {
if i >= total_samples {
break;
}
samples.push(StemSample {
value: s,
byte_index: self.byte_index,
});
}
samples
}
fn sample_triangle(&self, _sample_rate: u32) -> Vec<StemSample> {
Default::default()
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct TriangleNote {
pub midi: f64,
pub duration: f64,
pub byte_index: u64,
}
impl Note for TriangleNote {
fn midi(&self) -> f64 {
self.midi
}
fn duration(&self) -> f64 {
self.duration
}
fn byte_index(&self) -> u64 {
self.byte_index
}
}
impl SampleStem for TriangleNote {
fn sample_square(&self, _sample_rate: u32) -> Vec<StemSample> {
Default::default()
}
fn sample_triangle(&self, sample_rate: u32) -> Vec<StemSample> {
let sample_rate = sample_rate as f64;
let total_beats = (sample_rate as f64 * self.duration).floor() as usize;
if total_beats == 0 {
return Vec::new();
}
let dur_sec = total_beats as f64 / sample_rate;
let chunk = triangle(sample_rate, midi_to_hz(self.midi), dur_sec, 0.25);
let chunk = envelope(sample_rate, &chunk, 0.005, 0.05, 0.7, 0.05);
let mut samples = Vec::new();
for (i, &s) in chunk.iter().enumerate() {
if i >= total_beats {
break;
}
samples.push(StemSample {
value: s,
byte_index: self.byte_index,
});
}
samples
}
}
#[derive(Debug, Clone)]
pub struct SongSampleGenerator {
plugin: Rc<dyn Plugin>,
sample_rate: u32,
melody_notes: VecDeque<MelodyNote>,
bass_notes: VecDeque<BassNote>,
square_samples: VecDeque<StemSample>,
triangle_samples: VecDeque<StemSample>,
voice_samples: VecDeque<StemSample>,
}
impl SongSampleGenerator {
pub fn new(plugin: Rc<dyn Plugin>) -> Self {
Self {
plugin,
sample_rate: SAMPLE_RATE,
melody_notes: Default::default(),
bass_notes: Default::default(),
square_samples: Default::default(),
triangle_samples: Default::default(),
voice_samples: Default::default(),
}
}
pub fn with_sample_rate(self, sample_rate: u32) -> Self {
Self {
sample_rate,
..self
}
}
pub fn push(&mut self, note: SongNote) {
match note {
SongNote::Melody(n) => self.melody_notes.push_back(n),
SongNote::Bass(n) => self.bass_notes.push_back(n),
}
}
pub fn extend(&mut self, notes: &[SongNote]) {
for note in notes {
self.push(*note);
}
}
pub fn len(&self) -> usize {
self.square_samples.len().min(self.triangle_samples.len())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn sample(&mut self) -> Vec<SongSample> {
let mut samples = Vec::new();
loop {
let available_samples = self.square_samples.len().min(self.triangle_samples.len());
for _ in 0..available_samples {
let Some(square) = self.square_samples.pop_front() else {
break;
};
let Some(triangle) = self.triangle_samples.pop_front() else {
break;
};
let voice = self.voice_samples.pop_front().unwrap_or_default();
samples.push(SongSample { square, triangle, voice });
}
while self.triangle_samples.len() > self.square_samples.len() {
let Some(note) = self.melody_notes.pop_front() else {
return samples;
};
self.square_samples
.extend(self.plugin.sample_melody_note(note, self.sample_rate));
self.voice_samples
.extend(self.plugin.sample_voice_note(note, self.sample_rate));
}
if let Some(note) = self.bass_notes.pop_front() {
self.triangle_samples
.extend(self.plugin.sample_bass_note(note, self.sample_rate));
}
if self.triangle_samples.is_empty() {
return samples;
}
}
}
}
#[cfg(feature = "std")]
mod iter {
use alloc::rc::Rc;
use std::collections::VecDeque;
use crate::{
plugin::Plugin, DrumSample, MixSamples, SongNote, SongSample, SongSampleGenerator,
};
pub struct SampleSongNotes<I: IntoIterator<Item = std::io::Result<SongNote>>> {
inner: I::IntoIter,
sample_generator: SongSampleGenerator,
samples: VecDeque<SongSample>,
}
impl<I: IntoIterator<Item = std::io::Result<SongNote>>> SampleSongNotes<I> {
pub fn new(inner: I, plugin: Rc<dyn Plugin>) -> Self {
Self::from_generator(inner, SongSampleGenerator::new(plugin))
}
pub fn from_generator(inner: I, sample_generator: SongSampleGenerator) -> Self {
Self {
inner: inner.into_iter(),
sample_generator,
samples: Default::default(),
}
}
pub fn with_sample_rate(self, sample_rate: u32) -> Self {
Self {
sample_generator: self.sample_generator.with_sample_rate(sample_rate),
..self
}
}
pub fn mix<D: IntoIterator<Item = DrumSample>>(
self,
drum_samples: D,
) -> MixSamples<Self, D> {
MixSamples::new(self, drum_samples)
}
}
impl<I: IntoIterator<Item = std::io::Result<SongNote>>> Iterator for SampleSongNotes<I> {
type Item = std::io::Result<SongSample>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(sample) = self.samples.pop_front() {
return Some(Ok(sample));
}
match self.inner.next() {
None => return None,
Some(Err(e)) => return Some(Err(e)),
Some(Ok(n)) => {
self.sample_generator.push(n);
self.samples.extend(self.sample_generator.sample());
}
};
}
}
}
}
#[cfg(feature = "std")]
pub use iter::*;