use crate::{error::RytmError, util::stable_partition, RytmError::ParameterLockMemoryFull};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
#[derive(Derivative, Clone, Copy, Serialize, Deserialize)]
#[derivative(Debug)]
pub struct PlockSeq {
pub track_nr: u8,
pub plock_type: u8,
#[serde(with = "BigArray")]
pub data: [u8; 64],
}
impl Default for PlockSeq {
fn default() -> Self {
Self {
track_nr: 0xFF,
plock_type: 0xFF,
data: [0xFF; 64],
}
}
}
impl From<rytm_sys::ar_plock_seq_t> for PlockSeq {
fn from(raw: rytm_sys::ar_plock_seq_t) -> Self {
Self {
track_nr: raw.track_nr,
plock_type: raw.plock_type,
data: raw.data,
}
}
}
impl From<&PlockSeq> for rytm_sys::ar_plock_seq_t {
fn from(plock_seq: &PlockSeq) -> Self {
Self {
track_nr: plock_seq.track_nr,
plock_type: plock_seq.plock_type,
data: plock_seq.data,
}
}
}
#[derive(Derivative, Clone, Serialize, Deserialize)]
#[derivative(Debug)]
pub struct ParameterLockPool {
pub owner_pattern_index: usize,
pub inner: Vec<PlockSeq>,
pub is_owner_pattern_work_buffer: bool,
}
impl Default for ParameterLockPool {
fn default() -> Self {
let mut inner = Vec::with_capacity(72);
for _ in 0..72 {
inner.push(PlockSeq::default());
}
Self {
owner_pattern_index: 0,
inner,
is_owner_pattern_work_buffer: false,
}
}
}
impl ParameterLockPool {
pub fn as_raw(&self) -> [rytm_sys::ar_plock_seq_t; 72] {
self.inner
.iter()
.map(std::convert::Into::into)
.collect::<Vec<_>>()
.try_into()
.expect("This can not fail until we change the size of 72 anywhere.")
}
pub fn from_raw(
raw: &[rytm_sys::ar_plock_seq_t; 72],
owner_pattern_index: usize,
is_owner_pattern_work_buffer: bool,
) -> Self {
let inner = raw
.iter()
.map(|plock_seq| (*plock_seq).into())
.collect::<Vec<_>>();
Self {
owner_pattern_index,
inner,
is_owner_pattern_work_buffer,
}
}
pub fn set_basic_plock(
&mut self,
trig_index: usize,
track_index: u8,
plock_type: u8,
value: u8,
) -> Result<(), RytmError> {
if let Some(plock) = self.inner.iter_mut().find(|plock_seq| {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
}) {
plock.data[trig_index] = value;
return Ok(());
}
if let Some(empty_slot) = self
.inner
.iter_mut()
.find(|plock_seq| plock_seq.track_nr == 0xFF || plock_seq.plock_type == 0xFF)
{
empty_slot.track_nr = track_index;
empty_slot.plock_type = plock_type;
empty_slot.data[trig_index] = value;
return Ok(());
}
Err(ParameterLockMemoryFull)
}
pub fn set_compound_plock(
&mut self,
trig_index: usize,
track_index: u8,
plock_type: u8,
value: u16,
) -> Result<(), RytmError> {
const ADJACENT_PLOCK_SLOT_TRACK_NUMBER_BYTE: u8 = 128;
const ADJACENT_PLOCK_SLOT_TYPE_BYTE: u8 = 128;
let value_msb = (value >> 8) as u8;
let value_lsb = value as u8;
stable_partition(&mut self.inner[..], |plock_seq| {
plock_seq.track_nr != 0xFF || plock_seq.plock_type != 0xFF
});
let last_slot = &self.inner[self.inner.len() - 1];
let last_slot_available = last_slot.track_nr == 0xFF || last_slot.plock_type == 0xFF;
if let Some((i, found_plock)) =
self.inner
.iter_mut()
.enumerate()
.find(|(_, plock_seq)| -> bool {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
})
{
found_plock.data[trig_index] = value_msb;
self.inner[i + 1].data[trig_index] = value_lsb;
return Ok(());
}
if !last_slot_available {
return Err(ParameterLockMemoryFull);
}
if let Some((i, found_empty_slot)) = self
.inner
.iter_mut()
.enumerate()
.find(|(_, plock_seq)| plock_seq.track_nr == 0xFF || plock_seq.plock_type == 0xFF)
{
found_empty_slot.track_nr = track_index;
found_empty_slot.plock_type = plock_type;
found_empty_slot.data[trig_index] = value_msb;
self.inner[i + 1].track_nr = ADJACENT_PLOCK_SLOT_TRACK_NUMBER_BYTE;
self.inner[i + 1].plock_type = ADJACENT_PLOCK_SLOT_TYPE_BYTE;
self.inner[i + 1].data[trig_index] = value_lsb;
return Ok(());
}
Err(ParameterLockMemoryFull)
}
pub fn get_basic_plock(
&self,
trig_index: usize,
track_index: u8,
plock_type: u8,
) -> Option<u8> {
if let Some(plock) = self.inner.iter().find(|plock_seq| {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
}) {
return Some(plock.data[trig_index]);
}
None
}
pub fn get_compound_plock(
&self,
trig_index: usize,
track_index: u8,
plock_type: u8,
) -> Option<u16> {
if let Some((i, plock)) = self
.inner
.iter()
.enumerate()
.find(|(_, plock_seq)| -> bool {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
})
{
return Some(
((plock.data[trig_index] as u16) << 8)
| (self.inner[i + 1].data[trig_index] as u16),
);
}
None
}
pub fn clear_basic_plock(&mut self, trig_index: usize, track_index: u8, plock_type: u8) {
let mut plock_seq_index_which_we_cleared_from: Option<usize> = None;
if let Some((i, plock)) = self.inner.iter_mut().enumerate().find(|(_, plock_seq)| {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
}) {
plock.data[trig_index] = 0xFF;
plock_seq_index_which_we_cleared_from = Some(i);
}
if let Some(i) = plock_seq_index_which_we_cleared_from {
let plock = &mut self.inner[i];
if plock.data.iter_mut().all(|byte| *byte == 0xFF) {
plock.track_nr = 0xFF;
plock.plock_type = 0xFF;
}
}
}
pub fn clear_compound_plock(&mut self, trig_index: usize, track_index: u8, plock_type: u8) {
let mut plock_seq_index_which_we_cleared_from: Option<usize> = None;
if let Some((i, plock)) = self.inner.iter_mut().enumerate().find(|(_, plock_seq)| {
plock_seq.track_nr == track_index && plock_seq.plock_type == plock_type
}) {
plock.data[trig_index] = 0xFF;
self.inner[i + 1].data[trig_index] = 0xFF;
plock_seq_index_which_we_cleared_from = Some(i);
}
if let Some(i) = plock_seq_index_which_we_cleared_from {
let plock = &mut self.inner[i];
if plock.data.iter_mut().all(|byte| *byte == 0xFF) {
plock.track_nr = 0xFF;
plock.plock_type = 0xFF;
self.inner[i + 1].track_nr = 0xFF;
self.inner[i + 1].plock_type = 0xFF;
}
}
}
pub fn set_fx_basic_plock(
&mut self,
trig_index: usize,
plock_type: u8,
value: u8,
) -> Result<(), RytmError> {
self.set_basic_plock(trig_index, 12, plock_type, value)
}
pub fn set_fx_compound_plock(
&mut self,
trig_index: usize,
plock_type: u8,
value: u16,
) -> Result<(), RytmError> {
self.set_compound_plock(trig_index, 12, plock_type, value)
}
pub fn get_fx_basic_plock(&self, trig_index: usize, plock_type: u8) -> Option<u8> {
self.get_basic_plock(trig_index, 12, plock_type)
}
pub fn get_fx_compound_plock(&self, trig_index: usize, plock_type: u8) -> Option<u16> {
self.get_compound_plock(trig_index, 12, plock_type)
}
pub fn clear_fx_basic_plock(&mut self, trig_index: usize, plock_type: u8) {
self.clear_basic_plock(trig_index, 12, plock_type);
}
pub fn clear_fx_compound_plock(&mut self, trig_index: usize, plock_type: u8) {
self.clear_compound_plock(trig_index, 12, plock_type);
}
pub fn clear_all_plocks(&mut self) {
for plock_seq in &mut self.inner {
plock_seq.track_nr = 0xFF;
plock_seq.plock_type = 0xFF;
for byte in &mut plock_seq.data {
*byte = 0xFF;
}
}
}
pub fn clear_all_plocks_for_track(&mut self, track_index: u8) {
for plock_seq in &mut self.inner {
if plock_seq.track_nr == track_index {
plock_seq.track_nr = 0xFF;
plock_seq.plock_type = 0xFF;
for byte in &mut plock_seq.data {
*byte = 0xFF;
}
}
}
}
}