1#![allow(
5 clippy::cast_lossless,
6 clippy::cast_possible_truncation,
7 clippy::cast_sign_loss
8)]
9pub(crate) mod de;
14pub(crate) mod plock;
15
16pub mod track;
18pub mod types;
20
21use self::{
22 plock::ParameterLockPool,
23 types::{Speed, TimeMode},
24};
25use crate::{
26 defaults::default_tracks,
27 error::{ParameterError, RytmError, SysexConversionError},
28 impl_sysex_compatible,
29 object::pattern::track::Track,
30 sysex::{SysexCompatible, SysexMeta, SysexType, PATTERN_SYSEX_SIZE},
31 util::{
32 arc_mutex_owner, assemble_u32_from_u8_array_be, break_u32_into_u8_array_be, from_s_u16_t,
33 to_s_u16_t_union_b,
34 },
35 AnySysexType,
36};
37use derivative::Derivative;
38use parking_lot::Mutex;
39use rytm_rs_macro::parameter_range;
40use rytm_sys::{ar_pattern_raw_to_syx, ar_pattern_t, ar_pattern_track_t, ar_sysex_meta_t};
41use serde::Serialize;
42use std::sync::Arc;
43pub use track::{
44 trig::{types::*, Trig},
45 types::*,
46};
47
48impl_sysex_compatible!(
49 Pattern,
50 ar_pattern_t,
51 ar_pattern_raw_to_syx,
52 SysexType::Pattern,
53 PATTERN_SYSEX_SIZE
54);
55
56#[derive(Derivative, Clone, Serialize)]
60#[derivative(Debug)]
61pub struct Pattern {
62 #[derivative(Debug = "ignore")]
63 sysex_meta: SysexMeta,
64 pub(crate) index: usize,
68 version: u32,
70 #[serde(serialize_with = "arc_mutex_owner::serialize")]
72 pub(crate) fx_track: Arc<Mutex<Track>>,
73 tracks: Vec<Track>,
77 master_length: u16,
86 master_change: u16,
95 kit_number: u8,
100 swing_amount: u8,
107 time_mode: TimeMode,
112 speed: Speed,
122 global_quantize: u8,
126 bpm: f32,
130
131 pad_scale_per_pattern: u8,
133
134 #[derivative(Debug = "ignore")]
136 #[serde(serialize_with = "arc_mutex_owner::serialize")]
137 pub(crate) parameter_lock_pool: Arc<Mutex<ParameterLockPool>>,
138}
139
140impl From<&Pattern> for ar_pattern_t {
141 fn from(pattern: &Pattern) -> Self {
142 let mut tracks: [ar_pattern_track_t; 13] = [ar_pattern_track_t::default(); 13];
143
144 for (i, track) in pattern.tracks.iter().enumerate() {
145 if i == 12 {
146 tracks[i] = (&*pattern.fx_track.lock()).into();
147 break;
148 }
149 tracks[i] = track.into();
150 }
151
152 let bpm = (pattern.bpm * 120.0) as u16;
153 Self {
154 magic: break_u32_into_u8_array_be(pattern.version),
155 tracks,
156 plock_seqs: pattern.parameter_lock_pool.lock().as_raw(),
157 master_length: to_s_u16_t_union_b(pattern.master_length),
158 master_chg_msb: (pattern.master_change >> 8) as u8,
159 master_chg_lsb: pattern.master_change as u8,
160 kit_number: pattern.kit_number,
161 swing_amount: pattern.swing_amount,
162 time_mode: pattern.time_mode.into(),
163 master_speed: pattern.speed.into(),
164 global_quantize: pattern.global_quantize,
165 bpm_msb: (bpm >> 8) as u8,
166 bpm_lsb: bpm as u8,
167 pad_scale_per_pattern: pattern.pad_scale_per_pattern,
168 }
169 }
170}
171
172impl Pattern {
173 #[allow(clippy::too_many_arguments)]
174 pub(crate) fn try_from_raw(
175 sysex_meta: SysexMeta,
176 raw_pattern: &ar_pattern_t,
177 ) -> Result<Self, RytmError> {
178 let is_targeting_work_buffer = sysex_meta.is_targeting_work_buffer();
179 let index = sysex_meta.get_normalized_object_index();
180
181 let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::from_raw(
182 &raw_pattern.plock_seqs,
183 index,
184 is_targeting_work_buffer,
185 )));
186
187 let fx_track = Arc::new(Mutex::new(Track::try_from_raw(
188 12,
189 &raw_pattern.tracks[12],
190 ¶meter_lock_pool,
191 None,
192 )?));
193
194 let mut tracks: Vec<Track> = default_tracks(0, false, None);
197
198 for (i, track) in raw_pattern.tracks.iter().enumerate() {
199 if i == 12 {
200 break;
201 }
202 tracks[i] =
203 Track::try_from_raw(i, track, ¶meter_lock_pool, Some(Arc::clone(&fx_track)))?;
204 }
205
206 let version = assemble_u32_from_u8_array_be(&raw_pattern.magic);
207
208 let bpm = ((raw_pattern.bpm_msb as u16) << 8) | (raw_pattern.bpm_lsb as u16);
209 let bpm = bpm as f32 / 120.0;
210
211 let master_change =
212 ((raw_pattern.master_chg_msb as u16) << 8) | (raw_pattern.master_chg_lsb as u16);
213
214 Ok(Self {
215 index,
216 sysex_meta,
217 version,
218 tracks,
219 fx_track,
220 parameter_lock_pool,
221 master_length: unsafe { from_s_u16_t(raw_pattern.master_length) },
222 master_change,
223 kit_number: raw_pattern.kit_number,
224 swing_amount: raw_pattern.swing_amount,
225 time_mode: raw_pattern.time_mode.try_into()?,
226 speed: raw_pattern.master_speed.try_into()?,
227 global_quantize: raw_pattern.global_quantize,
228 bpm,
229 pad_scale_per_pattern: raw_pattern.pad_scale_per_pattern,
230 })
231 }
232
233 pub(crate) fn as_raw_parts(&self) -> (SysexMeta, ar_pattern_t) {
234 (self.sysex_meta, self.into())
235 }
236
237 #[parameter_range(range = "index:0..=127")]
241 pub fn try_default(index: usize) -> Result<Self, RytmError> {
242 Self::try_default_with_device_id(index, 0)
243 }
244
245 #[parameter_range(range = "index:0..=127", range = "device_id:0..=127")]
250 pub fn try_default_with_device_id(index: usize, device_id: u8) -> Result<Self, RytmError> {
251 let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::default()));
252
253 let mut fx_track = Track::try_default(12, index, false, None).unwrap();
254 fx_track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
255 let fx_track = Arc::new(Mutex::new(fx_track));
256
257 let mut tracks = default_tracks(0, true, Some(Arc::clone(&fx_track)));
258 for track in &mut tracks {
259 track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
260 for trig in track.trigs_mut() {
261 trig.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
262 }
263 }
264
265 Ok(Self {
266 sysex_meta: SysexMeta::try_default_for_pattern(index, Some(device_id))?,
267 index,
268 version: 5,
269 tracks,
270 fx_track,
271 parameter_lock_pool,
272 master_length: 16,
273 master_change: 1,
274 kit_number: 0,
275 swing_amount: 0,
276 time_mode: TimeMode::Normal,
277 speed: Speed::default(),
278 global_quantize: 0,
279 bpm: 120.0,
280 pad_scale_per_pattern: 0x01,
281 })
282 }
283
284 #[allow(clippy::missing_panics_doc)]
286 pub fn work_buffer_default() -> Self {
288 Self::work_buffer_default_with_device_id(0)
289 }
290
291 #[allow(clippy::missing_panics_doc)]
293 pub fn work_buffer_default_with_device_id(device_id: u8) -> Self {
295 let parameter_lock_pool = Arc::new(Mutex::new(ParameterLockPool::default()));
296
297 let mut fx_track = Track::try_default(12, 0, true, None).unwrap();
298 fx_track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
299 let fx_track = Arc::new(Mutex::new(fx_track));
300
301 let mut tracks = default_tracks(0, true, Some(Arc::clone(&fx_track)));
302 for track in &mut tracks {
303 track.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
304 for trig in track.trigs_mut() {
305 trig.parameter_lock_pool = Some(Arc::clone(¶meter_lock_pool));
306 }
307 }
308
309 Self {
310 sysex_meta: SysexMeta::default_for_pattern_in_work_buffer(Some(device_id)),
311 index: 0,
312 version: 5,
313 tracks,
314 fx_track,
315 parameter_lock_pool,
316 master_length: 16,
317 master_change: 1,
318 kit_number: 0,
319 swing_amount: 0,
320 time_mode: TimeMode::Normal,
321 speed: Speed::default(),
322 global_quantize: 0,
323 bpm: 120.0,
324 pad_scale_per_pattern: 0x01,
325 }
326 }
327
328 pub fn tracks_mut(&mut self) -> &mut [Track] {
332 &mut self.tracks
333 }
334
335 #[parameter_range(range = "master_length:1..=1024")]
344 pub fn set_master_length(&mut self, master_length: usize) -> Result<(), RytmError> {
345 self.master_length = master_length as u16;
346 Ok(())
347 }
348
349 #[parameter_range(range = "swing_amount:50..=80")]
355 pub fn set_swing_amount(&mut self, swing_amount: usize) -> Result<(), RytmError> {
356 self.swing_amount = (swing_amount - 50) as u8;
358 Ok(())
359 }
360
361 pub fn set_speed(&mut self, speed: Speed) {
365 self.speed = speed;
366 }
367
368 #[parameter_range(range = "global_quantize:0..=127")]
372 pub fn set_global_quantize(&mut self, global_quantize: usize) -> Result<(), RytmError> {
373 self.global_quantize = global_quantize as u8;
374 Ok(())
375 }
376
377 #[parameter_range(range = "kit_number:0..=127")]
381 pub fn set_kit_number(&mut self, kit_number: usize) -> Result<(), RytmError> {
382 self.kit_number = kit_number as u8;
383 Ok(())
384 }
385
386 pub fn set_time_mode(&mut self, time_mode: TimeMode) {
390 self.time_mode = time_mode;
391 }
392
393 #[parameter_range(range = "master_change:1..=1024")]
402 pub fn set_master_change(&mut self, master_change: usize) -> Result<(), RytmError> {
403 self.master_change = master_change as u16;
404 Ok(())
405 }
406
407 #[parameter_range(range = "bpm:30.0..=300.0")]
413 pub fn set_bpm(&mut self, bpm: f32) -> Result<(), RytmError> {
414 self.bpm = bpm;
415 Ok(())
416 }
417
418 pub fn tracks(&self) -> &[Track] {
422 &self.tracks
423 }
424
425 pub const fn master_length(&self) -> usize {
434 self.master_length as usize
435 }
436
437 pub const fn swing_amount(&self) -> usize {
443 self.swing_amount as usize + 50
445 }
446
447 pub const fn speed(&self) -> Speed {
451 self.speed
452 }
453
454 pub const fn global_quantize(&self) -> usize {
458 self.global_quantize as usize
459 }
460
461 pub const fn kit_number(&self) -> usize {
465 self.kit_number as usize
466 }
467
468 pub const fn time_mode(&self) -> TimeMode {
472 self.time_mode
473 }
474
475 pub const fn master_change(&self) -> usize {
484 self.master_change as usize
485 }
486
487 pub const fn bpm(&self) -> f32 {
493 self.bpm
494 }
495
496 pub const fn index(&self) -> usize {
498 self.index
499 }
500
501 pub const fn is_work_buffer_pattern(&self) -> bool {
503 self.sysex_meta.is_targeting_work_buffer()
504 }
505
506 pub const fn structure_version(&self) -> u32 {
508 self.version
509 }
510
511 pub fn clear_all_plocks(&mut self) {
513 self.parameter_lock_pool.lock().clear_all_plocks();
514 }
515
516 #[parameter_range(range = "track_index:0..=12")]
520 pub fn clear_all_plocks_for_track(&mut self, track_index: u8) -> Result<(), RytmError> {
521 self.parameter_lock_pool
522 .lock()
523 .clear_all_plocks_for_track(track_index);
524
525 Ok(())
526 }
527
528 pub(crate) fn set_device_id(&mut self, device_id: u8) {
529 self.sysex_meta.set_device_id(device_id);
530 }
531}