1use super::{tcat::tcd22xx_spec::*, *};
179
180#[derive(Default, Debug)]
182pub struct SPro24DspProtocol;
183
184impl TcatOperation for SPro24DspProtocol {}
185
186impl TcatGlobalSectionSpecification for SPro24DspProtocol {}
187
188impl TcatExtensionOperation for SPro24DspProtocol {}
189
190impl Tcd22xxSpecification for SPro24DspProtocol {
191 const INPUTS: &'static [Input] = &[
192 Input {
193 id: SrcBlkId::Ins0,
194 offset: 2,
195 count: 2,
196 label: Some("Mic"),
197 },
198 Input {
199 id: SrcBlkId::Ins0,
200 offset: 0,
201 count: 2,
202 label: Some("Line"),
203 },
204 Input {
205 id: SrcBlkId::Ins0,
206 offset: 8,
207 count: 2,
208 label: Some("Ch-strip"),
209 },
210 Input {
212 id: SrcBlkId::Ins0,
213 offset: 14,
214 count: 2,
215 label: Some("Reverb"),
216 },
217 Input {
219 id: SrcBlkId::Aes,
220 offset: 6,
221 count: 2,
222 label: Some("S/PDIF-coax"),
223 },
224 Input {
226 id: SrcBlkId::Adat,
227 offset: 0,
228 count: 8,
229 label: None,
230 },
231 Input {
232 id: SrcBlkId::Aes,
233 offset: 4,
234 count: 2,
235 label: Some("S/PDIF-opt"),
236 },
237 ];
238
239 const OUTPUTS: &'static [Output] = &[
240 Output {
241 id: DstBlkId::Ins0,
242 offset: 0,
243 count: 6,
244 label: None,
245 },
246 Output {
247 id: DstBlkId::Aes,
248 offset: 6,
249 count: 2,
250 label: Some("S/PDIF-coax"),
251 },
252 Output {
253 id: DstBlkId::Ins0,
254 offset: 8,
255 count: 2,
256 label: Some("Ch-strip"),
257 },
258 Output {
260 id: DstBlkId::Ins0,
261 offset: 14,
262 count: 2,
263 label: Some("Reverb"),
264 },
265 ];
267
268 const FIXED: &'static [SrcBlk] = &[
270 SrcBlk {
271 id: SrcBlkId::Ins0,
272 ch: 2,
273 },
274 SrcBlk {
275 id: SrcBlkId::Ins0,
276 ch: 3,
277 },
278 SrcBlk {
279 id: SrcBlkId::Ins0,
280 ch: 0,
281 },
282 SrcBlk {
283 id: SrcBlkId::Ins0,
284 ch: 1,
285 },
286 ];
287}
288
289impl SaffireproSwNoticeOperation for SPro24DspProtocol {
290 const SW_NOTICE_OFFSET: usize = 0x05ec;
291}
292
293impl SaffireproOutGroupSpecification for SPro24DspProtocol {
294 const OUT_GROUP_STATE_OFFSET: usize = 0x000c;
295
296 const ENTRY_COUNT: usize = 6;
297 const HAS_VOL_HWCTL: bool = false;
298
299 const SRC_NOTICE: u32 = 0x00000001;
300 const DIM_MUTE_NOTICE: u32 = 0x00000002;
301}
302
303impl SaffireproInputSpecification for SPro24DspProtocol {
304 const INPUT_PARAMS_OFFSET: usize = 0x0058;
305}
306
307#[allow(dead_code)]
309const DSP_ENABLE_OFFSET: usize = 0x0070; const CH_STRIP_FLAG_OFFSET: usize = 0x0078;
311const CH_STRIP_FLAG_EQ_ENABLE: u16 = 0x0001;
312const CH_STRIP_FLAG_COMP_ENABLE: u16 = 0x0002;
313const CH_STRIP_FLAG_EQ_AFTER_COMP: u16 = 0x0004;
314
315const CH_STRIP_FLAG_SW_NOTICE: u32 = 0x00000005;
316
317const COEF_OFFSET: usize = 0x0190;
318const COEF_BLOCK_SIZE: usize = 0x88;
319const EQ_COEF_COUNT: usize = 5;
322
323const COMP_OUTPUT_OFFSET: usize = 0x04;
324const COMP_THRESHOLD_OFFSET: usize = 0x08;
325const COMP_RATIO_OFFSET: usize = 0x0c;
326const COMP_ATTACK_OFFSET: usize = 0x10;
327const COMP_RELEASE_OFFSET: usize = 0x14;
328
329const COMP_CH0_SW_NOTICE: u32 = 0x00000006;
330const COMP_CH1_SW_NOTICE: u32 = 0x00000007;
331
332const EQ_OUTPUT_OFFSET: usize = 0x18;
333const EQ_LOW_FREQ_OFFSET: usize = 0x20;
334const EQ_OUTPUT_CH0_SW_NOTICE: u32 = 0x09;
339const EQ_OUTPUT_CH1_SW_NOTICE: u32 = 0x0a;
340const EQ_LOW_FREQ_CH0_SW_NOTICE: u32 = 0x0c;
341const EQ_LOW_FREQ_CH1_SW_NOTICE: u32 = 0x0c;
342const EQ_LOW_MIDDLE_FREQ_CH0_SW_NOTICE: u32 = 0x0f;
343const EQ_LOW_MIDDLE_FREQ_CH1_SW_NOTICE: u32 = 0x10;
344const EQ_HIGH_MIDDLE_FREQ_CH0_SW_NOTICE: u32 = 0x12;
345const EQ_HIGH_MIDDLE_FREQ_CH1_SW_NOTICE: u32 = 0x13;
346const EQ_HIGH_FREQ_CH0_SW_NOTICE: u32 = 0x15;
347const EQ_HIGH_FREQ_CH1_SW_NOTICE: u32 = 0x16;
348
349const REVERB_SIZE_OFFSET: usize = 0x70;
350const REVERB_AIR_OFFSET: usize = 0x74;
351const REVERB_ENABLE_OFFSET: usize = 0x78;
352const REVERB_DISABLE_OFFSET: usize = 0x7c;
353const REVERB_PRE_FILTER_VALUE_OFFSET: usize = 0x80;
354const REVERB_PRE_FILTER_SIGN_OFFSET: usize = 0x84;
355
356const REVERB_SW_NOTICE: u32 = 0x0000001a;
357
358fn serialize_f32(val: &f32, raw: &mut [u8]) -> Result<(), String> {
359 assert!(raw.len() >= 4);
360
361 raw[..4].copy_from_slice(&val.to_be_bytes());
362
363 Ok(())
364}
365
366fn deserialize_f32(val: &mut f32, raw: &[u8]) -> Result<(), String> {
367 assert!(raw.len() >= 4);
368
369 let mut quadlet = [0; 4];
370 quadlet.copy_from_slice(&raw[..4]);
371 *val = f32::from_be_bytes(quadlet);
372
373 Ok(())
374}
375
376#[derive(Default, Debug, Copy, Clone, PartialEq)]
378pub struct Spro24DspCompressorState {
379 pub output: [f32; 2],
381 pub threshold: [f32; 2],
383 pub ratio: [f32; 2],
385 pub attack: [f32; 2],
387 pub release: [f32; 2],
389}
390
391#[derive(Default, Debug, Copy, Clone, PartialEq)]
393pub struct Spro24DspEqualizerFrequencyBandState([f32; EQ_COEF_COUNT]);
394
395#[derive(Default, Debug, Copy, Clone, PartialEq)]
397pub struct Spro24DspEqualizerState {
398 pub output: [f32; 2],
400 pub low_coef: [Spro24DspEqualizerFrequencyBandState; 2],
402 pub low_middle_coef: [Spro24DspEqualizerFrequencyBandState; 2],
403 pub high_middle_coef: [Spro24DspEqualizerFrequencyBandState; 2],
404 pub high_coef: [Spro24DspEqualizerFrequencyBandState; 2],
405}
406
407#[derive(Default, Debug, Copy, Clone, PartialEq)]
409pub struct Spro24DspReverbState {
410 pub size: f32,
412 pub air: f32,
414 pub enabled: bool,
416 pub pre_filter: f32,
418}
419
420#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
422pub struct Spro24DspEffectGeneralParams {
423 pub eq_after_comp: [bool; 2],
425 pub comp_enable: [bool; 2],
427 pub eq_enable: [bool; 2],
429}
430
431const COEF_BLOCK_COMP: usize = 2;
432const COEF_BLOCK_EQ: usize = 2;
433const COEF_BLOCK_REVERB: usize = 3;
434
435fn serialize_compressor_state(
437 state: &Spro24DspCompressorState,
438 raw: &mut [u8],
439) -> Result<(), String> {
440 assert!(raw.len() >= COEF_BLOCK_SIZE * 2);
441
442 (0..2).try_for_each(|ch| {
443 let base_offset = COEF_BLOCK_SIZE * ch;
444
445 let pos = base_offset + COMP_OUTPUT_OFFSET;
446 serialize_f32(&state.output[ch], &mut raw[pos..(pos + 4)])?;
447
448 let pos = base_offset + COMP_THRESHOLD_OFFSET;
449 serialize_f32(&state.threshold[ch], &mut raw[pos..(pos + 4)])?;
450
451 let pos = base_offset + COMP_RATIO_OFFSET;
452 serialize_f32(&state.ratio[ch], &mut raw[pos..(pos + 4)])?;
453
454 let pos = base_offset + COMP_ATTACK_OFFSET;
455 serialize_f32(&state.attack[ch], &mut raw[pos..(pos + 4)])?;
456
457 let pos = base_offset + COMP_RELEASE_OFFSET;
458 serialize_f32(&state.release[ch], &mut raw[pos..(pos + 4)])
459 })
460}
461
462fn deserialize_compressor_state(
464 state: &mut Spro24DspCompressorState,
465 raw: &[u8],
466) -> Result<(), String> {
467 assert!(raw.len() >= COEF_BLOCK_SIZE * 2);
468
469 (0..2).try_for_each(|ch| {
470 let base_offset = COEF_BLOCK_SIZE * ch;
471
472 let pos = base_offset + COMP_OUTPUT_OFFSET;
473 deserialize_f32(&mut state.output[ch], &raw[pos..(pos + 4)])?;
474
475 let pos = base_offset + COMP_THRESHOLD_OFFSET;
476 deserialize_f32(&mut state.threshold[ch], &raw[pos..(pos + 4)])?;
477
478 let pos = base_offset + COMP_RATIO_OFFSET;
479 deserialize_f32(&mut state.ratio[ch], &raw[pos..(pos + 4)])?;
480
481 let pos = base_offset + COMP_ATTACK_OFFSET;
482 deserialize_f32(&mut state.attack[ch], &raw[pos..(pos + 4)])?;
483
484 let pos = base_offset + COMP_RELEASE_OFFSET;
485 deserialize_f32(&mut state.release[ch], &raw[pos..(pos + 4)])
486 })
487}
488
489fn serialize_equalizer_state(
491 state: &Spro24DspEqualizerState,
492 raw: &mut [u8],
493) -> Result<(), String> {
494 assert!(raw.len() >= COEF_BLOCK_SIZE * 2);
495
496 (0..2).try_for_each(|ch| {
497 let base_offset = COEF_BLOCK_SIZE * ch;
498
499 let pos = base_offset + EQ_OUTPUT_OFFSET;
500 serialize_f32(&state.output[ch], &mut raw[pos..(pos + 4)])?;
501
502 state.low_coef[ch]
503 .0
504 .iter()
505 .chain(state.low_middle_coef[ch].0.iter())
506 .chain(state.high_middle_coef[ch].0.iter())
507 .chain(state.high_coef[ch].0.iter())
508 .enumerate()
509 .try_for_each(|(i, coef)| {
510 let pos = base_offset + EQ_LOW_FREQ_OFFSET + i * 4;
511 serialize_f32(coef, &mut raw[pos..(pos + 4)])
512 })
513 })
514}
515
516fn deserialize_equalizer_state(
518 state: &mut Spro24DspEqualizerState,
519 raw: &[u8],
520) -> Result<(), String> {
521 assert!(raw.len() >= COEF_BLOCK_SIZE * 2);
522
523 (0..2).try_for_each(|ch| {
524 let base_offset = COEF_BLOCK_SIZE * ch;
525
526 let pos = base_offset + EQ_OUTPUT_OFFSET;
527 deserialize_f32(&mut state.output[ch], &raw[pos..(pos + 4)])?;
528
529 state.low_coef[ch]
530 .0
531 .iter_mut()
532 .chain(state.low_middle_coef[ch].0.iter_mut())
533 .chain(state.high_middle_coef[ch].0.iter_mut())
534 .chain(state.high_coef[ch].0.iter_mut())
535 .enumerate()
536 .try_for_each(|(i, coef)| {
537 let pos = base_offset + EQ_LOW_FREQ_OFFSET + i * 4;
538 deserialize_f32(coef, &raw[pos..(pos + 4)])
539 })
540 })
541}
542
543fn serialize_reverb_state(state: &Spro24DspReverbState, raw: &mut [u8]) -> Result<(), String> {
545 assert!(raw.len() >= COEF_BLOCK_SIZE);
546
547 let pos = REVERB_SIZE_OFFSET;
548 serialize_f32(&state.size, &mut raw[pos..(pos + 4)])?;
549
550 let pos = REVERB_AIR_OFFSET;
551 serialize_f32(&state.air, &mut raw[pos..(pos + 4)])?;
552
553 let enabled_pos = REVERB_ENABLE_OFFSET;
554 let disabled_pos = REVERB_DISABLE_OFFSET;
555 let vals = if state.enabled {
556 [1.0, 0.0]
557 } else {
558 [0.0, 1.0]
559 };
560 serialize_f32(&vals[0], &mut raw[enabled_pos..(enabled_pos + 4)])?;
561 serialize_f32(&vals[1], &mut raw[disabled_pos..(disabled_pos + 4)])?;
562
563 let pos = REVERB_ENABLE_OFFSET;
564 let val = if state.enabled { 1.0 } else { 0.0 };
565 serialize_f32(&val, &mut raw[pos..(pos + 4)])?;
566
567 let pos = REVERB_PRE_FILTER_VALUE_OFFSET;
568 let val = state.pre_filter.abs();
569 serialize_f32(&val, &mut raw[pos..(pos + 4)])?;
570
571 let pos = REVERB_PRE_FILTER_SIGN_OFFSET;
572 let sign = if state.pre_filter > 0.0 { 1.0 } else { 0.0 };
573 serialize_f32(&sign, &mut raw[pos..(pos + 4)])?;
574
575 Ok(())
576}
577
578fn deserialize_reverb_state(state: &mut Spro24DspReverbState, raw: &[u8]) -> Result<(), String> {
580 assert!(raw.len() >= COEF_BLOCK_SIZE);
581
582 let pos = REVERB_SIZE_OFFSET;
583 deserialize_f32(&mut state.size, &raw[pos..(pos + 4)])?;
584
585 let pos = REVERB_AIR_OFFSET;
586 deserialize_f32(&mut state.air, &raw[pos..(pos + 4)])?;
587
588 let mut val = 0.0;
589 let pos = REVERB_ENABLE_OFFSET;
590 deserialize_f32(&mut val, &raw[pos..(pos + 4)])?;
591 state.enabled = val > 0.0;
592
593 let mut val = 0.0;
594 let pos = REVERB_PRE_FILTER_VALUE_OFFSET;
595 deserialize_f32(&mut val, &raw[pos..(pos + 4)])?;
596
597 let mut sign = 0.0;
598 let pos = REVERB_PRE_FILTER_SIGN_OFFSET;
599 deserialize_f32(&mut sign, &raw[pos..(pos + 4)])?;
600 if sign == 0.0 {
601 val *= -1.0;
602 }
603 state.pre_filter = val;
604
605 Ok(())
606}
607
608fn serialize_effect_general_params(
609 params: &Spro24DspEffectGeneralParams,
610 raw: &mut [u8],
611) -> Result<(), String> {
612 assert!(raw.len() >= 4);
613
614 let mut val = 0u32;
615
616 (0..2).for_each(|i| {
617 let mut flags = 0u16;
618 if params.eq_after_comp[i] {
619 flags |= CH_STRIP_FLAG_EQ_AFTER_COMP;
620 }
621 if params.comp_enable[i] {
622 flags |= CH_STRIP_FLAG_COMP_ENABLE;
623 }
624 if params.eq_enable[i] {
625 flags |= CH_STRIP_FLAG_EQ_ENABLE;
626 }
627
628 val |= (flags as u32) << (16 * i);
629 });
630
631 serialize_u32(&val, raw);
632
633 Ok(())
634}
635
636fn deserialize_effect_general_params(
637 params: &mut Spro24DspEffectGeneralParams,
638 raw: &[u8],
639) -> Result<(), String> {
640 assert!(raw.len() >= 4);
641
642 let mut val = 0u32;
643 deserialize_u32(&mut val, raw);
644 (0..2).for_each(|i| {
645 let flags = (val >> (i * 16)) as u16;
646 params.eq_after_comp[i] = flags & CH_STRIP_FLAG_EQ_AFTER_COMP > 0;
647 params.comp_enable[i] = flags & CH_STRIP_FLAG_COMP_ENABLE > 0;
648 params.eq_enable[i] = flags & CH_STRIP_FLAG_EQ_ENABLE > 0;
649 });
650
651 Ok(())
652}
653
654impl TcatExtensionSectionParamsOperation<Spro24DspEffectGeneralParams> for SPro24DspProtocol {
655 fn cache_extension_whole_params(
656 req: &FwReq,
657 node: &FwNode,
658 sections: &ExtensionSections,
659 _: &ExtensionCaps,
660 params: &mut Spro24DspEffectGeneralParams,
661 timeout_ms: u32,
662 ) -> Result<(), Error> {
663 let mut raw = vec![0u8; 4];
664 Self::read_extension(
665 req,
666 node,
667 §ions.application,
668 CH_STRIP_FLAG_OFFSET,
669 &mut raw,
670 timeout_ms,
671 )?;
672 deserialize_effect_general_params(params, &raw)
673 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
674 }
675}
676
677impl TcatExtensionSectionPartialMutableParamsOperation<Spro24DspEffectGeneralParams>
678 for SPro24DspProtocol
679{
680 fn update_extension_partial_params(
681 req: &FwReq,
682 node: &FwNode,
683 sections: &ExtensionSections,
684 _: &ExtensionCaps,
685 params: &Spro24DspEffectGeneralParams,
686 prev: &mut Spro24DspEffectGeneralParams,
687 timeout_ms: u32,
688 ) -> Result<(), Error> {
689 let mut new = vec![0u8; 4];
690 serialize_effect_general_params(params, &mut new)
691 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
692
693 let mut old = vec![0u8; 4];
694 serialize_effect_general_params(prev, &mut old)
695 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
696
697 if new != old {
698 Self::write_extension(
699 req,
700 node,
701 §ions.application,
702 CH_STRIP_FLAG_OFFSET,
703 &mut new,
704 timeout_ms,
705 )?;
706 Self::write_sw_notice(req, node, sections, CH_STRIP_FLAG_SW_NOTICE, timeout_ms)?;
707 }
708 deserialize_effect_general_params(prev, &new)
709 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
710 }
711}
712
713impl TcatExtensionSectionParamsOperation<Spro24DspCompressorState> for SPro24DspProtocol {
714 fn cache_extension_whole_params(
715 req: &FwReq,
716 node: &FwNode,
717 sections: &ExtensionSections,
718 _: &ExtensionCaps,
719 params: &mut Spro24DspCompressorState,
720 timeout_ms: u32,
721 ) -> Result<(), Error> {
722 let mut raw = vec![0u8; COEF_BLOCK_SIZE * 2];
723 Self::read_extension(
724 req,
725 node,
726 §ions.application,
727 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_COMP,
728 &mut raw,
729 timeout_ms,
730 )?;
731 deserialize_compressor_state(params, &raw)
732 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
733 }
734}
735
736impl TcatExtensionSectionPartialMutableParamsOperation<Spro24DspCompressorState>
737 for SPro24DspProtocol
738{
739 fn update_extension_partial_params(
740 req: &FwReq,
741 node: &FwNode,
742 sections: &ExtensionSections,
743 _: &ExtensionCaps,
744 params: &Spro24DspCompressorState,
745 prev: &mut Spro24DspCompressorState,
746 timeout_ms: u32,
747 ) -> Result<(), Error> {
748 let mut new = vec![0u8; COEF_BLOCK_SIZE * 2];
749 serialize_compressor_state(params, &mut new)
750 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
751
752 let mut old = vec![0u8; COEF_BLOCK_SIZE * 2];
753 serialize_compressor_state(prev, &mut old)
754 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
755
756 (0..(COEF_BLOCK_SIZE * 2)).step_by(4).try_for_each(|pos| {
757 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
758 Self::write_extension(
759 req,
760 node,
761 §ions.application,
762 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_COMP + pos,
763 &mut new[pos..(pos + 4)],
764 timeout_ms,
765 )
766 } else {
767 Ok(())
768 }
769 })?;
770 Self::write_sw_notice(req, node, sections, COMP_CH0_SW_NOTICE, timeout_ms)?;
771 Self::write_sw_notice(req, node, sections, COMP_CH1_SW_NOTICE, timeout_ms)?;
772
773 deserialize_compressor_state(prev, &new)
774 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
775 }
776}
777
778impl TcatExtensionSectionParamsOperation<Spro24DspEqualizerState> for SPro24DspProtocol {
779 fn cache_extension_whole_params(
780 req: &FwReq,
781 node: &FwNode,
782 sections: &ExtensionSections,
783 _: &ExtensionCaps,
784 params: &mut Spro24DspEqualizerState,
785 timeout_ms: u32,
786 ) -> Result<(), Error> {
787 let mut raw = vec![0u8; COEF_BLOCK_SIZE * 2];
788 Self::read_extension(
789 req,
790 node,
791 §ions.application,
792 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_EQ,
793 &mut raw,
794 timeout_ms,
795 )?;
796 deserialize_equalizer_state(params, &raw)
797 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
798 }
799}
800
801impl TcatExtensionSectionPartialMutableParamsOperation<Spro24DspEqualizerState>
802 for SPro24DspProtocol
803{
804 fn update_extension_partial_params(
805 req: &FwReq,
806 node: &FwNode,
807 sections: &ExtensionSections,
808 _: &ExtensionCaps,
809 params: &Spro24DspEqualizerState,
810 prev: &mut Spro24DspEqualizerState,
811 timeout_ms: u32,
812 ) -> Result<(), Error> {
813 let mut new = vec![0u8; COEF_BLOCK_SIZE * 2];
814 serialize_equalizer_state(params, &mut new)
815 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
816
817 let mut old = vec![0u8; COEF_BLOCK_SIZE * 2];
818 serialize_equalizer_state(prev, &mut old)
819 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
820
821 (0..(COEF_BLOCK_SIZE * 2)).step_by(4).try_for_each(|pos| {
822 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
823 Self::write_extension(
824 req,
825 node,
826 §ions.application,
827 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_EQ + pos,
828 &mut new[pos..(pos + 4)],
829 timeout_ms,
830 )
831 } else {
832 Ok(())
833 }
834 })?;
835 Self::write_sw_notice(req, node, sections, EQ_OUTPUT_CH0_SW_NOTICE, timeout_ms)?;
836 Self::write_sw_notice(req, node, sections, EQ_OUTPUT_CH1_SW_NOTICE, timeout_ms)?;
837 Self::write_sw_notice(req, node, sections, EQ_LOW_FREQ_CH0_SW_NOTICE, timeout_ms)?;
838 Self::write_sw_notice(req, node, sections, EQ_LOW_FREQ_CH1_SW_NOTICE, timeout_ms)?;
839 Self::write_sw_notice(
840 req,
841 node,
842 sections,
843 EQ_LOW_MIDDLE_FREQ_CH0_SW_NOTICE,
844 timeout_ms,
845 )?;
846 Self::write_sw_notice(
847 req,
848 node,
849 sections,
850 EQ_LOW_MIDDLE_FREQ_CH1_SW_NOTICE,
851 timeout_ms,
852 )?;
853 Self::write_sw_notice(
854 req,
855 node,
856 sections,
857 EQ_HIGH_MIDDLE_FREQ_CH0_SW_NOTICE,
858 timeout_ms,
859 )?;
860 Self::write_sw_notice(
861 req,
862 node,
863 sections,
864 EQ_HIGH_MIDDLE_FREQ_CH1_SW_NOTICE,
865 timeout_ms,
866 )?;
867 Self::write_sw_notice(req, node, sections, EQ_HIGH_FREQ_CH0_SW_NOTICE, timeout_ms)?;
868 Self::write_sw_notice(req, node, sections, EQ_HIGH_FREQ_CH1_SW_NOTICE, timeout_ms)?;
869
870 deserialize_equalizer_state(prev, &new)
871 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
872 }
873}
874
875impl TcatExtensionSectionParamsOperation<Spro24DspReverbState> for SPro24DspProtocol {
876 fn cache_extension_whole_params(
877 req: &FwReq,
878 node: &FwNode,
879 sections: &ExtensionSections,
880 _: &ExtensionCaps,
881 params: &mut Spro24DspReverbState,
882 timeout_ms: u32,
883 ) -> Result<(), Error> {
884 let mut raw = vec![0u8; COEF_BLOCK_SIZE];
885 Self::read_extension(
886 req,
887 node,
888 §ions.application,
889 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_REVERB,
890 &mut raw,
891 timeout_ms,
892 )?;
893 deserialize_reverb_state(params, &raw)
894 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
895 }
896}
897
898impl TcatExtensionSectionPartialMutableParamsOperation<Spro24DspReverbState> for SPro24DspProtocol {
899 fn update_extension_partial_params(
900 req: &FwReq,
901 node: &FwNode,
902 sections: &ExtensionSections,
903 _: &ExtensionCaps,
904 params: &Spro24DspReverbState,
905 prev: &mut Spro24DspReverbState,
906 timeout_ms: u32,
907 ) -> Result<(), Error> {
908 let mut new = vec![0u8; COEF_BLOCK_SIZE];
909 serialize_reverb_state(params, &mut new)
910 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
911
912 let mut old = vec![0u8; COEF_BLOCK_SIZE];
913 serialize_reverb_state(prev, &mut old)
914 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
915
916 (0..(COEF_BLOCK_SIZE * 2)).step_by(4).try_for_each(|pos| {
917 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
918 Self::write_extension(
919 req,
920 node,
921 §ions.application,
922 COEF_OFFSET + COEF_BLOCK_SIZE * COEF_BLOCK_REVERB + pos,
923 &mut new[pos..(pos + 4)],
924 timeout_ms,
925 )
926 } else {
927 Ok(())
928 }
929 })?;
930 Self::write_sw_notice(req, node, sections, REVERB_SW_NOTICE, timeout_ms)?;
931 deserialize_reverb_state(prev, &new)
932 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
933 }
934}
935
936impl SPro24DspProtocol {
937 pub const COMPRESSOR_OUTPUT_MIN: f32 = 0.0;
938 pub const COMPRESSOR_OUTPUT_MAX: f32 = 64.0;
939
940 pub const COMPRESSOR_THRESHOLD_MIN: f32 = -1.25;
941 pub const COMPRESSOR_THRESHOLD_MAX: f32 = 0.0;
942
943 pub const COMPRESSOR_RATIO_MIN: f32 = 0.03125;
944 pub const COMPRESSOR_RATIO_MAX: f32 = 0.5;
945
946 pub const COMPRESSOR_ATTACK_MIN: f32 = -1.0;
947 pub const COMPRESSOR_ATTACK_MAX: f32 = -0.9375;
948
949 pub const COMPRESSOR_RELEASE_MIN: f32 = 0.9375;
950 pub const COMPRESSOR_RELEASE_MAX: f32 = 1.0;
951
952 pub const EQUALIZER_OUTPUT_MIN: f32 = 0.0;
953 pub const EQUALIZER_OUTPUT_MAX: f32 = 1.0;
954
955 pub const REVERB_SIZE_MIN: f32 = 0.0;
956 pub const REVERB_SIZE_MAX: f32 = 1.0;
957
958 pub const REVERB_AIR_MIN: f32 = 0.0;
959 pub const REVERB_AIR_MAX: f32 = 1.0;
960
961 pub const REVERB_PRE_FILTER_MIN: f32 = -1.0;
962 pub const REVERB_PRE_FILTER_MAX: f32 = 1.0;
963}
964
965#[cfg(test)]
966mod test {
967 use super::*;
968
969 #[test]
970 fn compressor_state_serdes() {
971 let state = Spro24DspCompressorState {
972 output: [0.04, 0.05],
973 threshold: [0.16, 0.17],
974 ratio: [0.20, 0.21],
975 attack: [0.32, 0.33],
976 release: [0.44, 0.45],
977 };
978
979 let mut raw = [0u8; COEF_BLOCK_SIZE * 2];
980 serialize_compressor_state(&state, &mut raw).unwrap();
981
982 let mut s = Spro24DspCompressorState::default();
983 deserialize_compressor_state(&mut s, &raw).unwrap();
984
985 assert_eq!(state, s);
986 }
987
988 #[test]
989 fn equalizer_state_serdes() {
990 let state = Spro24DspEqualizerState {
991 output: [0.06, 0.07],
992 low_coef: [
993 Spro24DspEqualizerFrequencyBandState([0.00, 0.01, 0.02, 0.03, 0.04]),
994 Spro24DspEqualizerFrequencyBandState([0.10, 0.11, 0.12, 0.13, 0.14]),
995 ],
996 low_middle_coef: [
997 Spro24DspEqualizerFrequencyBandState([0.20, 0.21, 0.22, 0.23, 0.24]),
998 Spro24DspEqualizerFrequencyBandState([0.30, 0.31, 0.32, 0.33, 0.34]),
999 ],
1000 high_middle_coef: [
1001 Spro24DspEqualizerFrequencyBandState([0.40, 0.41, 0.42, 0.43, 0.44]),
1002 Spro24DspEqualizerFrequencyBandState([0.50, 0.51, 0.52, 0.53, 0.54]),
1003 ],
1004 high_coef: [
1005 Spro24DspEqualizerFrequencyBandState([0.60, 0.61, 0.62, 0.63, 0.64]),
1006 Spro24DspEqualizerFrequencyBandState([0.70, 0.71, 0.72, 0.73, 0.74]),
1007 ],
1008 };
1009
1010 let mut raw = [0u8; COEF_BLOCK_SIZE * 2];
1011 serialize_equalizer_state(&state, &mut raw).unwrap();
1012
1013 let mut s = Spro24DspEqualizerState::default();
1014 deserialize_equalizer_state(&mut s, &raw).unwrap();
1015
1016 assert_eq!(state, s);
1017 }
1018
1019 #[test]
1020 fn reverb_state_serdes() {
1021 let state = Spro24DspReverbState {
1022 size: 0.04,
1023 air: 0.14,
1024 enabled: false,
1025 pre_filter: -0.1,
1026 };
1027 let mut raw = [0u8; COEF_BLOCK_SIZE];
1028 serialize_reverb_state(&state, &mut raw).unwrap();
1029
1030 let mut s = Spro24DspReverbState::default();
1031 deserialize_reverb_state(&mut s, &raw).unwrap();
1032
1033 assert_eq!(state, s);
1034 }
1035
1036 #[test]
1037 fn effect_general_params_serdes() {
1038 let params = Spro24DspEffectGeneralParams {
1039 eq_after_comp: [false, true],
1040 comp_enable: [true, false],
1041 eq_enable: [false, true],
1042 };
1043 let mut raw = [0u8; 4];
1044 serialize_effect_general_params(¶ms, &mut raw).unwrap();
1045
1046 let mut p = Spro24DspEffectGeneralParams::default();
1047 deserialize_effect_general_params(&mut p, &raw).unwrap();
1048
1049 assert_eq!(params, p);
1050 }
1051}