old school music tracker audio backend
at main 182 lines 5.6 kB view raw
1use std::ops::{Index, IndexMut}; 2 3use crate::project::note_event::NoteEvent; 4use crate::project::Song; 5 6/// both row and channel are zero based. If this ever changes a lot of the implementations of 7/// Pattern need to be changed, because the searching starts working differently 8// don't change the Order of fields, as PartialOrd derive depends on it 9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 10pub struct InPatternPosition { 11 pub row: u16, 12 pub channel: u8, 13} 14 15#[cfg(test)] 16mod test { 17 use crate::project::pattern::InPatternPosition; 18 #[test] 19 fn position_ord() { 20 let one_zero = InPatternPosition { row: 1, channel: 0 }; 21 let zero_one = InPatternPosition { row: 0, channel: 1 }; 22 assert!(one_zero > zero_one); 23 } 24} 25 26#[derive(Clone, Debug)] 27pub struct Pattern { 28 rows: u16, 29 // Events are sorted with InPatternPosition as the key. 30 data: Vec<(InPatternPosition, NoteEvent)>, 31} 32 33const fn key(data: &(InPatternPosition, NoteEvent)) -> InPatternPosition { 34 data.0 35} 36 37impl Default for Pattern { 38 fn default() -> Self { 39 Self::new(Self::DEFAULT_ROWS) 40 } 41} 42 43impl Pattern { 44 pub const MAX_ROWS: u16 = 200; 45 46 pub const DEFAULT_ROWS: u16 = 64; 47 48 /// panics if len larger than 'Self::MAX_LEN' 49 pub const fn new(len: u16) -> Self { 50 assert!(len <= Self::MAX_ROWS); 51 Self { 52 rows: len, 53 data: Vec::new(), 54 } 55 } 56 57 /// panics it the new len is larger than 'Self::MAX_LEN' 58 /// deletes the data on higher rows 59 pub fn set_length(&mut self, new_len: u16) { 60 assert!(new_len <= Self::MAX_ROWS); 61 // gets the index of the first element of the first row to be removed 62 if new_len < self.rows { 63 let idx = self.data.partition_point(|(pos, _)| pos.row < new_len); 64 self.data.truncate(idx); 65 } 66 self.rows = new_len; 67 } 68 69 /// overwrites the event if the row already has an event for that channel 70 /// panics if the row position is larger than current amount of rows 71 pub fn set_event(&mut self, position: InPatternPosition, event: NoteEvent) { 72 assert!(position.row < self.rows); 73 match self.data.binary_search_by_key(&position, key) { 74 Ok(idx) => self.data[idx].1 = event, 75 Err(idx) => self.data.insert(idx, (position, event)), 76 } 77 } 78 79 pub fn get_event(&self, index: InPatternPosition) -> Option<&NoteEvent> { 80 self.data 81 .binary_search_by_key(&index, key) 82 .ok() 83 .map(|idx| &self.data[idx].1) 84 } 85 86 pub fn get_event_mut(&mut self, index: InPatternPosition) -> Option<&mut NoteEvent> { 87 self.data 88 .binary_search_by_key(&index, key) 89 .ok() 90 .map(|idx| &mut self.data[idx].1) 91 } 92 93 /// if there is no event, does nothing 94 pub fn remove_event(&mut self, position: InPatternPosition) { 95 if let Ok(index) = self.data.binary_search_by_key(&position, key) { 96 self.data.remove(index); 97 } 98 } 99 100 pub const fn row_count(&self) -> u16 { 101 self.rows 102 } 103 104 /// Panics if the Operation is invalid 105 pub fn apply_operation(&mut self, op: PatternOperation) { 106 match op { 107 PatternOperation::SetLength { new_len } => self.set_length(new_len), 108 PatternOperation::SetEvent { position, event } => self.set_event(position, event), 109 PatternOperation::RemoveEvent { position } => self.remove_event(position), 110 } 111 } 112 113 pub const fn operation_is_valid(&self, op: &PatternOperation) -> bool { 114 match op { 115 PatternOperation::SetLength { new_len } => *new_len < Self::MAX_ROWS, 116 PatternOperation::SetEvent { position, event: _ } => { 117 position.row < self.rows && position.channel as usize <= Song::MAX_CHANNELS 118 } 119 PatternOperation::RemoveEvent { position: _ } => true, 120 } 121 } 122 123 pub fn is_empty(&self) -> bool { 124 self.data.is_empty() 125 } 126} 127 128impl Index<u16> for Pattern { 129 type Output = [(InPatternPosition, NoteEvent)]; 130 131 /// # Out of Bounds 132 /// Debug: Panic 133 /// 134 /// Release: Empty slice 135 fn index(&self, index: u16) -> &Self::Output { 136 // only a debug assert because if out of bounds the output is simply empty 137 debug_assert!(index <= self.rows); 138 let start_position = self.data.partition_point(|(pos, _)| { 139 *pos < InPatternPosition { 140 row: index, 141 channel: 0, 142 } 143 }); 144 // only search after start_position 145 let end_position = 146 self.data[start_position..self.data.len()].partition_point(|(pos, _)| { 147 *pos < InPatternPosition { 148 row: index + 1, 149 channel: 0, 150 } 151 }) + start_position; 152 &self.data[start_position..end_position] 153 } 154} 155 156impl Index<InPatternPosition> for Pattern { 157 type Output = NoteEvent; 158 159 fn index(&self, index: InPatternPosition) -> &Self::Output { 160 self.get_event(index).unwrap() 161 } 162} 163 164impl IndexMut<InPatternPosition> for Pattern { 165 fn index_mut(&mut self, index: InPatternPosition) -> &mut Self::Output { 166 self.get_event_mut(index).unwrap() 167 } 168} 169 170#[derive(Debug, Clone, Copy)] 171pub enum PatternOperation { 172 SetLength { 173 new_len: u16, 174 }, 175 SetEvent { 176 position: InPatternPosition, 177 event: NoteEvent, 178 }, 179 RemoveEvent { 180 position: InPatternPosition, 181 }, 182}