use std::{num::NonZero, ops::ControlFlow};
use crate::{
audio_processing::{sample::SamplePlayer, Frame},
channel::Pan,
manager::PlaybackSettings,
project::song::Song,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlaybackStatus {
pub position: PlaybackPosition,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlaybackPosition {
pub order: Option<u16>,
pub pattern: u8,
pub row: u16,
pub loop_active: bool,
}
impl PlaybackPosition {
#[inline]
fn step_row(&mut self, song: &Song) -> ControlFlow<()> {
self.row += 1;
if self.row >= song.patterns[usize::from(self.pattern)].row_count() {
self.row = 0;
if let Some(order) = &mut self.order {
if let Some(pattern) = song.next_pattern(order) {
self.pattern = pattern;
ControlFlow::Continue(())
} else {
if !self.loop_active {
return ControlFlow::Break(());
}
*order = 0;
if let Some(pattern) = song.next_pattern(order) {
self.pattern = pattern;
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
}
} else if self.loop_active {
return ControlFlow::Continue(());
} else {
return ControlFlow::Break(());
}
} else {
ControlFlow::Continue(())
}
}
fn new(settings: PlaybackSettings, song: &Song) -> Option<Self> {
match settings {
PlaybackSettings::Pattern { idx, should_loop } => {
if idx < u8::try_from(song.patterns.len()).unwrap() {
Some(Self {
order: None,
pattern: idx,
row: 0,
loop_active: should_loop,
})
} else {
None
}
}
PlaybackSettings::Order {
mut idx,
should_loop,
} => {
let pattern = song.next_pattern(&mut idx)?;
Some(Self {
order: Some(idx),
pattern,
row: 0,
loop_active: should_loop,
})
}
}
}
}
pub struct PlaybackState {
position: PlaybackPosition,
is_done: bool,
tick: u8,
frame: u32,
samplerate: NonZero<u32>,
voices: [Option<SamplePlayer>; PlaybackState::VOICES],
}
impl PlaybackState {
pub const VOICES: usize = Song::MAX_CHANNELS;
pub fn iter<'playback, 'song, const INTERPOLATION: u8>(
&'playback mut self,
song: &'song Song,
) -> PlaybackIter<'song, 'playback, INTERPOLATION> {
PlaybackIter { state: self, song }
}
fn frames_per_tick(samplerate: NonZero<u32>, tempo: NonZero<u8>) -> u32 {
(samplerate.get() * 2) / u32::from(tempo.get())
}
pub fn get_status(&self) -> PlaybackStatus {
PlaybackStatus {
position: self.position,
}
}
pub fn set_samplerate(&mut self, samplerate: NonZero<u32>) {
self.samplerate = samplerate;
self.voices
.iter_mut()
.flatten()
.for_each(|voice| voice.set_out_samplerate(samplerate));
}
pub fn is_done(&self) -> bool {
self.is_done
}
}
impl PlaybackState {
pub fn new(song: &Song, samplerate: NonZero<u32>, settings: PlaybackSettings) -> Option<Self> {
let mut out = Self {
position: PlaybackPosition::new(settings, song)?,
is_done: false,
tick: song.initial_speed.get(),
frame: Self::frames_per_tick(samplerate, song.initial_tempo),
samplerate,
voices: std::array::from_fn(|_| None),
};
out.iter::<0>(song).create_sample_players();
Some(out)
}
}
impl std::fmt::Debug for PlaybackState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PlaybackState")
.field("position", &self.position)
.field("tick", &self.tick)
.field("frame", &self.frame)
.field("samplerate", &self.samplerate)
.finish_non_exhaustive()?;
write!(
f,
"active channels: {}",
self.voices.iter().filter(|v| v.is_some()).count()
)
}
}
pub struct PlaybackIter<'song, 'playback, const INTERPOLATION: u8> {
state: &'playback mut PlaybackState,
song: &'song Song,
}
impl<const INTERPOLATION: u8> PlaybackIter<'_, '_, INTERPOLATION> {
pub fn frames_per_tick(&self) -> u32 {
PlaybackState::frames_per_tick(self.state.samplerate, self.song.initial_tempo)
}
}
impl<const INTERPOLATION: u8> Iterator for PlaybackIter<'_, '_, INTERPOLATION> {
type Item = Frame;
fn next(&mut self) -> Option<Self::Item> {
fn scale_vol(vol: u8) -> f32 {
(vol as f32) / (u8::MAX as f32)
}
fn scale_pan(pan: u8) -> f32 {
debug_assert!((0..=64).contains(&pan));
(pan as f32) * const { (1. / 64.) * (std::f32::consts::FRAC_PI_2) }
}
if self.state.is_done {
return None;
}
debug_assert!(self.song.volume.len() == self.state.voices.len());
debug_assert!(self.song.pan.len() == self.state.voices.len());
let out: Frame = self
.state
.voices
.iter_mut()
.zip(self.song.volume)
.zip(self.song.pan)
.flat_map(|((channel, vol), pan)| {
if let Some(voice) = channel {
let mut out = voice.next::<INTERPOLATION>().unwrap();
if voice.check_position().is_break() {
*channel = None;
}
let channel_vol = scale_vol(vol);
if let Pan::Value(pan) = pan {
let angle = scale_pan(pan);
out.pan_constant_power(angle);
}
Some(out * channel_vol)
} else {
None
}
})
.sum();
self.step();
let out_vol = scale_vol(self.song.global_volume);
Some(out * out_vol)
}
}
impl<const INTERPOLATION: u8> PlaybackIter<'_, '_, INTERPOLATION> {
fn step(&mut self) {
if self.state.frame > 0 {
self.state.frame -= 1;
return;
} else {
self.state.frame = self.frames_per_tick();
}
if self.state.tick > 0 {
self.state.tick -= 1;
return;
} else {
self.state.tick = self.song.initial_speed.get();
}
match self.state.position.step_row(self.song) {
ControlFlow::Continue(_) => self.create_sample_players(),
ControlFlow::Break(_) => self.state.is_done = true,
}
}
fn create_sample_players(&mut self) {
for (position, event) in
&self.song.patterns[usize::from(self.state.position.pattern)][self.state.position.row]
{
if let Some((meta, ref sample)) = self.song.samples[usize::from(event.sample_instr)] {
let player =
SamplePlayer::new(sample.clone(), meta, self.state.samplerate, event.note);
self.state.voices[usize::from(position.channel)] = Some(player);
}
}
}
}