use std::array;
use std::fmt::{Debug, Formatter};
use std::num::NonZero;
use super::pattern::{Pattern, PatternOperation};
use crate::channel::Pan;
use crate::file::impulse_format;
use crate::file::impulse_format::header::PatternOrder;
use crate::manager::Collector;
use crate::sample::{Sample, SampleMetaData};
#[derive(Clone, Debug)]
pub struct Song {
pub global_volume: u8,
pub mix_volume: u8,
pub initial_speed: NonZero<u8>,
pub initial_tempo: NonZero<u8>,
pub pan_separation: u8,
pub pitch_wheel_depth: u8,
pub patterns: [Pattern; Song::MAX_PATTERNS],
pub pattern_order: [PatternOrder; Song::MAX_ORDERS],
pub volume: [u8; Song::MAX_CHANNELS],
pub pan: [Pan; Song::MAX_CHANNELS],
pub samples: [Option<(SampleMetaData, Sample)>; Song::MAX_SAMPLES_INSTR],
}
impl Song {
pub const MAX_ORDERS: usize = 256;
pub const MAX_PATTERNS: usize = 240;
pub const MAX_SAMPLES_INSTR: usize = 236;
pub const MAX_CHANNELS: usize = 64;
pub fn next_pattern(&self, order: &mut u16) -> Option<u8> {
loop {
match self.get_order(*order) {
PatternOrder::Number(pattern) => break Some(pattern),
PatternOrder::EndOfSong => break None,
PatternOrder::SkipOrder => (),
}
*order += 1;
}
}
pub(crate) fn get_order(&self, order: u16) -> PatternOrder {
self.pattern_order
.get(usize::from(order))
.copied()
.unwrap_or_default()
}
pub fn copy_values_from_header(&mut self, header: &impulse_format::header::ImpulseHeader) {
self.global_volume = header.global_volume;
self.initial_speed = NonZero::new(header.initial_speed).unwrap();
self.initial_tempo = NonZero::new(header.initial_tempo).unwrap();
self.mix_volume = header.mix_volume;
self.pan_separation = header.pan_separation;
self.pitch_wheel_depth = header.pitch_wheel_depth;
self.pan = header.channel_pan;
self.volume = header.channel_volume;
for (idx, order) in header.orders.iter().enumerate() {
self.pattern_order[idx] = *order;
}
}
pub fn dbg_relevant(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "global_volume: {}, ", self.global_volume)?;
write!(f, "mix_volume: {}, ", self.mix_volume)?;
write!(f, "initial_speed: {}, ", self.initial_speed)?;
write!(f, "initial_tempo: {}, ", self.initial_tempo)?;
write!(f, "pan_seperation: {}, ", self.pan_separation)?;
write!(f, "pitch_wheel_depth: {}, ", self.pitch_wheel_depth)?;
write!(
f,
"{} not empty patterns, ",
self.patterns.iter().filter(|p| !p.is_empty()).count()
)?;
write!(
f,
"{} orders, ",
self.pattern_order
.iter()
.filter(|o| **o != PatternOrder::EndOfSong)
.count()
)?;
Ok(())
}
}
impl Default for Song {
fn default() -> Self {
Self {
global_volume: 128,
mix_volume: Default::default(),
initial_speed: NonZero::new(6).unwrap(),
initial_tempo: NonZero::new(125).unwrap(),
pan_separation: 128,
pitch_wheel_depth: Default::default(),
patterns: array::from_fn(|_| Pattern::default()),
pattern_order: array::from_fn(|_| PatternOrder::default()),
volume: array::from_fn(|_| 64),
pan: array::from_fn(|_| Pan::default()),
samples: array::from_fn(|_| None),
}
}
}
#[derive(Clone, Debug)]
pub enum SongOperation {
SetVolume(u8, u8),
SetPan(u8, Pan),
SetSample(u8, SampleMetaData, Sample),
RemoveSample(u8),
PatternOperation(u8, PatternOperation),
SetOrder(u16, PatternOrder),
SetInitialSpeed(NonZero<u8>),
SetInitialTempo(NonZero<u8>),
SetGlobalVol(u8),
}
#[derive(Clone, Debug)]
pub(crate) enum ValidOperation {
SetVolume(u8, u8),
SetPan(u8, Pan),
SetSample(u8, SampleMetaData, Sample),
RemoveSample(u8),
PatternOperation(u8, PatternOperation),
SetOrder(u16, PatternOrder),
SetInitialSpeed(NonZero<u8>),
SetInitialTempo(NonZero<u8>),
SetGlobalVol(u8),
}
impl ValidOperation {
pub(crate) fn new(
op: SongOperation,
handle: &mut Collector,
song: &Song,
) -> Result<ValidOperation, SongOperation> {
let valid = match op {
SongOperation::SetVolume(c, _) => usize::from(c) < Song::MAX_CHANNELS,
SongOperation::SetPan(c, _) => usize::from(c) < Song::MAX_CHANNELS,
SongOperation::SetSample(idx, _, _) => usize::from(idx) < Song::MAX_SAMPLES_INSTR,
SongOperation::RemoveSample(idx) => usize::from(idx) < Song::MAX_SAMPLES_INSTR,
SongOperation::PatternOperation(idx, op) => match song.patterns.get(usize::from(idx)) {
Some(pattern) => pattern.operation_is_valid(&op),
None => false,
},
SongOperation::SetOrder(idx, _) => usize::from(idx) < Song::MAX_ORDERS,
SongOperation::SetInitialSpeed(_) => true,
SongOperation::SetInitialTempo(_) => true,
SongOperation::SetGlobalVol(_) => true,
};
if valid {
Ok(match op {
SongOperation::SetVolume(c, v) => Self::SetVolume(c, v),
SongOperation::SetPan(c, pan) => Self::SetPan(c, pan),
SongOperation::SetSample(i, meta_data, sample) => {
handle.add_sample(sample.clone());
Self::SetSample(i, meta_data, sample)
}
SongOperation::RemoveSample(i) => Self::RemoveSample(i),
SongOperation::PatternOperation(i, pattern_operation) => {
Self::PatternOperation(i, pattern_operation)
}
SongOperation::SetOrder(i, pattern_order) => Self::SetOrder(i, pattern_order),
SongOperation::SetInitialSpeed(s) => Self::SetInitialSpeed(s),
SongOperation::SetInitialTempo(t) => Self::SetInitialTempo(t),
SongOperation::SetGlobalVol(v) => Self::SetGlobalVol(v),
})
} else {
Err(op)
}
}
}
impl simple_left_right::Absorb<ValidOperation> for Song {
fn absorb(&mut self, operation: ValidOperation) {
match operation {
ValidOperation::SetVolume(i, val) => self.volume[usize::from(i)] = val,
ValidOperation::SetPan(i, val) => self.pan[usize::from(i)] = val,
ValidOperation::SetSample(i, meta, sample) => {
self.samples[usize::from(i)] = Some((meta, sample))
}
ValidOperation::RemoveSample(i) => self.samples[usize::from(i)] = None,
ValidOperation::PatternOperation(i, op) => {
self.patterns[usize::from(i)].apply_operation(op)
}
ValidOperation::SetOrder(i, order) => self.pattern_order[usize::from(i)] = order,
ValidOperation::SetInitialSpeed(s) => self.initial_speed = s,
ValidOperation::SetInitialTempo(t) => self.initial_tempo = t,
ValidOperation::SetGlobalVol(v) => self.global_volume = v,
}
}
}