Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
6.6 kB · 206 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207use core::ops::{Index, IndexMut};use core::slice;
use alloc::vec::Vec;
use crate::project::Song;use crate::project::note_event::NoteEvent;
/// both row and channel are zero based.// If this ever changes a lot of the implementations of// Pattern need to be changed, because the searching starts working differently// don't change the Order of fields, as PartialOrd derive depends on it#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]pub struct InPatternPosition { pub row: u16, pub channel: u8,}
#[cfg(test)]mod test { use crate::project::pattern::InPatternPosition; #[test] fn position_ord() { let one_zero = InPatternPosition { row: 1, channel: 0 }; let zero_one = InPatternPosition { row: 0, channel: 1 }; assert!(one_zero > zero_one); }}
#[derive(Clone, Debug, PartialEq, Eq)]pub struct Pattern { rows: u16, // Events are sorted with InPatternPosition as the key. // TODO: figure out a better representation. // Maybe use array of structs for indexing, as InPatternPosition is only 32 bytes (maybe also make it actually 32 bytes with 0 padding // so comparisons don't have to care about padding) // // Maybe also use the cache optimized Binary search layout // // Or use a BTreeMap. this could have higher insertion speeds, but i probably don't care about that if i can provide an API that takes a // list of changes and only resorts it at the end // // This shouln't have any impact on the API of Pattern, as i already hid the row iterator away // and the iter element doesn't force the Position and the Event to live next to each other data: Vec<(InPatternPosition, NoteEvent)>,}
pub struct RowIter<'a> { inner: slice::Iter<'a, (InPatternPosition, NoteEvent)>,}
// TODO: maybe even provide a mut API based on this, now that the Position can't be modified anymoreimpl<'a> Iterator for RowIter<'a> { type Item = (InPatternPosition, &'a NoteEvent);
fn next(&mut self) -> Option<Self::Item> { self.inner.next().map(|(pos, event)| (*pos, event)) }}
const fn key(data: &(InPatternPosition, NoteEvent)) -> InPatternPosition { data.0}
impl Default for Pattern { fn default() -> Self { Self::new(Self::DEFAULT_ROWS) }}
impl Pattern { pub const MAX_ROWS: u16 = 200;
pub const DEFAULT_ROWS: u16 = 64;
/// panics if len larger than 'Self::MAX_LEN' pub const fn new(len: u16) -> Self { assert!(len <= Self::MAX_ROWS); Self { rows: len, data: Vec::new(), } }
/// panics it the new len is larger than 'Self::MAX_LEN' /// deletes the data on higher rows pub fn set_length(&mut self, new_len: u16) { assert!(new_len <= Self::MAX_ROWS); // gets the index of the first element of the first row to be removed if new_len < self.rows { let idx = self.data.partition_point(|(pos, _)| pos.row < new_len); self.data.truncate(idx); } self.rows = new_len; }
/// overwrites the event if the row already has an event for that channel /// panics if the row position is larger than current amount of rows pub fn set_event(&mut self, position: InPatternPosition, event: NoteEvent) { assert!(position.row < self.rows); match self.data.binary_search_by_key(&position, key) { Ok(idx) => self.data[idx].1 = event, Err(idx) => self.data.insert(idx, (position, event)), } }
pub fn get_event(&self, index: InPatternPosition) -> Option<&NoteEvent> { self.data .binary_search_by_key(&index, key) .ok() .map(|idx| &self.data[idx].1) }
pub fn get_event_mut(&mut self, index: InPatternPosition) -> Option<&mut NoteEvent> { self.data .binary_search_by_key(&index, key) .ok() .map(|idx| &mut self.data[idx].1) }
/// if there is no event, does nothing pub fn remove_event(&mut self, position: InPatternPosition) { if let Ok(index) = self.data.binary_search_by_key(&position, key) { self.data.remove(index); } }
pub const fn row_count(&self) -> u16 { self.rows }
/// Panics if the Operation is invalid pub fn apply_operation(&mut self, op: PatternOperation) { match op { PatternOperation::SetLength { new_len } => self.set_length(new_len), PatternOperation::SetEvent { position, event } => self.set_event(position, event), PatternOperation::RemoveEvent { position } => self.remove_event(position), } }
pub const fn operation_is_valid(&self, op: &PatternOperation) -> bool { match op { PatternOperation::SetLength { new_len } => *new_len < Self::MAX_ROWS, PatternOperation::SetEvent { position, event: _ } => { position.row < self.rows && position.channel as usize <= Song::MAX_CHANNELS } PatternOperation::RemoveEvent { position: _ } => true, } }
pub fn is_empty(&self) -> bool { self.data.is_empty() }
// TODO: think about out of bounds behaviour pub fn row(&self, row: u16) -> RowIter<'_> { let start_position = self.data.partition_point(|(pos, _)| { *pos < InPatternPosition { row: row, channel: 0, } }); // only search after start_position let end_position = self.data[start_position..self.data.len()].partition_point(|(pos, _)| { *pos < InPatternPosition { row: row + 1, channel: 0, } }) + start_position; RowIter { inner: self.data[start_position..end_position].iter(), } }}
// TODO: think about panics here / provide non-panicking APIimpl Index<InPatternPosition> for Pattern { type Output = NoteEvent;
#[track_caller] fn index(&self, index: InPatternPosition) -> &Self::Output { self.get_event(index).unwrap() }}
impl IndexMut<InPatternPosition> for Pattern { #[track_caller] fn index_mut(&mut self, index: InPatternPosition) -> &mut Self::Output { self.get_event_mut(index).unwrap() }}
#[derive(Debug, Clone, Copy)]pub enum PatternOperation { SetLength { new_len: u16, }, SetEvent { position: InPatternPosition, event: NoteEvent, }, RemoveEvent { position: InPatternPosition, },}