use std::ops::{Index, IndexMut};
use crate::project::note_event::NoteEvent;
use crate::project::Song;
#[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)]
pub struct Pattern {
rows: u16,
data: Vec<(InPatternPosition, NoteEvent)>,
}
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;
pub const fn new(len: u16) -> Self {
assert!(len <= Self::MAX_ROWS);
Self {
rows: len,
data: Vec::new(),
}
}
pub fn set_length(&mut self, new_len: u16) {
assert!(new_len <= Self::MAX_ROWS);
if new_len < self.rows {
let idx = self.data.partition_point(|(pos, _)| pos.row < new_len);
self.data.truncate(idx);
}
self.rows = new_len;
}
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)
}
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
}
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()
}
}
impl Index<u16> for Pattern {
type Output = [(InPatternPosition, NoteEvent)];
fn index(&self, index: u16) -> &Self::Output {
debug_assert!(index <= self.rows);
let start_position = self.data.partition_point(|(pos, _)| {
*pos < InPatternPosition {
row: index,
channel: 0,
}
});
let end_position =
self.data[start_position..self.data.len()].partition_point(|(pos, _)| {
*pos < InPatternPosition {
row: index + 1,
channel: 0,
}
}) + start_position;
&self.data[start_position..end_position]
}
}
impl Index<InPatternPosition> for Pattern {
type Output = NoteEvent;
fn index(&self, index: InPatternPosition) -> &Self::Output {
self.get_event(index).unwrap()
}
}
impl IndexMut<InPatternPosition> for Pattern {
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,
},
}