1use 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#[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#[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 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#[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
166fn check_speed(speed: u8) -> Result<(), SyroError> {
168 check_speed_semitone(speed).or(check_speed_continuous(speed))
169}
170
171#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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#[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}