korg_syro/
pattern.rs

1//!
2//! Bindings for building patterns for the Volca Sample sequencer.
3//!
4//! # Examples
5//!
6//! Simple
7//!
8//! ```rust
9//! use korg_syro::SyroStream;
10//! use korg_syro::pattern::*;
11//!
12//! let mut syro_stream = SyroStream::default();
13//!
14//! let mut pattern = Pattern::default();
15//! pattern.with_part(
16//!     0u8,
17//!     Part::for_sample(0)?.with_steps(
18//!         Steps::builder()
19//!             .on(Step::One)
20//!             .on(Step::Three)
21//!             .on(Step::Five)
22//!             .on(Step::Seven)
23//!             .build(),
24//!     ).build(),
25//! )?;
26//!
27//! syro_stream.add_pattern(0, pattern)?;
28//!
29//! # Ok::<(), korg_syro::SyroError>(())
30//! ```
31//!
32//! Bells & Whistles
33//!
34//! ```rust
35//! use korg_syro::SyroStream;
36//! use korg_syro::pattern::*;
37//! use korg_syro::pattern::Toggle::*;
38//!
39//! let mut syro_stream = SyroStream::default();
40//!
41//! let mut pattern = Pattern::default();
42//! pattern.with_part(
43//!     0u8,
44//!     Part::for_sample(0)?.with_steps(
45//!         Steps::builder()
46//!             .on(Step::One)
47//!             .on(Step::Three)
48//!             .on(Step::Five)
49//!             .on(Step::Seven)
50//!             .build(),
51//!     )
52//!     .level(42)?
53//!     .pan(42)?
54//!     .amp_eg_attack(42)?
55//!     .amp_eg_decay(42)?
56//!     .pitch_eg_attack(42)?
57//!     .pitch_eg_int(42)?
58//!     .pitch_eg_decay(42)?
59//!     .starting_point(42)?
60//!     .length(42)?
61//!     .hi_cut(42)?
62//!     .motion(On)
63//!     .looped(On)
64//!     .reverb(On)
65//!     .reverse(On)
66//!     .mute(Off)
67//!     .build()
68//! )?;
69//!
70//! syro_stream.add_pattern(0, pattern)?;
71//!
72//! # Ok::<(), korg_syro::SyroError>(())
73//! ```
74//!
75use korg_syro_sys::{
76    VolcaSample_Part_Data, VolcaSample_Pattern_Data, VOLCASAMPLE_FUNC_LOOP,
77    VOLCASAMPLE_FUNC_MOTION, VOLCASAMPLE_FUNC_MUTE, VOLCASAMPLE_FUNC_REVERB,
78    VOLCASAMPLE_FUNC_REVERSE, VOLCASAMPLE_PARAM_AMPEG_ATTACK, VOLCASAMPLE_PARAM_AMPEG_DECAY,
79    VOLCASAMPLE_PARAM_HICUT, VOLCASAMPLE_PARAM_LENGTH, VOLCASAMPLE_PARAM_LEVEL,
80    VOLCASAMPLE_PARAM_PAN, VOLCASAMPLE_PARAM_PITCHEG_ATTACK, VOLCASAMPLE_PARAM_PITCHEG_DECAY,
81    VOLCASAMPLE_PARAM_PITCHEG_INT, VOLCASAMPLE_PARAM_SPEED, VOLCASAMPLE_PARAM_START_POINT,
82};
83pub use num_enum;
84use num_enum::TryFromPrimitive;
85
86use crate::macros::*;
87use crate::{check_sample_index, SyroError};
88
89/// Defines the available steps
90#[derive(Copy, Clone, Debug, TryFromPrimitive)]
91#[repr(u8)]
92pub enum Step {
93    One,
94    Two,
95    Three,
96    Four,
97    Five,
98    Six,
99    Seven,
100    Eight,
101    Nine,
102    Ten,
103    Eleven,
104    Twelve,
105    Thirteen,
106    Fourteen,
107    Fifteen,
108    Sixteen,
109}
110
111impl Step {
112    pub fn to_bitmask(self) -> u16 {
113        1 << self as u16
114    }
115}
116
117/// Builder for a step sequence
118#[derive(Copy, Clone, Debug)]
119pub struct Steps {
120    steps: u16,
121}
122
123impl Steps {
124    pub fn builder() -> Self {
125        Self { steps: 0 }
126    }
127
128    /// Turns on the step
129    pub fn on(&mut self, step: Step) -> &mut Self {
130        self.steps |= step.to_bitmask();
131        self
132    }
133
134    pub fn build(self) -> Self {
135        self
136    }
137
138    pub fn to_bytes(self) -> u16 {
139        self.steps
140    }
141}
142
143/// Defines a toggle value
144#[derive(Copy, Clone, Debug)]
145pub enum Toggle {
146    On,
147    Off,
148}
149
150max_check!(pattern_index, 9);
151max_check!(part_index, 9);
152
153max_check!(level, 127);
154bounds_check!(pan, 1, 127);
155bounds_check!(speed_semitone, 40, 88);
156bounds_check!(speed_continuous, 129, 255);
157max_check!(amp_eg_attack, 127);
158max_check!(amp_eg_decay, 127);
159bounds_check!(pitch_eg_int, 1, 127);
160max_check!(pitch_eg_attack, 127);
161max_check!(pitch_eg_decay, 127);
162max_check!(starting_point, 127);
163max_check!(length, 127);
164max_check!(hi_cut, 127);
165
166// there's two valid ranges for speed
167fn check_speed(speed: u8) -> Result<(), SyroError> {
168    check_speed_semitone(speed).or(check_speed_continuous(speed))
169}
170
171/// Defines a part of a sequence pattern
172#[derive(Copy, Clone, Debug)]
173pub struct Part {
174    data: VolcaSample_Part_Data,
175}
176
177macro_rules! impl_func_memory_part {
178    ($i:ident, $j:ident) => {
179        paste! {
180            pub fn [<$i>](&mut self, value: Toggle) -> &mut Self {
181                self.toggle_func_memory_part($j, value);
182                self
183            }
184        }
185    };
186}
187
188impl Part {
189    pub fn for_sample(sample_num: u16) -> Result<Self, SyroError> {
190        check_sample_index(sample_num as u8)?;
191        let mut data = VolcaSample_Part_Data::default();
192        data.SampleNum = sample_num;
193
194        Ok(Self { data })
195    }
196
197    pub fn with_steps(&mut self, steps: Steps) -> &mut Self {
198        self.data.StepOn = steps.to_bytes();
199        self
200    }
201
202    fn toggle_func_memory_part(&mut self, func: u32, value: Toggle) {
203        match value {
204            Toggle::On => {
205                self.data.FuncMemoryPart |= func as u8;
206            }
207            Toggle::Off => {
208                self.data.FuncMemoryPart &= !(func as u8);
209            }
210        }
211    }
212
213    impl_func_memory_part!(motion, VOLCASAMPLE_FUNC_MOTION);
214    impl_func_memory_part!(looped, VOLCASAMPLE_FUNC_LOOP);
215    impl_func_memory_part!(reverb, VOLCASAMPLE_FUNC_REVERB);
216    impl_func_memory_part!(reverse, VOLCASAMPLE_FUNC_REVERSE);
217
218    pub fn mute(&mut self, value: Toggle) -> &mut Self {
219        // apparently mute toggle is reversed
220        match value {
221            Toggle::On => {
222                self.data.FuncMemoryPart &= VOLCASAMPLE_FUNC_MUTE as u8;
223            }
224            Toggle::Off => {
225                self.data.FuncMemoryPart |= !(VOLCASAMPLE_FUNC_MUTE as u8);
226            }
227        }
228        self
229    }
230
231    pub fn level(&mut self, level: u8) -> Result<&mut Self, SyroError> {
232        check_level(level)?;
233        self.data.Param[VOLCASAMPLE_PARAM_LEVEL as usize] = level;
234        Ok(self)
235    }
236
237    pub fn pan(&mut self, pan: u8) -> Result<&mut Self, SyroError> {
238        check_pan(pan)?;
239        self.data.Param[VOLCASAMPLE_PARAM_PAN as usize] = pan;
240        Ok(self)
241    }
242
243    pub fn speed(&mut self, speed: u8) -> Result<&mut Self, SyroError> {
244        check_speed(speed)?;
245        self.data.Param[VOLCASAMPLE_PARAM_SPEED as usize] = speed;
246        Ok(self)
247    }
248
249    pub fn amp_eg_attack(&mut self, amp_eg_attack: u8) -> Result<&mut Self, SyroError> {
250        check_amp_eg_attack(amp_eg_attack)?;
251        self.data.Param[VOLCASAMPLE_PARAM_AMPEG_ATTACK as usize] = amp_eg_attack;
252        Ok(self)
253    }
254
255    pub fn amp_eg_decay(&mut self, amp_eg_decay: u8) -> Result<&mut Self, SyroError> {
256        check_amp_eg_decay(amp_eg_decay)?;
257        self.data.Param[VOLCASAMPLE_PARAM_AMPEG_DECAY as usize] = amp_eg_decay;
258        Ok(self)
259    }
260
261    pub fn pitch_eg_attack(&mut self, pitch_eg_attack: u8) -> Result<&mut Self, SyroError> {
262        check_pitch_eg_attack(pitch_eg_attack)?;
263        self.data.Param[VOLCASAMPLE_PARAM_PITCHEG_ATTACK as usize] = pitch_eg_attack;
264        Ok(self)
265    }
266
267    pub fn pitch_eg_int(&mut self, pitch_eg_int: u8) -> Result<&mut Self, SyroError> {
268        check_pitch_eg_int(pitch_eg_int)?;
269        self.data.Param[VOLCASAMPLE_PARAM_PITCHEG_INT as usize] = pitch_eg_int;
270        Ok(self)
271    }
272
273    pub fn pitch_eg_decay(&mut self, pitch_eg_decay: u8) -> Result<&mut Self, SyroError> {
274        check_pitch_eg_decay(pitch_eg_decay)?;
275        self.data.Param[VOLCASAMPLE_PARAM_PITCHEG_DECAY as usize] = pitch_eg_decay;
276        Ok(self)
277    }
278
279    pub fn starting_point(&mut self, starting_point: u8) -> Result<&mut Self, SyroError> {
280        check_starting_point(starting_point)?;
281        self.data.Param[VOLCASAMPLE_PARAM_START_POINT as usize] = starting_point;
282        Ok(self)
283    }
284
285    pub fn length(&mut self, length: u8) -> Result<&mut Self, SyroError> {
286        check_starting_point(length)?;
287        self.data.Param[VOLCASAMPLE_PARAM_LENGTH as usize] = length;
288        Ok(self)
289    }
290
291    pub fn hi_cut(&mut self, hi_cut: u8) -> Result<&mut Self, SyroError> {
292        check_hi_cut(hi_cut)?;
293        self.data.Param[VOLCASAMPLE_PARAM_HICUT as usize] = hi_cut;
294        Ok(self)
295    }
296
297    /// Valid values in the sequence are 0-127
298    pub fn level_start_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
299        sequence
300            .iter()
301            .map(|&v| check_level(v))
302            .collect::<Result<(), SyroError>>()?;
303        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_LEVEL_0 as usize] = sequence;
304        Ok(self)
305    }
306
307    /// Valid values in the sequence are 0-127
308    pub fn level_end_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
309        sequence
310            .iter()
311            .map(|&v| check_level(v))
312            .collect::<Result<(), SyroError>>()?;
313        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_LEVEL_1 as usize] = sequence;
314        Ok(self)
315    }
316
317    /// Valid values in the sequence are 1-127
318    pub fn pan_start_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
319        sequence
320            .iter()
321            .map(|&v| check_pan(v))
322            .collect::<Result<(), SyroError>>()?;
323        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_PAN_0 as usize] = sequence;
324        Ok(self)
325    }
326
327    /// Valid values in the sequence are 1-127
328    pub fn pan_end_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
329        sequence
330            .iter()
331            .map(|&v| check_pan(v))
332            .collect::<Result<(), SyroError>>()?;
333        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_PAN_1 as usize] = sequence;
334        Ok(self)
335    }
336
337    /// Valid values in the sequence are 40-88 for semitones, and 129-255 for continuous
338    pub fn speed_start_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
339        sequence
340            .iter()
341            .map(|&v| check_speed(v))
342            .collect::<Result<(), SyroError>>()?;
343        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_SPEED_0 as usize] = sequence;
344        Ok(self)
345    }
346
347    /// Valid values in the sequence are 40-88 for semitones, and 129-255 for continuous
348    pub fn speed_end_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
349        sequence
350            .iter()
351            .map(|&v| check_speed(v))
352            .collect::<Result<(), SyroError>>()?;
353        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_SPEED_1 as usize] = sequence;
354        Ok(self)
355    }
356
357    /// Valid values in the sequence are 0-127
358    pub fn amp_eg_attack_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
359        sequence
360            .iter()
361            .map(|&v| check_amp_eg_attack(v))
362            .collect::<Result<(), SyroError>>()?;
363        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_AMPEG_ATTACK as usize] = sequence;
364        Ok(self)
365    }
366
367    /// Valid values in the sequence are 0-127
368    pub fn amp_eg_decay_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
369        sequence
370            .iter()
371            .map(|&v| check_amp_eg_decay(v))
372            .collect::<Result<(), SyroError>>()?;
373        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_AMPEG_DECAY as usize] = sequence;
374        Ok(self)
375    }
376
377    /// Valid values in the sequence are 1-127
378    pub fn pitch_eg_int_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
379        sequence
380            .iter()
381            .map(|&v| check_pitch_eg_int(v))
382            .collect::<Result<(), SyroError>>()?;
383        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_PITCHEG_INT as usize] = sequence;
384        Ok(self)
385    }
386
387    /// Valid values in the sequence are 0-127
388    pub fn pitch_eg_attack_motion_seq(
389        &mut self,
390        sequence: [u8; 16],
391    ) -> Result<&mut Self, SyroError> {
392        sequence
393            .iter()
394            .map(|&v| check_pitch_eg_attack(v))
395            .collect::<Result<(), SyroError>>()?;
396        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_PITCHEG_ATTACK as usize] = sequence;
397        Ok(self)
398    }
399
400    /// Valid values in the sequence are 0-127
401    pub fn pitch_eg_decay_motion_seq(
402        &mut self,
403        sequence: [u8; 16],
404    ) -> Result<&mut Self, SyroError> {
405        sequence
406            .iter()
407            .map(|&v| check_pitch_eg_decay(v))
408            .collect::<Result<(), SyroError>>()?;
409        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_PITCHEG_DECAY as usize] = sequence;
410        Ok(self)
411    }
412
413    /// Valid values in the sequence are 0-127
414    pub fn start_point_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
415        sequence
416            .iter()
417            .map(|&v| check_starting_point(v))
418            .collect::<Result<(), SyroError>>()?;
419        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_START_POINT as usize] = sequence;
420        Ok(self)
421    }
422
423    /// Valid values in the sequence are 0-127
424    pub fn length_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
425        sequence
426            .iter()
427            .map(|&v| check_length(v))
428            .collect::<Result<(), SyroError>>()?;
429        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_LENGTH as usize] = sequence;
430        Ok(self)
431    }
432
433    /// Valid values in the sequence are 0-127
434    pub fn hi_cut_motion_seq(&mut self, sequence: [u8; 16]) -> Result<&mut Self, SyroError> {
435        sequence
436            .iter()
437            .map(|&v| check_hi_cut(v))
438            .collect::<Result<(), SyroError>>()?;
439        self.data.Motion[korg_syro_sys::VOLCASAMPLE_MOTION_HICUT as usize] = sequence;
440        Ok(self)
441    }
442
443    pub fn build(self) -> Self {
444        self
445    }
446}
447
448/// Defines a pattern for the sequencer
449#[derive(Clone, Debug, Default)]
450pub struct Pattern {
451    data: VolcaSample_Pattern_Data,
452}
453
454impl Pattern {
455    pub fn with_part(&mut self, part_index: u8, part: Part) -> Result<&Self, SyroError> {
456        check_part_index(part_index)?;
457        self.data.Part[part_index as usize] = part.data;
458        Ok(self)
459    }
460
461    pub fn to_bytes(self) -> Vec<u8> {
462        let mut bytes = vec![];
463        bytes.extend_from_slice(&self.data.Header.to_le_bytes());
464        bytes.extend_from_slice(&self.data.DevCode.to_le_bytes());
465        bytes.extend_from_slice(&self.data.Reserved);
466        bytes.extend_from_slice(&self.data.ActiveStep.to_le_bytes());
467        bytes.extend_from_slice(&self.data.Padding1);
468        for part in self.data.Part.iter() {
469            let mut part_bytes = vec![];
470            part_bytes.extend_from_slice(&part.SampleNum.to_le_bytes());
471            part_bytes.extend_from_slice(&part.StepOn.to_le_bytes());
472            part_bytes.extend_from_slice(&part.Accent.to_le_bytes());
473            part_bytes.extend_from_slice(&part.Reserved.to_le_bytes());
474            part_bytes.extend_from_slice(&part.Level.to_le_bytes());
475            part_bytes.extend_from_slice(&part.Param);
476            part_bytes.extend_from_slice(&part.FuncMemoryPart.to_le_bytes());
477            part_bytes.extend_from_slice(&part.Padding1);
478            for motion in part.Motion.iter() {
479                part_bytes.extend_from_slice(motion);
480            }
481            bytes.extend_from_slice(part_bytes.as_slice());
482        }
483        bytes.extend_from_slice(&self.data.Padding2);
484        bytes.extend_from_slice(&self.data.Footer.to_le_bytes());
485        bytes
486    }
487}
488
489#[cfg(test)]
490mod test {
491    use super::Toggle::*;
492    use super::*;
493    use anyhow;
494    use korg_syro_sys::VolcaSample_Pattern_Init;
495
496    #[test]
497    fn test_step() {
498        let steps = Steps::builder()
499            .on(Step::Three)
500            .on(Step::Seven)
501            .on(Step::Twelve)
502            .build()
503            .to_bytes();
504
505        println!("{:#018b}", steps);
506
507        assert_eq!(steps, 0b000100001000100);
508    }
509
510    #[test]
511    fn test_part_builder() -> anyhow::Result<()> {
512        let motion_seq: [u8; 16] = [
513            1, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120,
514        ];
515        let continuous_speed_motion_seq: [u8; 16] = [
516            129, 137, 145, 153, 161, 169, 177, 185, 193, 201, 209, 217, 225, 233, 241, 249,
517        ];
518        let semitone_speed_motion_seq: [u8; 16] = [
519            40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85,
520        ];
521        let _part = Part::for_sample(0)?
522            .with_steps(
523                Steps::builder()
524                    .on(Step::Three)
525                    .on(Step::Seven)
526                    .on(Step::Thirteen)
527                    .build(),
528            )
529            .level(42)?
530            .pan(42)?
531            .amp_eg_attack(42)?
532            .amp_eg_decay(42)?
533            .pitch_eg_attack(42)?
534            .pitch_eg_int(42)?
535            .pitch_eg_decay(42)?
536            .starting_point(42)?
537            .length(42)?
538            .hi_cut(42)?
539            .level_start_motion_seq(motion_seq.clone())?
540            .level_end_motion_seq(motion_seq.clone())?
541            .pan_start_motion_seq(motion_seq.clone())?
542            .pan_end_motion_seq(motion_seq.clone())?
543            .speed_start_motion_seq(continuous_speed_motion_seq.clone())?
544            .speed_end_motion_seq(semitone_speed_motion_seq.clone())?
545            .amp_eg_attack_motion_seq(motion_seq.clone())?
546            .amp_eg_decay_motion_seq(motion_seq.clone())?
547            .pitch_eg_int_motion_seq(motion_seq.clone())?
548            .pitch_eg_attack_motion_seq(motion_seq.clone())?
549            .pitch_eg_decay_motion_seq(motion_seq.clone())?
550            .start_point_motion_seq(motion_seq.clone())?
551            .length_motion_seq(motion_seq.clone())?
552            .hi_cut_motion_seq(motion_seq.clone())?
553            .motion(On)
554            .looped(On)
555            .reverb(On)
556            .reverse(On)
557            .mute(Off)
558            .build();
559
560        Ok(())
561    }
562
563    #[test]
564    fn test_pattern_default() -> anyhow::Result<()> {
565        let mut raw_bytes: Vec<u8> = vec![0; std::mem::size_of::<VolcaSample_Pattern_Data>()];
566        unsafe {
567            VolcaSample_Pattern_Init(raw_bytes.as_mut_ptr() as *mut VolcaSample_Pattern_Data);
568        }
569
570        let default_bytes = Pattern::default().to_bytes();
571
572        assert_eq!(raw_bytes, default_bytes);
573        Ok(())
574    }
575
576    #[test]
577    fn test_pattern() -> anyhow::Result<()> {
578        let mut pattern = Pattern::default();
579        pattern.with_part(
580            0u8,
581            Part::for_sample(0)?
582                .with_steps(
583                    Steps::builder()
584                        .on(Step::One)
585                        .on(Step::Three)
586                        .on(Step::Five)
587                        .on(Step::Seven)
588                        .build(),
589                )
590                .mute(Off)
591                .build(),
592        )?;
593
594        let _data = pattern.to_bytes();
595        Ok(())
596    }
597}