Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
11 kB · 320 lines
Rust
at dev
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321use core::fmt::Debug;use core::num::NonZero;use core::{array, fmt};
use super::pattern::{Pattern, PatternOperation};use crate::Collector;use crate::sample::{Sample, SampleMetaData};
#[derive(Clone, Debug, PartialEq)]pub struct Song { pub global_volume: u8, pub mix_volume: u8, /// Speed specifies how many ticks are in one row. This reduces tempo, but increases resolution of some effects. pub initial_speed: NonZero<u8>, /// Tempo determines how many ticks are in one second with the following formula: tempo/2 = ticks per second. pub initial_tempo: NonZero<u8>, pub pan_separation: u8, pub pitch_wheel_depth: u8,
pub patterns: [Pattern; Song::MAX_PATTERNS], pub pattern_order: [PackedPatternOrder; Self::MAX_ORDERS], pub volume: [u8; Self::MAX_CHANNELS], pub pan: [PackedPan; Self::MAX_CHANNELS], // maybe move to struct of arrays or similar pub samples: [Option<(SampleMetaData, Sample)>; Self::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;
/// order value shouldn't be modified outside of this function. /// This moves it forward correctly and returns the pattern to be played 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; } }
/// out of bounds is EndOfSong pub(crate) fn get_order(&self, order: u16) -> PatternOrder { self.pattern_order .get(usize::from(order)) .copied() .unwrap_or_default() .get() }
/// debug like impl which isn't as long by cutting down a lot of information pub fn dbg_relevant(&self, f: &mut fmt::Formatter<'_>) -> Result<(), 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.into()) .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()), // please const traits i need you pattern_order: array::from_fn(|_| PackedPatternOrder::default()), volume: [64; _], // please const traits pan: array::from_fn(|_| PackedPan::default()), samples: array::from_fn(|_| None), } }}
// On change: also change ValidOperation#[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),}
/// keep in sync with SongOperation#[derive(Clone, Debug)]pub(crate) enum ValidOperation { SetVolume(u8, u8), SetPan(u8, PackedPan), SetSample(u8, SampleMetaData, Sample), RemoveSample(u8), PatternOperation(u8, PatternOperation), SetOrder(u16, PackedPatternOrder), 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, pan) => { usize::from(c) < Song::MAX_CHANNELS && !matches!(pan, Pan::Value(v) if v > PackedPan::MAX_VALID) } 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, order) => { usize::from(idx) < Song::MAX_ORDERS && !matches!(order, PatternOrder::Number(n) if n > PackedPatternOrder::MAX_VALID) } 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.into()), 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.into()) } 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, } }}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum Pan { /// Value ranges from 0 to 64, with 32 being center // please pattern types // find a way to actually enforce the constraints, so that conversion to and from Packed can be unchecked Value(u8), Surround, Disabled,}
impl Default for Pan { fn default() -> Self { Self::Value(32) }}
#[derive(Clone, Copy, PartialEq, Eq)]// always a valid panpub struct PackedPan(pub(crate) u8);
impl PackedPan { pub(crate) const SURROUND: u8 = 100; pub(crate) const DISABLED: u8 = 128; pub(crate) const MAX_VALID: u8 = 64; #[inline] pub fn get(self) -> Pan { match self.0 { Self::SURROUND => Pan::Surround, Self::DISABLED => Pan::Disabled, 0..=Self::MAX_VALID => Pan::Value(self.0), // Pan value currently public _ => unreachable!(), } }}
impl From<Pan> for PackedPan { fn from(value: Pan) -> Self { match value { Pan::Value(v) => { // pan value currently public assert!(v <= Self::MAX_VALID); Self(v) } Pan::Surround => Self(Self::SURROUND), Pan::Disabled => Self(Self::DISABLED), } }}
impl Default for PackedPan { fn default() -> Self { Pan::default().into() }}
impl Debug for PackedPan { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Packed") .field("value", &self.get()) .finish() }}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]pub enum PatternOrder { // please pattern types i need you // somehow enforce that this stays below 200 Number(u8), #[default] EndOfSong, SkipOrder,}
#[derive(Clone, Copy, PartialEq, Eq)]// this is always a valid PatternOrder. Values 200..254 don't appear. i want pattern typespub struct PackedPatternOrder(pub(crate) u8);
impl PackedPatternOrder { pub(crate) const END_OF_SONG: u8 = 255; pub(crate) const SKIP_ORDER: u8 = 254; pub(crate) const MAX_VALID: u8 = 199; #[inline] pub fn get(self) -> PatternOrder { match self.0 { Self::END_OF_SONG => PatternOrder::EndOfSong, Self::SKIP_ORDER => PatternOrder::SkipOrder, 0..=Self::MAX_VALID => PatternOrder::Number(self.0), // value is currently public _ => unreachable!(), } }}
impl From<PatternOrder> for PackedPatternOrder { fn from(value: PatternOrder) -> Self { match value { PatternOrder::Number(n) => { assert!(n <= Self::MAX_VALID); Self(n) } PatternOrder::EndOfSong => Self(Self::END_OF_SONG), PatternOrder::SkipOrder => Self(Self::SKIP_ORDER), } }}
impl Default for PackedPatternOrder { fn default() -> Self { PatternOrder::default().into() }}
impl Debug for PackedPatternOrder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Packed") .field("value", &self.get()) .finish() }}