use crate::channel::Channel;
use crate::historical_helper::HistoricalHelper;
use crate::triggerkeep::*;
use alloc::{vec, vec::Vec};
use xmrs::prelude::*;
pub struct XmrsPlayer<'a> {
pub module: &'a Module,
pub current_song: usize,
sample_rate: f32,
tempo: usize,
bpm: usize,
pub global_volume: f32,
effect_volume_slide_speed: f32,
effect_volume_slide_fine: bool,
pub amplification: f32,
current_table_index: usize,
current_row: usize,
current_tick: usize,
remaining_samples_in_tick: f32,
pub generated_samples: u64,
position_jump: bool,
pattern_break: bool,
jump_dest: usize,
jump_row: usize,
extra_ticks: usize,
pub channel: Vec<Channel<'a>>,
row_loop_count: Vec<Vec<usize>>,
loop_count: usize,
max_loop_count: usize,
right_sample: Option<f32>,
#[cfg(feature = "std")]
debug: bool,
hhelper: Option<HistoricalHelper>,
pause: bool,
}
impl<'a> XmrsPlayer<'a> {
pub fn new(module: &'a Module, sample_rate: f32, song: usize, historical: bool) -> Self {
let num_channels = module.get_num_channels();
let hhelper = if historical {
Some(HistoricalHelper::new(module.default_tempo))
} else {
None
};
let current_song = if song < module.pattern_order.len() {
song
} else {
0
};
let mut player = Self {
module,
current_song,
sample_rate,
tempo: module.default_tempo,
bpm: module.default_bpm,
global_volume: 1.0,
amplification: 1.0,
row_loop_count: vec![vec![0; MAX_NUM_ROWS]; module.get_song_length(current_song)],
hhelper,
effect_volume_slide_speed: 0.0,
effect_volume_slide_fine: true,
current_table_index: 0,
current_row: 0,
current_tick: 0,
remaining_samples_in_tick: 0.0,
generated_samples: 0,
position_jump: false,
pattern_break: false,
jump_dest: 0,
jump_row: 0,
extra_ticks: 0,
channel: vec![],
loop_count: 0,
max_loop_count: 0,
right_sample: None,
#[cfg(feature = "std")]
debug: false,
pause: false,
};
player.channel = vec![Channel::new(module, sample_rate, hhelper); num_channels];
player
}
#[cfg(feature = "std")]
pub fn debug(&mut self, debug: bool) {
self.debug = debug;
}
pub fn set_mute_channel(&mut self, channel_num: usize, mute: bool) {
if channel_num < self.channel.len() {
self.channel[channel_num].muted = mute;
}
}
pub fn mute_all(&mut self, mute: bool) {
for c in &mut self.channel {
c.muted = mute;
}
}
pub fn set_max_loop_count(&mut self, max_loop_count: usize) {
self.max_loop_count = max_loop_count;
}
pub fn get_loop_count(&self) -> usize {
self.loop_count
}
pub fn get_sample_rate(&self) -> f32 {
self.sample_rate
}
pub fn get_tempo(&self) -> usize {
self.tempo
}
pub fn get_bpm(&self) -> usize {
self.bpm
}
pub fn goto(&mut self, table_position: usize, row: usize, speed: usize) -> bool {
if table_position < self.module.get_song_length(self.current_song) {
let num_row = self.module.pattern_order[self.current_song][table_position];
if row < self.module.get_num_rows(num_row) {
self.jump_dest = table_position;
self.jump_row = row;
self.position_jump = true;
self.tempo = if speed == 0 {
self.module.default_tempo
} else {
speed
};
self.bpm = self.module.default_bpm;
self.global_volume = 1.0;
let num_channels = self.module.get_num_channels();
for i in 0..num_channels {
self.channel[i].trigger_pitch(TRIGGER_KEEP_PERIOD); }
self.remaining_samples_in_tick = 0.0;
self.current_tick = 0;
true
} else {
false
}
} else {
false
}
}
pub fn get_current_pattern(&self) -> usize {
self.module.pattern_order[self.current_song][self.current_table_index]
}
pub fn get_current_table_index(&self) -> usize {
self.current_table_index
}
pub fn get_current_row(&self) -> usize {
self.current_row
}
pub fn pause(&mut self, pause: bool) {
self.pause = pause;
}
fn post_pattern_change(&mut self) {
if self.current_table_index >= self.module.pattern_order[self.current_song].len() {
self.current_table_index = self.module.restart_position;
}
#[cfg(feature = "std")]
if self.debug {
println!(
"pattern_order[0x{:03x}] = 0x{:03x}",
self.current_table_index,
self.module.pattern_order[self.current_song][self.current_table_index]
);
}
}
fn tick0_global_effects(&mut self, ch_index: usize) {
let ch = &mut self.channel[ch_index];
let pattern_slot = &ch.current;
for gfx in pattern_slot.global_effects.clone() {
match gfx {
GlobalEffect::Bpm(bpm) => {
self.bpm = bpm;
}
GlobalEffect::BpmSlide(_value) => {
#[cfg(feature = "demo")]
println!("BpmSlide");
todo!();
}
GlobalEffect::MidiMacro(_m) => {
#[cfg(feature = "demo")]
println!("MidiMacro");
todo!();
}
GlobalEffect::PatternBreak(position) => {
self.pattern_break = true;
self.jump_row = position;
}
GlobalEffect::PatternDelay {
quantity: q,
tempo: t,
} => self.extra_ticks = if t { q * self.tempo } else { q },
GlobalEffect::PatternLoop(value) => {
if value != 0 {
if value == ch.pattern_loop_count {
ch.pattern_loop_count = 0;
} else {
ch.pattern_loop_count += 1;
self.position_jump = true;
self.jump_row = ch.pattern_loop_origin;
self.jump_dest = self.current_table_index;
}
} else {
ch.pattern_loop_origin = self.current_row;
if self.hhelper.is_some() {
self.jump_row = ch.pattern_loop_origin;
}
}
}
GlobalEffect::PositionJump(position) => {
if position < self.module.pattern_order[self.current_song].len() {
self.position_jump = true;
self.jump_dest = position;
self.jump_row = 0;
}
}
GlobalEffect::Speed(speed) => {
self.tempo = speed;
}
GlobalEffect::Volume(volume) => {
self.global_volume = volume.clamp(0.0, 1.0);
}
GlobalEffect::VolumeSlide { speed: s, fine: f } => {
if f {
self.global_volume = (self.global_volume + s).clamp(0.0, 1.0);
}
self.effect_volume_slide_speed = s;
self.effect_volume_slide_fine = f;
}
}
}
}
fn tick0(&mut self) {
if self.position_jump {
self.current_table_index = self.jump_dest;
self.current_row = self.jump_row;
self.position_jump = false;
self.pattern_break = false;
self.jump_row = 0;
self.post_pattern_change();
} else if self.pattern_break {
self.current_table_index += 1;
self.current_row = self.jump_row;
self.pattern_break = false;
self.jump_row = 0;
self.post_pattern_change();
}
let pat_idx_temp = self.module.pattern_order[self.current_song][self.current_table_index];
let pat_idx = if pat_idx_temp < self.module.pattern.len() {
pat_idx_temp
} else {
self.current_table_index = 0;
self.module.pattern_order[self.current_song][self.current_table_index]
};
let num_channels = self.module.get_num_channels();
let mut in_a_loop = false;
let current_row = self.current_row;
#[cfg(feature = "std")]
if self.debug {
print!("{:03X} ", current_row);
}
for ch_index in 0..num_channels {
let ps = &self.module.pattern[pat_idx][current_row][ch_index];
#[cfg(feature = "std")]
if self.debug {
print!("{:?} ", ps.note);
}
self.channel[ch_index].tick0(ps);
self.tick0_global_effects(ch_index);
if !in_a_loop && self.channel[ch_index].pattern_loop_count > 0 {
in_a_loop = true;
}
}
#[cfg(feature = "std")]
if self.debug {
println!();
}
if !in_a_loop {
self.loop_count = self.row_loop_count[self.current_table_index][self.current_row];
self.row_loop_count[self.current_table_index][self.current_row] += 1;
}
self.current_row += 1;
let pattern_len = self.module.pattern[pat_idx].len();
if !self.position_jump && !self.pattern_break && self.current_row >= pattern_len {
self.current_table_index += 1;
self.current_row = self.jump_row;
self.jump_row = 0;
self.post_pattern_change();
}
}
fn tick(&mut self) {
let any_global_slide = self
.channel
.iter()
.any(|ch| ch.current.has_global_volume_slide());
for ch in &mut self.channel {
ch.tick(self.current_tick);
}
if any_global_slide && !self.effect_volume_slide_fine {
self.global_volume =
(self.global_volume + self.effect_volume_slide_speed).clamp(0.0, 1.0);
}
}
fn step(&mut self) {
if self.remaining_samples_in_tick <= 0.0 {
if self.current_tick == 0 {
self.tick0();
} else {
self.tick();
}
self.current_tick += 1;
if self.tempo != 0 && self.current_tick >= self.tempo + self.extra_ticks {
self.current_tick = 0;
self.extra_ticks = 0;
}
if let Some(hhelper) = &mut self.hhelper {
hhelper.set_tempo(self.tempo);
}
self.remaining_samples_in_tick += self.sample_rate / (self.bpm as f32 * 0.4);
}
self.remaining_samples_in_tick -= 1.0;
}
pub fn samples_from_channels(&mut self) -> Option<Vec<(f32, f32)>> {
if self.pause {
return Some(vec![(0.0, 0.0); self.channel.len()]);
}
self.step();
if self.max_loop_count > 0 && self.loop_count >= self.max_loop_count {
return None;
}
let samples: Vec<(f32, f32)> = self
.channel
.iter_mut()
.map(|ch| match ch.next() {
Some(fval) => {
if ch.is_muted() {
(0.0, 0.0)
} else {
fval
}
}
None => (0.0, 0.0),
})
.collect();
self.generated_samples += 1;
Some(samples)
}
pub fn samples_to_sample(&mut self, samples: &[(f32, f32)]) -> (f32, f32) {
let sample = samples
.iter()
.fold((0.0, 0.0), |(acc_left, acc_right), (left, right)| {
(acc_left + left, acc_right + right)
});
(sample.0, sample.1)
}
pub fn samples_apply_volume(&mut self, samples: &[(f32, f32)]) -> (f32, f32) {
let fgvol = self.global_volume * self.amplification;
let sample = self.samples_to_sample(samples);
(sample.0 * fgvol, sample.1 * fgvol)
}
pub fn sample(&mut self, apply_volume: bool) -> Option<(f32, f32)> {
if let Some(samples) = self.samples_from_channels() {
if apply_volume {
Some(self.samples_apply_volume(&samples))
} else {
Some(self.samples_to_sample(&samples))
}
} else {
None
}
}
fn sample_one(&mut self) -> Option<f32> {
match self.right_sample {
Some(right) => {
self.right_sample = None;
Some(right)
}
None => {
let next_samples = self.sample(true);
match next_samples {
Some((left, right)) => {
self.right_sample = Some(right);
Some(left)
}
None => None,
}
}
}
}
}
impl<'a> Iterator for XmrsPlayer<'a> {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
if self.max_loop_count > 0 && self.loop_count >= self.max_loop_count {
None
} else {
self.sample_one()
}
}
}