Skip to main content

rytm_rs/object/pattern/
track.rs

1/// Holds the structures to represent a trig in a track.
2pub mod trig;
3/// Types related to the track.
4pub mod types;
5
6use self::{
7    trig::{HoldsTrigFlags, TrigFlags},
8    types::{PadScale, RootNote},
9};
10use super::{plock::ParameterLockPool, Length};
11use crate::{
12    defaults::default_trig_array,
13    error::{ParameterError, RytmError},
14    object::pattern::types::Speed,
15    util::{from_s_u16_t, to_s_u16_t_union_b},
16};
17use bitstream_io::{BigEndian, BitRead, BitReader, BitWrite, BitWriter};
18use derivative::Derivative;
19use parking_lot::Mutex;
20use rytm_rs_macro::parameter_range;
21use rytm_sys::ar_pattern_track_t;
22use serde::Serialize;
23use std::{io::Cursor, sync::Arc};
24use trig::Trig;
25
26/// Represents a single track in a pattern.
27///
28/// If the track index is 12 then this is the FX track.
29#[derive(Derivative, Clone, Serialize)]
30#[derivative(Debug)]
31pub struct Track {
32    pub(crate) is_owner_pattern_work_buffer: bool,
33    pub(crate) owner_pattern_index: usize,
34    pub(crate) index: usize,
35
36    pub(crate) trigs: Vec<Trig>,
37
38    pub(crate) default_trig_flags: TrigFlags,
39    pub(crate) default_trig_note: u8,
40    pub(crate) default_trig_velocity: u8,
41    pub(crate) default_trig_note_length: Length,
42    pub(crate) default_trig_probability: u8,
43
44    pub(crate) number_of_steps: u8,
45    pub(crate) quantize_amount: u8,
46    pub(crate) sends_midi: bool,
47    pub(crate) speed: Speed,
48
49    pub(crate) euclidean_mode: bool,
50    pub(crate) euclidean_pl1: u8,
51    pub(crate) euclidean_pl2: u8,
52    pub(crate) euclidean_ro1: u8,
53    pub(crate) euclidean_ro2: u8,
54    pub(crate) euclidean_tro: u8,
55
56    pub(crate) pad_scale: PadScale,
57    pub(crate) root_note: RootNote,
58
59    /// MSB of `default_trig_note`.
60    ///
61    /// For now it is always 0.
62    ///
63    /// Maybe it mean something?
64    #[derivative(Debug = "ignore")]
65    pub(crate) __maybe_useful_flag_from_default_trig_note: u8,
66
67    /// Mid bits of `flags_and_speed`.
68    ///
69    /// For now they're always 0.
70    ///
71    /// Maybe they mean something?
72    #[derivative(Debug = "ignore")]
73    pub(crate) __maybe_useful_flags_from_flags_and_speed: u8,
74
75    #[derivative(Debug = "ignore")]
76    #[serde(skip)]
77    pub(crate) parameter_lock_pool: Option<Arc<Mutex<ParameterLockPool>>>,
78
79    #[derivative(Debug = "ignore")]
80    #[allow(dead_code)]
81    #[serde(skip)]
82    pub(crate) fx_track_ref: Option<Arc<Mutex<Track>>>,
83}
84
85impl From<&Track> for ar_pattern_track_t {
86    fn from(track: &Track) -> Self {
87        let mut notes: [u8; 64] = [0; 64];
88        let mut velocities: [u8; 64] = [0; 64];
89        let mut note_lengths: [u8; 64] = [0; 64];
90        let mut micro_timings: [u8; 64] = [0; 64];
91        let mut retrig_lengths: [u8; 64] = [0; 64];
92        let mut retrig_rates: [u8; 64] = [0; 64];
93        let mut retrig_velocity_offsets: [i8; 64] = [0; 64];
94        let mut sound_locks: [u8; 64] = [0; 64];
95
96        let mut encoded_trig_bits: [u8; 112] = [0; 112];
97        let mut bit_writer =
98            BitWriter::endian(Cursor::new(encoded_trig_bits.as_mut_slice()), BigEndian);
99
100        for (i, trig) in track.trigs.iter().enumerate() {
101            // Encode trig flags to packed 14 bits.
102            bit_writer
103                .write::<u16>(14, trig.raw_trig_flags())
104                .expect("This shouldn't fail.");
105
106            notes[i] = trig.encode_note();
107            velocities[i] = trig.velocity() as u8;
108            note_lengths[i] = trig.note_length().into();
109            micro_timings[i] = trig.encode_micro_timing();
110            retrig_lengths[i] = trig.encode_retrig_length();
111            retrig_rates[i] = trig.encode_retrig_rate();
112            retrig_velocity_offsets[i] = trig.retrig_velocity_offset() as i8;
113            sound_locks[i] = trig.sound_lock() as u8;
114        }
115
116        // Encode flags and speed.
117        let mut encoded_flags_and_speed: u8 = 0;
118        encoded_flags_and_speed |= track.speed as u8;
119        encoded_flags_and_speed |= if track.sends_midi { 0b1000_0000 } else { 0 };
120        encoded_flags_and_speed |= track.__maybe_useful_flags_from_flags_and_speed;
121
122        // Encoded euclidean mode.
123        let encoded_euc_mode = if track.euclidean_mode { 128 } else { 0 };
124
125        // Compile note and unknown flag.
126        let encoded_default_trig_note =
127            track.default_trig_note | track.__maybe_useful_flag_from_default_trig_note;
128
129        Self {
130            trig_bits: encoded_trig_bits,
131            notes,
132            velocities,
133            note_lengths,
134            micro_timings,
135            retrig_lengths,
136            retrig_rates,
137            retrig_velocity_offsets,
138            default_note: encoded_default_trig_note,
139            default_velocity: track.default_trig_velocity,
140            default_note_length: track.default_trig_note_length.into(),
141            default_trig_flags: to_s_u16_t_union_b(*track.default_trig_flags),
142            num_steps: track.number_of_steps,
143            quantize_amount: track.quantize_amount,
144            sound_locks,
145            flags_and_speed: encoded_flags_and_speed,
146            trig_probability: track.default_trig_probability,
147            euc_mode: encoded_euc_mode,
148            euc_pl1: track.euclidean_pl1,
149            euc_pl2: track.euclidean_pl2,
150            euc_ro1: track.euclidean_ro1,
151            euc_ro2: track.euclidean_ro2,
152            euc_tro: track.euclidean_tro,
153            pad_scale: track.pad_scale.into(),
154            root_note: track.root_note.into(),
155        }
156    }
157}
158
159impl Track {
160    #[parameter_range(range = "index:0..=12", range = "owner_pattern_index:0..=127")]
161    pub(crate) fn try_default(
162        index: usize,
163        owner_pattern_index: usize,
164        is_owner_pattern_work_buffer: bool,
165        fx_track_ref: Option<Arc<Mutex<Self>>>,
166    ) -> Result<Self, RytmError> {
167        Ok(Self {
168            owner_pattern_index,
169            is_owner_pattern_work_buffer,
170            index,
171            trigs: default_trig_array(index),
172
173            default_trig_flags: TrigFlags::default(),
174            default_trig_note: 60,
175            default_trig_velocity: 100,
176            default_trig_note_length: Length::default(),
177            default_trig_probability: 100,
178
179            number_of_steps: 16,
180            quantize_amount: 0,
181            sends_midi: false,
182            speed: Speed::default(),
183
184            euclidean_mode: false,
185            euclidean_pl1: 0,
186            euclidean_pl2: 0,
187            euclidean_ro1: 63,
188            euclidean_ro2: 63,
189            euclidean_tro: 63,
190
191            pad_scale: PadScale::default(),
192            root_note: RootNote::default(),
193
194            __maybe_useful_flag_from_default_trig_note: 0,
195            __maybe_useful_flags_from_flags_and_speed: 0,
196
197            parameter_lock_pool: None,
198            fx_track_ref,
199        })
200    }
201
202    pub(crate) fn try_from_raw(
203        index: usize,
204        raw_track: &ar_pattern_track_t,
205        parameter_lock_pool: &Arc<Mutex<ParameterLockPool>>,
206        fx_track_ref: Option<Arc<Mutex<Self>>>,
207    ) -> Result<Self, RytmError> {
208        let mut trigs: Vec<Trig> = default_trig_array(index);
209
210        let trig_cursor = Cursor::new(raw_track.trig_bits);
211        let mut bit_reader = BitReader::endian(trig_cursor, BigEndian);
212
213        for (i, trig) in trigs.iter_mut().enumerate() {
214            let parameter_lock_pool_ref = Arc::clone(parameter_lock_pool);
215
216            let raw_trig_flags = bit_reader.read::<u16>(14).unwrap();
217            *trig = Trig::new(
218                // Trig index
219                i,
220                // Track index
221                index,
222                raw_trig_flags,
223                raw_track.notes[i],
224                raw_track.note_lengths[i],
225                raw_track.velocities[i],
226                raw_track.micro_timings[i],
227                raw_track.retrig_rates[i],
228                raw_track.retrig_lengths[i],
229                raw_track.retrig_velocity_offsets[i],
230                raw_track.sound_locks[i],
231                parameter_lock_pool_ref,
232                fx_track_ref.as_ref().map(Arc::clone),
233            )?;
234        }
235
236        let sends_midi = raw_track.flags_and_speed & 0b1000_0000 != 0;
237        let speed: Speed = (raw_track.flags_and_speed & 0b0000_0111).try_into()?;
238
239        #[allow(clippy::no_effect_underscore_binding)]
240        // They always seem to be 0.
241        let __maybe_useful_flags_from_flags_and_speed = raw_track.flags_and_speed & 0b0111_1000;
242        #[allow(clippy::no_effect_underscore_binding)]
243        let __maybe_useful_flag_from_default_trig_note = raw_track.default_note & 0b1000_0000;
244
245        let default_trig_note = raw_track.default_note & 0b0111_1111;
246
247        let plock_pool = parameter_lock_pool.lock();
248
249        Ok(Self {
250            is_owner_pattern_work_buffer: plock_pool.is_owner_pattern_work_buffer,
251            owner_pattern_index: plock_pool.owner_pattern_index,
252            index,
253            trigs,
254
255            default_trig_note,
256            default_trig_velocity: raw_track.default_velocity,
257            default_trig_note_length: raw_track.default_note_length.try_into()?,
258            default_trig_flags: unsafe { from_s_u16_t(raw_track.default_trig_flags).into() },
259            default_trig_probability: raw_track.trig_probability,
260
261            number_of_steps: raw_track.num_steps,
262            quantize_amount: raw_track.quantize_amount,
263            sends_midi,
264            speed,
265
266            euclidean_mode: raw_track.euc_mode == 128,
267            euclidean_pl1: raw_track.euc_pl1,
268            euclidean_pl2: raw_track.euc_pl2,
269            euclidean_ro1: raw_track.euc_ro1,
270            euclidean_ro2: raw_track.euc_ro2,
271            euclidean_tro: raw_track.euc_tro,
272
273            pad_scale: raw_track.pad_scale.try_into()?,
274            root_note: raw_track.root_note.try_into()?,
275
276            __maybe_useful_flag_from_default_trig_note,
277            __maybe_useful_flags_from_flags_and_speed,
278
279            parameter_lock_pool: Some(Arc::clone(parameter_lock_pool)),
280            fx_track_ref,
281        })
282    }
283    /// Returns a mutable reference to the trigs in this track.
284    ///
285    /// 64 trigs in total.
286    pub fn trigs_mut(&mut self) -> &mut [Trig] {
287        self.trigs.as_mut_slice()
288    }
289
290    /// Sets the default note for any trig in this track.
291    ///
292    /// Range `0..=127`
293    ///
294    /// Follows the midi note convention. C-4 is `0x3C`.
295    #[parameter_range(range = "default_trig_note:0..=127")]
296    pub fn set_default_trig_note(&mut self, default_trig_note: usize) -> Result<(), RytmError> {
297        self.default_trig_note = default_trig_note as u8;
298        Ok(())
299    }
300
301    /// Sets the default velocity for any trig in this track.
302    ///
303    /// Range `0..=127`
304    #[parameter_range(range = "default_trig_velocity:0..=127")]
305    pub fn set_default_trig_velocity(
306        &mut self,
307        default_trig_velocity: usize,
308    ) -> Result<(), RytmError> {
309        self.default_trig_velocity = default_trig_velocity as u8;
310        Ok(())
311    }
312
313    /// Sets the default note length for any trig in this track.
314    ///
315    /// Range `0..=127`
316    pub fn set_default_trig_note_length(&mut self, default_trig_note_length: Length) {
317        self.default_trig_note_length = default_trig_note_length;
318    }
319
320    // TODO: Double check if this api is good.
321    /// Sets the default trig flags for any trig in this track.
322    pub fn set_default_trig_flags<F: Into<TrigFlags>>(&mut self, default_trig_flags: F) {
323        self.default_trig_flags = default_trig_flags.into();
324    }
325
326    /// Sets the default trig probability for any trig in this track.
327    ///
328    /// Range `0..=100`
329    #[parameter_range(range = "default_trig_probability:0..=100")]
330    pub fn set_default_trig_probability(
331        &mut self,
332        default_trig_probability: usize,
333    ) -> Result<(), RytmError> {
334        self.default_trig_probability = default_trig_probability as u8;
335        Ok(())
336    }
337
338    /// Sets the number of steps in this track.
339    ///
340    /// Range `1..=64`
341    #[parameter_range(range = "number_of_steps:1..=64")]
342    pub fn set_number_of_steps(&mut self, number_of_steps: usize) -> Result<(), RytmError> {
343        self.number_of_steps = number_of_steps as u8;
344        Ok(())
345    }
346
347    /// Sets the quantize amount for this track.
348    ///
349    /// Range `0..=127`
350    #[parameter_range(range = "quantize_amount:0..=127")]
351    pub fn set_quantize_amount(&mut self, quantize_amount: usize) -> Result<(), RytmError> {
352        self.quantize_amount = quantize_amount as u8;
353        Ok(())
354    }
355
356    /// Sets whether this track sends MIDI.
357    pub fn set_sends_midi(&mut self, sends_midi: bool) {
358        self.sends_midi = sends_midi;
359    }
360
361    /// Sets the speed for this track.
362    pub fn set_speed(&mut self, speed: Speed) {
363        self.speed = speed;
364    }
365
366    /// Sets whether this track is in euclidean mode.
367    pub fn set_euclidean_mode(&mut self, euclidean_mode: bool) {
368        self.euclidean_mode = euclidean_mode;
369    }
370
371    /// Sets the euclidean pulse length 1 for this track.
372    ///
373    /// Number of pulses.
374    ///
375    /// Range `0..=64`
376    #[parameter_range(range = "euclidean_pl1:0..=64")]
377    pub fn set_euclidean_pl1(&mut self, euclidean_pl1: usize) -> Result<(), RytmError> {
378        self.euclidean_pl1 = euclidean_pl1 as u8;
379        Ok(())
380    }
381
382    /// Sets the euclidean pulse length 2 for this track.
383    ///
384    /// Number of pulses.
385    ///
386    /// Range `0..=64`
387    #[parameter_range(range = "euclidean_pl2:0..=64")]
388    pub fn set_euclidean_pl2(&mut self, euclidean_pl2: usize) -> Result<(), RytmError> {
389        self.euclidean_pl2 = euclidean_pl2 as u8;
390        Ok(())
391    }
392
393    /// Sets the euclidean rotation 1 for this track.
394    ///
395    /// Range `0..=126`
396    ///
397    /// Middle point `63`
398    #[parameter_range(range = "euclidean_ro1:0..=126")]
399    pub fn set_euclidean_ro1(&mut self, euclidean_ro1: usize) -> Result<(), RytmError> {
400        self.euclidean_ro1 = euclidean_ro1 as u8;
401        Ok(())
402    }
403
404    /// Sets the euclidean rotation 2 for this track.
405    ///
406    /// Range `0..=126`
407    ///
408    /// Middle point `63`
409    #[parameter_range(range = "euclidean_ro2:0..=126")]
410    pub fn set_euclidean_ro2(&mut self, euclidean_ro2: usize) -> Result<(), RytmError> {
411        self.euclidean_ro2 = euclidean_ro2 as u8;
412        Ok(())
413    }
414
415    /// Sets the euclidean track rotation for this track.
416    ///
417    /// Range `0..=126`
418    ///
419    /// Middle point `63`
420    #[parameter_range(range = "euclidean_tro:0..=126")]
421    pub fn set_euclidean_tro(&mut self, euclidean_tro: usize) -> Result<(), RytmError> {
422        self.euclidean_tro = euclidean_tro as u8;
423        Ok(())
424    }
425
426    /// Sets the pad scale for this track.
427    pub fn set_pad_scale(&mut self, pad_scale: PadScale) {
428        self.pad_scale = pad_scale;
429    }
430
431    /// Sets the root note for this track.
432    pub fn set_root_note(&mut self, root_note: RootNote) {
433        self.root_note = root_note;
434    }
435
436    /// Returns a reference to the trigs in this track.
437    ///
438    /// 64 trigs in total.
439    pub fn trigs(&self) -> &[Trig] {
440        &self.trigs
441    }
442
443    /// Returns the default note for any trig in this track.
444    ///
445    /// Range `0..=127`
446    ///
447    /// Follows the midi note convention. C-4 is `0x3C`.
448    pub const fn default_trig_note(&self) -> usize {
449        self.default_trig_note as usize
450    }
451
452    /// Returns the default velocity for any trig in this track.
453    ///
454    /// Range `0..=127`
455    pub const fn default_trig_velocity(&self) -> usize {
456        self.default_trig_velocity as usize
457    }
458
459    /// Returns the default note length for any trig in this track.
460    pub const fn default_trig_note_length(&self) -> Length {
461        self.default_trig_note_length
462    }
463
464    /// Returns the default trig flags for any trig in this track.
465    pub const fn default_trig_flags(&self) -> TrigFlags {
466        self.default_trig_flags
467    }
468
469    /// Returns the default trig probability for any trig in this track.
470    ///
471    /// Range `0..=100`
472    pub const fn default_trig_probability(&self) -> usize {
473        self.default_trig_probability as usize
474    }
475
476    /// Returns the number of steps in this track.
477    ///
478    /// Range `1..=64`
479    pub const fn number_of_steps(&self) -> usize {
480        self.number_of_steps as usize
481    }
482
483    /// Returns the quantize amount for this track.
484    ///
485    /// Range `0..=127`
486    pub const fn quantize_amount(&self) -> usize {
487        self.quantize_amount as usize
488    }
489
490    /// Returns whether this track sends MIDI.
491    pub const fn sends_midi(&self) -> bool {
492        self.sends_midi
493    }
494
495    /// Returns the speed for this track.
496    pub const fn speed(&self) -> Speed {
497        self.speed
498    }
499
500    /// Returns whether this track is in euclidean mode.
501    pub const fn euclidean_mode(&self) -> bool {
502        self.euclidean_mode
503    }
504
505    /// Returns the euclidean pulse length 1 for this track.
506    ///
507    /// Number of pulses.
508    ///
509    /// Range `0..=64`
510    pub const fn euclidean_pl1(&self) -> usize {
511        self.euclidean_pl1 as usize
512    }
513
514    /// Returns the euclidean pulse length 2 for this track.
515    ///
516    /// Number of pulses.
517    ///
518    /// Range `0..=64`
519    pub const fn euclidean_pl2(&self) -> usize {
520        self.euclidean_pl2 as usize
521    }
522
523    /// Returns the euclidean rotation 1 for this track.
524    ///
525    /// Range `0..=126`
526    ///
527    /// Middle point `63`
528    pub const fn euclidean_ro1(&self) -> usize {
529        self.euclidean_ro1 as usize
530    }
531
532    /// Returns the euclidean rotation 2 for this track.
533    ///
534    /// Range `0..=126`
535    ///
536    /// Middle point `63`
537    pub const fn euclidean_ro2(&self) -> usize {
538        self.euclidean_ro2 as usize
539    }
540
541    /// Returns the euclidean track rotation for this track.
542    ///
543    /// Range `0..=126`
544    ///
545    /// Middle point `63`
546    pub const fn euclidean_tro(&self) -> usize {
547        self.euclidean_tro as usize
548    }
549
550    /// Returns the pad scale for this track.
551    pub const fn pad_scale(&self) -> PadScale {
552        self.pad_scale
553    }
554
555    /// Returns the root note for this track.
556    pub const fn root_note(&self) -> RootNote {
557        self.root_note
558    }
559
560    /// Clears all the parameter locks for this track.
561    pub fn clear_all_plocks(&self) {
562        if let Some(pool) = &self.parameter_lock_pool {
563            pool.lock().clear_all_plocks_for_track(self.index as u8);
564        }
565    }
566
567    /// Returns the index of this track.
568    pub const fn index(&self) -> usize {
569        self.index
570    }
571
572    /// Returns the index of the pattern that owns this track.
573    pub const fn owner_pattern_index(&self) -> usize {
574        self.owner_pattern_index
575    }
576}