#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
pub(crate) mod de;
pub(crate) mod plock;
pub mod track;
pub mod types;
use self::{
plock::ParameterLockPool,
types::{Speed, TimeMode},
};
use crate::{
defaults::default_tracks,
error::{ParameterError, RytmError, SysexConversionError},
impl_sysex_compatible,
object::pattern::track::Track,
sysex::{SysexCompatible, SysexMeta, SysexType, PATTERN_SYSEX_SIZE},
util::{
arc_mutex_owner, assemble_u32_from_u8_array_be, break_u32_into_u8_array_be, from_s_u16_t,
to_s_u16_t_union_b,
},
AnySysexType,
};
use derivative::Derivative;
use parking_lot::Mutex;
use rytm_rs_macro::parameter_range;
use rytm_sys::{ar_pattern_raw_to_syx, ar_pattern_t, ar_pattern_track_t, ar_sysex_meta_t};
use serde::Serialize;
use std::sync::Arc;
pub use track::{
trig::{types::*, Trig},
types::*,
};
impl_sysex_compatible!(
Pattern,
ar_pattern_t,
ar_pattern_raw_to_syx,
SysexType::Pattern,
PATTERN_SYSEX_SIZE
);
#[derive(Derivative, Clone, Serialize)]
#[derivative(Debug)]
pub struct Pattern {
#[derivative(Debug = "ignore")]
sysex_meta: SysexMeta,
pub(crate) index: usize,
version: u32,
#[serde(serialize_with = "arc_mutex_owner::serialize")]
pub(crate) fx_track: Arc<Mutex<Track>>,
tracks: Vec<Track>,
master_length: u16,
master_change: u16,
kit_number: u8,
swing_amount: u8,
time_mode: TimeMode,
speed: Speed,
global_quantize: u8,
bpm: f32,
pad_scale_per_pattern: u8,
#[derivative(Debug = "ignore")]
#[serde(serialize_with = "arc_mutex_owner::serialize")]
pub(crate) parameter_lock_pool: Arc<Mutex<ParameterLockPool>>,
}
impl From<&Pattern> for ar_pattern_t {
fn from(pattern: &Pattern) -> Self {
let mut tracks: [ar_pattern_track_t; 13] = [ar_pattern_track_t::default(); 13];
for (i, track) in pattern.tracks.iter().enumerate() {
if i == 12 {
tracks[i] = (&*pattern.fx_track.lock()).into();
break;
}
tracks[i] = track.into();
}
let bpm = (pattern.bpm * 120.0) as u16;
Self {
magic: break_u32_into_u8_array_be(pattern.version),
tracks,
plock_seqs: pattern.parameter_lock_pool.lock().as_raw(),
master_length: to_s_u16_t_union_b(pattern.master_length),
master_chg_msb: (pattern.master_change >> 8) as u8,
master_chg_lsb: pattern.master_change as u8,
kit_number: pattern.kit_number,
swing_amount: pattern.swing_amount,
time_mode: pattern.time_mode.into(),
master_speed: pattern.speed.into(),
global_quantize: pattern.global_quantize,
bpm_msb: (bpm >> 8) as u8,
bpm_lsb: bpm as u8,
pad_scale_per_pattern: pattern.pad_scale_per_pattern,
}
}
}
impl Pattern {
#[allow(clippy::too_many_arguments)]
pub(crate) fn try_from_raw(
sysex_meta: SysexMeta,
raw_pattern: &ar_pattern_t,
) -> Result<Self, RytmError> {
let is_targeting_work_buffer = sysex_meta.is_targeting_work_buffer();
let index = sysex_meta.get_normalized_object_index();
let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::from_raw(
&raw_pattern.plock_seqs,
index,
is_targeting_work_buffer,
)));
let fx_track = Arc::new(Mutex::new(Track::try_from_raw(
12,
&raw_pattern.tracks[12],
¶meter_lock_pool,
None,
)?));
let mut tracks: Vec<Track> = default_tracks(0, false, None);
for (i, track) in raw_pattern.tracks.iter().enumerate() {
if i == 12 {
break;
}
tracks[i] =
Track::try_from_raw(i, track, ¶meter_lock_pool, Some(Arc::clone(&fx_track)))?;
}
let version = assemble_u32_from_u8_array_be(&raw_pattern.magic);
let bpm = ((raw_pattern.bpm_msb as u16) << 8) | (raw_pattern.bpm_lsb as u16);
let bpm = bpm as f32 / 120.0;
let master_change =
((raw_pattern.master_chg_msb as u16) << 8) | (raw_pattern.master_chg_lsb as u16);
Ok(Self {
index,
sysex_meta,
version,
tracks,
fx_track,
parameter_lock_pool,
master_length: unsafe { from_s_u16_t(raw_pattern.master_length) },
master_change,
kit_number: raw_pattern.kit_number,
swing_amount: raw_pattern.swing_amount,
time_mode: raw_pattern.time_mode.try_into()?,
speed: raw_pattern.master_speed.try_into()?,
global_quantize: raw_pattern.global_quantize,
bpm,
pad_scale_per_pattern: raw_pattern.pad_scale_per_pattern,
})
}
pub(crate) fn as_raw_parts(&self) -> (SysexMeta, ar_pattern_t) {
(self.sysex_meta, self.into())
}
#[parameter_range(range = "index:0..=127")]
pub fn try_default(index: usize) -> Result<Self, RytmError> {
Self::try_default_with_device_id(index, 0)
}
#[parameter_range(range = "index:0..=127", range = "device_id:0..=127")]
pub fn try_default_with_device_id(index: usize, device_id: u8) -> Result<Self, RytmError> {
let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::default()));
let mut fx_track = Track::try_default(12, index, false, None).unwrap();
fx_track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
let fx_track = Arc::new(Mutex::new(fx_track));
let mut tracks = default_tracks(0, true, Some(Arc::clone(&fx_track)));
for track in &mut tracks {
track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
for trig in track.trigs_mut() {
trig.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
}
}
Ok(Self {
sysex_meta: SysexMeta::try_default_for_pattern(index, Some(device_id))?,
index,
version: 5,
tracks,
fx_track,
parameter_lock_pool,
master_length: 16,
master_change: 1,
kit_number: 0,
swing_amount: 0,
time_mode: TimeMode::Normal,
speed: Speed::default(),
global_quantize: 0,
bpm: 120.0,
pad_scale_per_pattern: 0x01,
})
}
#[allow(clippy::missing_panics_doc)]
pub fn work_buffer_default() -> Self {
Self::work_buffer_default_with_device_id(0)
}
#[allow(clippy::missing_panics_doc)]
pub fn work_buffer_default_with_device_id(device_id: u8) -> Self {
let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::default()));
let mut fx_track = Track::try_default(12, 0, true, None).unwrap();
fx_track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
let fx_track = Arc::new(Mutex::new(fx_track));
let mut tracks = default_tracks(0, true, Some(Arc::clone(&fx_track)));
for track in &mut tracks {
track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
for trig in track.trigs_mut() {
trig.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
}
}
Self {
sysex_meta: SysexMeta::default_for_pattern_in_work_buffer(Some(device_id)),
index: 0,
version: 5,
tracks,
fx_track,
parameter_lock_pool,
master_length: 16,
master_change: 1,
kit_number: 0,
swing_amount: 0,
time_mode: TimeMode::Normal,
speed: Speed::default(),
global_quantize: 0,
bpm: 120.0,
pad_scale_per_pattern: 0x01,
}
}
pub fn tracks_mut(&mut self) -> &mut [Track] {
&mut self.tracks
}
#[parameter_range(range = "master_length:1..=1024")]
pub fn set_master_length(&mut self, master_length: usize) -> Result<(), RytmError> {
self.master_length = master_length as u16;
Ok(())
}
#[parameter_range(range = "swing_amount:50..=80")]
pub fn set_swing_amount(&mut self, swing_amount: usize) -> Result<(), RytmError> {
self.swing_amount = (swing_amount - 50) as u8;
Ok(())
}
pub fn set_speed(&mut self, speed: Speed) {
self.speed = speed;
}
#[parameter_range(range = "global_quantize:0..=127")]
pub fn set_global_quantize(&mut self, global_quantize: usize) -> Result<(), RytmError> {
self.global_quantize = global_quantize as u8;
Ok(())
}
#[parameter_range(range = "kit_number:0..=127")]
pub fn set_kit_number(&mut self, kit_number: usize) -> Result<(), RytmError> {
self.kit_number = kit_number as u8;
Ok(())
}
pub fn set_time_mode(&mut self, time_mode: TimeMode) {
self.time_mode = time_mode;
}
#[parameter_range(range = "master_change:1..=1024")]
pub fn set_master_change(&mut self, master_change: usize) -> Result<(), RytmError> {
self.master_change = master_change as u16;
Ok(())
}
#[parameter_range(range = "bpm:30.0..=300.0")]
pub fn set_bpm(&mut self, bpm: f32) -> Result<(), RytmError> {
self.bpm = bpm;
Ok(())
}
pub fn tracks(&self) -> &[Track] {
&self.tracks
}
pub const fn master_length(&self) -> usize {
self.master_length as usize
}
pub const fn swing_amount(&self) -> usize {
self.swing_amount as usize + 50
}
pub const fn speed(&self) -> Speed {
self.speed
}
pub const fn global_quantize(&self) -> usize {
self.global_quantize as usize
}
pub const fn kit_number(&self) -> usize {
self.kit_number as usize
}
pub const fn time_mode(&self) -> TimeMode {
self.time_mode
}
pub const fn master_change(&self) -> usize {
self.master_change as usize
}
pub const fn bpm(&self) -> f32 {
self.bpm
}
pub const fn index(&self) -> usize {
self.index
}
pub const fn is_work_buffer_pattern(&self) -> bool {
self.sysex_meta.is_targeting_work_buffer()
}
pub const fn structure_version(&self) -> u32 {
self.version
}
pub fn clear_all_plocks(&mut self) {
self.parameter_lock_pool.lock().clear_all_plocks();
}
#[parameter_range(range = "track_index:0..=12")]
pub fn clear_all_plocks_for_track(&mut self, track_index: u8) -> Result<(), RytmError> {
self.parameter_lock_pool
.lock()
.clear_all_plocks_for_track(track_index);
Ok(())
}
pub(crate) fn set_device_id(&mut self, device_id: u8) {
self.sysex_meta.set_device_id(device_id);
}
}