old school music tracker audio backend
at dev 209 lines 7.5 kB view raw
1use std::array; 2use std::fmt::{Debug, Formatter}; 3use std::num::NonZero; 4 5use super::pattern::{Pattern, PatternOperation}; 6use crate::Collector; 7use crate::sample::{Sample, SampleMetaData}; 8 9#[derive(Clone, Debug)] 10pub struct Song { 11 pub global_volume: u8, 12 pub mix_volume: u8, 13 /// Speed specifies how many ticks are in one row. This reduces tempo, but increases resolution of some effects. 14 pub initial_speed: NonZero<u8>, 15 /// Tempo determines how many ticks are in one second with the following formula: tempo/2 = ticks per second. 16 pub initial_tempo: NonZero<u8>, 17 pub pan_separation: u8, 18 pub pitch_wheel_depth: u8, 19 20 pub patterns: [Pattern; Song::MAX_PATTERNS], 21 pub pattern_order: [PatternOrder; Self::MAX_ORDERS], 22 pub volume: [u8; Self::MAX_CHANNELS], 23 pub pan: [Pan; Self::MAX_CHANNELS], 24 pub samples: [Option<(SampleMetaData, Sample)>; Self::MAX_SAMPLES_INSTR], 25} 26 27impl Song { 28 pub const MAX_ORDERS: usize = 256; 29 pub const MAX_PATTERNS: usize = 240; 30 pub const MAX_SAMPLES_INSTR: usize = 236; 31 pub const MAX_CHANNELS: usize = 64; 32 33 /// order value shouldn't be modified outside of this function. 34 /// This moves it forward correctly and returns the pattern to be played 35 pub fn next_pattern(&self, order: &mut u16) -> Option<u8> { 36 loop { 37 match self.get_order(*order) { 38 PatternOrder::Number(pattern) => break Some(pattern), 39 PatternOrder::EndOfSong => break None, 40 PatternOrder::SkipOrder => (), 41 } 42 *order += 1; 43 } 44 } 45 46 /// out of bounds is EndOfSong 47 pub(crate) fn get_order(&self, order: u16) -> PatternOrder { 48 self.pattern_order 49 .get(usize::from(order)) 50 .copied() 51 .unwrap_or_default() 52 } 53 54 /// debug like impl which isn't as long by cutting down a lot of information 55 pub fn dbg_relevant(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { 56 write!(f, "global_volume: {}, ", self.global_volume)?; 57 write!(f, "mix_volume: {}, ", self.mix_volume)?; 58 write!(f, "initial_speed: {}, ", self.initial_speed)?; 59 write!(f, "initial_tempo: {}, ", self.initial_tempo)?; 60 write!(f, "pan_seperation: {}, ", self.pan_separation)?; 61 write!(f, "pitch_wheel_depth: {}, ", self.pitch_wheel_depth)?; 62 write!( 63 f, 64 "{} not empty patterns, ", 65 self.patterns.iter().filter(|p| !p.is_empty()).count() 66 )?; 67 write!( 68 f, 69 "{} orders, ", 70 self.pattern_order 71 .iter() 72 .filter(|o| **o != PatternOrder::EndOfSong) 73 .count() 74 )?; 75 Ok(()) 76 } 77} 78 79impl Default for Song { 80 fn default() -> Self { 81 Self { 82 global_volume: 128, 83 mix_volume: Default::default(), 84 initial_speed: NonZero::new(6).unwrap(), 85 initial_tempo: NonZero::new(125).unwrap(), 86 pan_separation: 128, 87 pitch_wheel_depth: Default::default(), 88 patterns: array::from_fn(|_| Pattern::default()), 89 pattern_order: array::from_fn(|_| PatternOrder::default()), 90 volume: [64; _], 91 pan: array::from_fn(|_| Pan::default()), 92 samples: array::from_fn(|_| None), 93 } 94 } 95} 96 97// On change: also change ValidOperation 98#[derive(Clone, Debug)] 99pub enum SongOperation { 100 SetVolume(u8, u8), 101 SetPan(u8, Pan), 102 SetSample(u8, SampleMetaData, Sample), 103 RemoveSample(u8), 104 PatternOperation(u8, PatternOperation), 105 SetOrder(u16, PatternOrder), 106 SetInitialSpeed(NonZero<u8>), 107 SetInitialTempo(NonZero<u8>), 108 SetGlobalVol(u8), 109} 110 111/// keep in sync with SongOperation 112#[derive(Clone, Debug)] 113pub(crate) enum ValidOperation { 114 SetVolume(u8, u8), 115 SetPan(u8, Pan), 116 SetSample(u8, SampleMetaData, Sample), 117 RemoveSample(u8), 118 PatternOperation(u8, PatternOperation), 119 SetOrder(u16, PatternOrder), 120 SetInitialSpeed(NonZero<u8>), 121 SetInitialTempo(NonZero<u8>), 122 SetGlobalVol(u8), 123} 124 125impl ValidOperation { 126 pub(crate) fn new( 127 op: SongOperation, 128 handle: &mut Collector, 129 song: &Song, 130 ) -> Result<ValidOperation, SongOperation> { 131 let valid = match op { 132 SongOperation::SetVolume(c, _) => usize::from(c) < Song::MAX_CHANNELS, 133 SongOperation::SetPan(c, _) => usize::from(c) < Song::MAX_CHANNELS, 134 SongOperation::SetSample(idx, _, _) => usize::from(idx) < Song::MAX_SAMPLES_INSTR, 135 SongOperation::RemoveSample(idx) => usize::from(idx) < Song::MAX_SAMPLES_INSTR, 136 SongOperation::PatternOperation(idx, op) => match song.patterns.get(usize::from(idx)) { 137 Some(pattern) => pattern.operation_is_valid(&op), 138 None => false, 139 }, 140 SongOperation::SetOrder(idx, _) => usize::from(idx) < Song::MAX_ORDERS, 141 SongOperation::SetInitialSpeed(_) => true, 142 SongOperation::SetInitialTempo(_) => true, 143 SongOperation::SetGlobalVol(_) => true, 144 }; 145 146 if valid { 147 Ok(match op { 148 SongOperation::SetVolume(c, v) => Self::SetVolume(c, v), 149 SongOperation::SetPan(c, pan) => Self::SetPan(c, pan), 150 SongOperation::SetSample(i, meta_data, sample) => { 151 handle.add_sample(sample.clone()); 152 Self::SetSample(i, meta_data, sample) 153 } 154 SongOperation::RemoveSample(i) => Self::RemoveSample(i), 155 SongOperation::PatternOperation(i, pattern_operation) => { 156 Self::PatternOperation(i, pattern_operation) 157 } 158 SongOperation::SetOrder(i, pattern_order) => Self::SetOrder(i, pattern_order), 159 SongOperation::SetInitialSpeed(s) => Self::SetInitialSpeed(s), 160 SongOperation::SetInitialTempo(t) => Self::SetInitialTempo(t), 161 SongOperation::SetGlobalVol(v) => Self::SetGlobalVol(v), 162 }) 163 } else { 164 Err(op) 165 } 166 } 167} 168 169impl simple_left_right::Absorb<ValidOperation> for Song { 170 fn absorb(&mut self, operation: ValidOperation) { 171 match operation { 172 ValidOperation::SetVolume(i, val) => self.volume[usize::from(i)] = val, 173 ValidOperation::SetPan(i, val) => self.pan[usize::from(i)] = val, 174 ValidOperation::SetSample(i, meta, sample) => { 175 self.samples[usize::from(i)] = Some((meta, sample)) 176 } 177 ValidOperation::RemoveSample(i) => self.samples[usize::from(i)] = None, 178 ValidOperation::PatternOperation(i, op) => { 179 self.patterns[usize::from(i)].apply_operation(op) 180 } 181 ValidOperation::SetOrder(i, order) => self.pattern_order[usize::from(i)] = order, 182 ValidOperation::SetInitialSpeed(s) => self.initial_speed = s, 183 ValidOperation::SetInitialTempo(t) => self.initial_tempo = t, 184 ValidOperation::SetGlobalVol(v) => self.global_volume = v, 185 } 186 } 187} 188 189#[derive(Debug, Clone, Copy)] 190pub enum Pan { 191 /// Value ranges from 0 to 64, with 32 being center 192 Value(u8), 193 Surround, 194 Disabled, 195} 196 197impl Default for Pan { 198 fn default() -> Self { 199 Self::Value(32) 200 } 201} 202 203#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] 204pub enum PatternOrder { 205 Number(u8), 206 #[default] 207 EndOfSong, 208 SkipOrder, 209}