firewire_motu_protocols/
command_dsp.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol for hardware mixer function operated by command.
5//!
6//! The module includes structure, enumeration, and trait for hardware mixer function operated by
7//! command.
8//!
9//! The hardware transfers asynchronous packet to registered address when changing its state by
10//! user operation. The packet includes some command to express the change of status. The
11//! hardware also accepts the same command in asynchronous packet arrived at specific address.
12
13use {
14    super::*,
15    hinawa::{prelude::FwRespExt, FwResp},
16};
17
18const DSP_CMD_OFFSET: u64 = 0xffff00010000;
19const DSP_MSG_DST_HIGH_OFFSET: u32 = 0x0b38;
20const DSP_MSG_DST_LOW_OFFSET: u32 = 0x0b3c;
21
22const MAXIMUM_DSP_FRAME_SIZE: usize = 248;
23
24const CMD_RESOURCE: u8 = 0x23;
25const CMD_BYTE_MULTIPLE: u8 = 0x49;
26const CMD_QUADLET_MULTIPLE: u8 = 0x46;
27const CMD_DRAIN: u8 = 0x62;
28const CMD_END: u8 = 0x65;
29const CMD_BYTE_SINGLE: u8 = 0x69;
30const CMD_QUADLET_SINGLE: u8 = 0x66;
31
32const CMD_RESOURCE_LENGTH: usize = 6;
33const CMD_BYTE_SINGLE_LENGTH: usize = 6;
34const CMD_QUADLET_SINGLE_LENGTH: usize = 9;
35
36const MSG_DST_OFFSET_BEGIN: u64 = 0xffffe0000000;
37const MSG_DST_OFFSET_END: u64 = MSG_DST_OFFSET_BEGIN + 0x10000000;
38
39/// The mode of stereo-paired channels.
40#[derive(Debug, Copy, Clone, Eq, PartialEq)]
41pub enum InputStereoPairMode {
42    /// Adjustable left/right balance.
43    LeftRight,
44    /// Adjustable monaural/stereo balance.
45    MonauralStereo,
46    Reserved(u8),
47}
48
49impl Default for InputStereoPairMode {
50    fn default() -> Self {
51        InputStereoPairMode::LeftRight
52    }
53}
54
55impl From<u8> for InputStereoPairMode {
56    fn from(val: u8) -> Self {
57        match val {
58            0 => Self::LeftRight,
59            1 => Self::MonauralStereo,
60            _ => Self::Reserved(val),
61        }
62    }
63}
64
65impl From<InputStereoPairMode> for u8 {
66    fn from(mode: InputStereoPairMode) -> Self {
67        match mode {
68            InputStereoPairMode::LeftRight => 0,
69            InputStereoPairMode::MonauralStereo => 1,
70            InputStereoPairMode::Reserved(val) => val,
71        }
72    }
73}
74
75/// The level to decline audio signal.
76#[derive(Debug, Copy, Clone, Eq, PartialEq)]
77pub enum RollOffLevel {
78    /// 6 dB per octave.
79    L6,
80    /// 12 dB per octave.
81    L12,
82    /// 18 dB per octave.
83    L18,
84    /// 24 dB per octave.
85    L24,
86    /// 30 dB per octave.
87    L30,
88    /// 36 dB per octave.
89    L36,
90    Reserved(u8),
91}
92
93impl Default for RollOffLevel {
94    fn default() -> Self {
95        Self::L6
96    }
97}
98
99impl From<u8> for RollOffLevel {
100    fn from(val: u8) -> Self {
101        match val {
102            0 => Self::L6,
103            1 => Self::L12,
104            2 => Self::L18,
105            3 => Self::L24,
106            4 => Self::L30,
107            5 => Self::L36,
108            _ => Self::Reserved(val),
109        }
110    }
111}
112
113impl From<RollOffLevel> for u8 {
114    fn from(level: RollOffLevel) -> Self {
115        match level {
116            RollOffLevel::L6 => 0,
117            RollOffLevel::L12 => 1,
118            RollOffLevel::L18 => 2,
119            RollOffLevel::L24 => 3,
120            RollOffLevel::L30 => 4,
121            RollOffLevel::L36 => 5,
122            RollOffLevel::Reserved(val) => val,
123        }
124    }
125}
126
127/// The type of filter for equalizer (5 options).
128#[derive(Debug, Copy, Clone, Eq, PartialEq)]
129pub enum FilterType5 {
130    T1,
131    T2,
132    T3,
133    T4,
134    Shelf,
135    Reserved(u8),
136}
137
138impl Default for FilterType5 {
139    fn default() -> Self {
140        Self::T1
141    }
142}
143
144impl From<u8> for FilterType5 {
145    fn from(val: u8) -> Self {
146        match val {
147            0 => Self::T1,
148            1 => Self::T2,
149            2 => Self::T3,
150            3 => Self::T4,
151            4 => Self::Shelf,
152            _ => Self::Reserved(val),
153        }
154    }
155}
156
157impl From<FilterType5> for u8 {
158    fn from(filter_type: FilterType5) -> Self {
159        match filter_type {
160            FilterType5::T1 => 0,
161            FilterType5::T2 => 1,
162            FilterType5::T3 => 2,
163            FilterType5::T4 => 3,
164            FilterType5::Shelf => 4,
165            FilterType5::Reserved(val) => val,
166        }
167    }
168}
169
170/// The type of filter for equalizer (5 options).
171#[derive(Debug, Copy, Clone, Eq, PartialEq)]
172pub enum FilterType4 {
173    T1,
174    T2,
175    T3,
176    T4,
177    Reserved(u8),
178}
179
180impl Default for FilterType4 {
181    fn default() -> Self {
182        Self::T1
183    }
184}
185
186impl From<u8> for FilterType4 {
187    fn from(val: u8) -> Self {
188        match val {
189            0 => Self::T1,
190            1 => Self::T2,
191            2 => Self::T3,
192            3 => Self::T4,
193            _ => Self::Reserved(val),
194        }
195    }
196}
197
198impl From<FilterType4> for u8 {
199    fn from(filter_type: FilterType4) -> Self {
200        match filter_type {
201            FilterType4::T1 => 0,
202            FilterType4::T2 => 1,
203            FilterType4::T3 => 2,
204            FilterType4::T4 => 3,
205            FilterType4::Reserved(val) => val,
206        }
207    }
208}
209
210/// The way to decide loudness level of input signal.
211#[derive(Debug, Copy, Clone, Eq, PartialEq)]
212pub enum LevelDetectMode {
213    /// According to the peak of signal.
214    Peak,
215    /// According to the Root Mean Square of signal.
216    Rms,
217    Reserved(u8),
218}
219
220impl Default for LevelDetectMode {
221    fn default() -> Self {
222        Self::Peak
223    }
224}
225
226impl From<u8> for LevelDetectMode {
227    fn from(val: u8) -> Self {
228        match val {
229            0 => Self::Peak,
230            1 => Self::Rms,
231            _ => Self::Reserved(val),
232        }
233    }
234}
235
236impl From<LevelDetectMode> for u8 {
237    fn from(mode: LevelDetectMode) -> Self {
238        match mode {
239            LevelDetectMode::Peak => 0,
240            LevelDetectMode::Rms => 1,
241            LevelDetectMode::Reserved(val) => val,
242        }
243    }
244}
245
246/// The mode of leveler.
247#[derive(Debug, Copy, Clone, Eq, PartialEq)]
248pub enum LevelerMode {
249    Compress,
250    Limit,
251    Reserved(u8),
252}
253
254impl Default for LevelerMode {
255    fn default() -> Self {
256        LevelerMode::Compress
257    }
258}
259
260impl From<u8> for LevelerMode {
261    fn from(val: u8) -> Self {
262        match val {
263            0 => Self::Compress,
264            1 => Self::Limit,
265            _ => Self::Reserved(val),
266        }
267    }
268}
269
270impl From<LevelerMode> for u8 {
271    fn from(mode: LevelerMode) -> Self {
272        match mode {
273            LevelerMode::Compress => 0,
274            LevelerMode::Limit => 1,
275            LevelerMode::Reserved(val) => val,
276        }
277    }
278}
279
280/// The DSP command specific to equalizer effects.
281#[derive(Debug, Clone, PartialEq)]
282pub enum EqualizerParameter {
283    Enable(bool),
284    HpfEnable(bool),
285    HpfSlope(RollOffLevel),
286    HpfFreq(u32),
287    LpfEnable(bool),
288    LpfSlope(RollOffLevel),
289    LpfFreq(u32),
290    LfEnable(bool),
291    LfType(FilterType5),
292    LfFreq(u32),
293    LfGain(f32),
294    LfWidth(f32),
295    LmfEnable(bool),
296    LmfType(FilterType4),
297    LmfFreq(u32),
298    LmfGain(f32),
299    LmfWidth(f32),
300    MfEnable(bool),
301    MfType(FilterType4),
302    MfFreq(u32),
303    MfGain(f32),
304    MfWidth(f32),
305    HmfEnable(bool),
306    HmfType(FilterType4),
307    HmfFreq(u32),
308    HmfGain(f32),
309    HmfWidth(f32),
310    HfEnable(bool),
311    HfType(FilterType5),
312    HfFreq(u32),
313    HfGain(f32),
314    HfWidth(f32),
315}
316
317/// The DSP command specific to dynamics effects.
318#[derive(Debug, Clone, PartialEq)]
319pub enum DynamicsParameter {
320    Enable(bool),
321    CompEnable(bool),
322    CompDetectMode(LevelDetectMode),
323    CompThreshold(i32),
324    CompRatio(f32),
325    CompAttack(u32),
326    CompRelease(u32),
327    CompGain(f32),
328    LevelerEnable(bool),
329    LevelerMode(LevelerMode),
330    LevelerMakeup(u32),
331    LevelerReduce(u32),
332}
333
334fn to_bool(raw: &[u8]) -> bool {
335    assert_eq!(raw.len(), 1);
336
337    raw[0] > 0
338}
339
340fn to_usize(raw: &[u8]) -> usize {
341    assert_eq!(raw.len(), 1);
342
343    raw[0] as usize
344}
345
346fn to_i32(raw: &[u8]) -> i32 {
347    to_f32(raw) as i32
348}
349
350fn to_f32(raw: &[u8]) -> f32 {
351    assert_eq!(raw.len(), 4);
352
353    let mut quadlet = [0; 4];
354    quadlet.copy_from_slice(raw);
355
356    f32::from_le_bytes(quadlet)
357}
358
359fn to_u32(raw: &[u8]) -> u32 {
360    to_f32(raw) as u32
361}
362
363fn append_data(raw: &mut Vec<u8>, identifier: &[u8], vals: &[u8]) {
364    if vals.len() == 4 {
365        raw.push(CMD_QUADLET_SINGLE);
366        raw.extend_from_slice(identifier);
367        raw.extend_from_slice(vals);
368    } else {
369        raw.push(CMD_BYTE_SINGLE);
370        raw.extend_from_slice(vals);
371        raw.extend_from_slice(identifier);
372    }
373}
374
375/// Target of focus.
376#[derive(Debug, Copy, Clone, Eq, PartialEq)]
377pub enum FocusTarget {
378    Output(usize),
379    Input(usize),
380    Reserved(usize, usize),
381}
382
383impl Default for FocusTarget {
384    fn default() -> Self {
385        Self::Output(0)
386    }
387}
388
389impl From<&[u8]> for FocusTarget {
390    fn from(raw: &[u8]) -> Self {
391        match raw[3] {
392            0x01 => Self::Input(raw[0] as usize),
393            0x03 => Self::Output(raw[0] as usize),
394            _ => Self::Reserved(raw[3] as usize, raw[0] as usize),
395        }
396    }
397}
398
399impl From<&FocusTarget> for Vec<u8> {
400    fn from(target: &FocusTarget) -> Self {
401        match target {
402            FocusTarget::Input(ch) => vec![*ch as u8, 0x00, 0x00, 0x01],
403            FocusTarget::Output(ch) => vec![*ch as u8, 0x00, 0x00, 0x03],
404            FocusTarget::Reserved(dir, ch) => vec![*ch as u8, 0x00, 0x00, *dir as u8],
405        }
406    }
407}
408
409/// The DSP command specific to master output.
410#[derive(Debug, Clone, PartialEq)]
411pub enum MonitorCmd {
412    Volume(f32),
413    TalkbackEnable(bool),
414    ListenbackEnable(bool),
415    TalkbackVolume(f32),
416    ListenbackVolume(f32),
417    Focus(FocusTarget),
418    ReturnAssign(usize),
419    Reserved(Vec<u8>, Vec<u8>),
420}
421
422impl MonitorCmd {
423    fn parse(identifier: &[u8], vals: &[u8]) -> Self {
424        assert_eq!(identifier.len(), 4);
425        assert!(vals.len() > 0);
426
427        match (identifier[3], identifier[2], identifier[1]) {
428            (0x00, 0x00, 0x00) => MonitorCmd::Volume(to_f32(vals)),
429            (0x00, 0x00, 0x01) => MonitorCmd::TalkbackEnable(to_bool(vals)),
430            (0x00, 0x00, 0x02) => MonitorCmd::ListenbackEnable(to_bool(vals)),
431            // TODO: model dependent, I guess.
432            // (0, 0, 3) => u8
433            // (0, 0, 4) => u8
434            (0x00, 0x00, 0x05) => MonitorCmd::TalkbackVolume(to_f32(vals)),
435            (0x00, 0x00, 0x06) => MonitorCmd::ListenbackVolume(to_f32(vals)),
436            (0x00, 0x00, 0x07) => MonitorCmd::Focus(FocusTarget::from(vals)),
437            (0x00, 0x00, 0x08) => MonitorCmd::ReturnAssign(to_usize(vals)),
438            _ => MonitorCmd::Reserved(identifier.to_vec(), vals.to_vec()),
439        }
440    }
441
442    fn build(&self, raw: &mut Vec<u8>) {
443        match self {
444            MonitorCmd::Volume(val) => append_f32(raw, 0x00, 0x00, 0x00, 0, *val),
445            MonitorCmd::TalkbackEnable(val) => append_u8(raw, 0x00, 0x00, 0x01, 0, *val as u8),
446            MonitorCmd::ListenbackEnable(val) => append_u8(raw, 0x00, 0x00, 0x02, 0, *val as u8),
447            MonitorCmd::TalkbackVolume(val) => append_f32(raw, 0x00, 0x00, 0x05, 0, *val),
448            MonitorCmd::ListenbackVolume(val) => append_f32(raw, 0x00, 0x00, 0x06, 0, *val),
449            MonitorCmd::Focus(target) => {
450                append_data(raw, &[0x00, 0x07, 0x00, 0x00], &Vec::from(target))
451            }
452            MonitorCmd::ReturnAssign(target) => append_u8(raw, 0x00, 0x00, 0x08, 0, *target as u8),
453            MonitorCmd::Reserved(identifier, vals) => append_data(raw, identifier, vals),
454        }
455    }
456}
457
458/// The DSP command specific to input.
459#[derive(Debug, Clone, PartialEq)]
460pub enum InputCmd {
461    Phase(usize, bool),
462    Pair(usize, bool),
463    Gain(usize, i32),
464    Swap(usize, bool),
465    StereoMode(usize, InputStereoPairMode),
466    Width(usize, f32),
467    Equalizer(usize, EqualizerParameter),
468    Dynamics(usize, DynamicsParameter),
469    ReverbSend(usize, f32),
470    ReverbLrBalance(usize, f32),
471    Pad(usize, bool),
472    NominalLevel(usize, NominalSignalLevel),
473    Phantom(usize, bool),
474    Limitter(usize, bool),
475    Lookahead(usize, bool),
476    Softclip(usize, bool),
477    Reserved(Vec<u8>, Vec<u8>),
478}
479
480impl InputCmd {
481    fn parse(identifier: &[u8], vals: &[u8]) -> Self {
482        assert_eq!(identifier.len(), 4);
483        assert!(vals.len() > 0);
484
485        let ch = identifier[0] as usize;
486
487        match (identifier[3], identifier[2], identifier[1]) {
488            (0x01, 0x00, 0x00) => InputCmd::Phase(ch, to_bool(vals)),
489            (0x01, 0x00, 0x01) => InputCmd::Pair(ch, to_bool(vals)),
490            (0x01, 0x00, 0x02) => InputCmd::Gain(ch, to_i32(vals)),
491            (0x01, 0x00, 0x03) => InputCmd::Swap(ch, to_bool(vals)),
492            (0x01, 0x00, 0x04) => InputCmd::StereoMode(ch, InputStereoPairMode::from(vals[0])),
493            (0x01, 0x00, 0x05) => InputCmd::Width(ch, to_f32(vals)),
494            (0x01, 0x00, 0x06) => InputCmd::Limitter(ch, to_bool(vals)),
495            (0x01, 0x00, 0x07) => InputCmd::Lookahead(ch, to_bool(vals)),
496            (0x01, 0x00, 0x08) => InputCmd::Softclip(ch, to_bool(vals)),
497            (0x01, 0x00, 0x09) => InputCmd::Pad(ch, to_bool(vals)),
498            (0x01, 0x00, 0x0a) => {
499                let level = if to_bool(vals) {
500                    NominalSignalLevel::Professional
501                } else {
502                    NominalSignalLevel::Consumer
503                };
504                InputCmd::NominalLevel(ch, level)
505            }
506            (0x01, 0x00, 0x0b) => InputCmd::Phantom(ch, to_bool(vals)),
507
508            (0x01, 0x01, 0x00) => {
509                InputCmd::Equalizer(ch, EqualizerParameter::Enable(to_bool(vals)))
510            }
511
512            (0x01, 0x02, 0x00) => {
513                InputCmd::Equalizer(ch, EqualizerParameter::HpfEnable(to_bool(vals)))
514            }
515            (0x01, 0x02, 0x01) => InputCmd::Equalizer(
516                ch,
517                EqualizerParameter::HpfSlope(RollOffLevel::from(vals[0])),
518            ),
519            (0x01, 0x02, 0x02) => {
520                InputCmd::Equalizer(ch, EqualizerParameter::HpfFreq(to_u32(vals)))
521            }
522
523            (0x01, 0x03, 0x00) => {
524                InputCmd::Equalizer(ch, EqualizerParameter::LfEnable(to_bool(vals)))
525            }
526            (0x01, 0x03, 0x01) => {
527                InputCmd::Equalizer(ch, EqualizerParameter::LfType(FilterType5::from(vals[0])))
528            }
529            (0x01, 0x03, 0x02) => InputCmd::Equalizer(ch, EqualizerParameter::LfFreq(to_u32(vals))),
530            (0x01, 0x03, 0x03) => InputCmd::Equalizer(ch, EqualizerParameter::LfGain(to_f32(vals))),
531            (0x01, 0x03, 0x04) => {
532                InputCmd::Equalizer(ch, EqualizerParameter::LfWidth(to_f32(vals)))
533            }
534
535            (0x01, 0x04, 0x00) => {
536                InputCmd::Equalizer(ch, EqualizerParameter::LmfEnable(to_bool(vals)))
537            }
538            (0x01, 0x04, 0x01) => {
539                InputCmd::Equalizer(ch, EqualizerParameter::LmfType(FilterType4::from(vals[0])))
540            }
541            (0x01, 0x04, 0x02) => {
542                InputCmd::Equalizer(ch, EqualizerParameter::LmfFreq(to_u32(vals)))
543            }
544            (0x01, 0x04, 0x03) => {
545                InputCmd::Equalizer(ch, EqualizerParameter::LmfGain(to_f32(vals)))
546            }
547            (0x01, 0x04, 0x04) => {
548                InputCmd::Equalizer(ch, EqualizerParameter::LmfWidth(to_f32(vals)))
549            }
550
551            (0x01, 0x05, 0x00) => {
552                InputCmd::Equalizer(ch, EqualizerParameter::MfEnable(to_bool(vals)))
553            }
554            (0x01, 0x05, 0x01) => {
555                InputCmd::Equalizer(ch, EqualizerParameter::MfType(FilterType4::from(vals[0])))
556            }
557            (0x01, 0x05, 0x02) => InputCmd::Equalizer(ch, EqualizerParameter::MfFreq(to_u32(vals))),
558            (0x01, 0x05, 0x03) => InputCmd::Equalizer(ch, EqualizerParameter::MfGain(to_f32(vals))),
559            (0x01, 0x05, 0x04) => {
560                InputCmd::Equalizer(ch, EqualizerParameter::MfWidth(to_f32(vals)))
561            }
562
563            (0x01, 0x06, 0x00) => {
564                InputCmd::Equalizer(ch, EqualizerParameter::HmfEnable(to_bool(vals)))
565            }
566            (0x01, 0x06, 0x01) => {
567                InputCmd::Equalizer(ch, EqualizerParameter::HmfType(FilterType4::from(vals[0])))
568            }
569            (0x01, 0x06, 0x02) => {
570                InputCmd::Equalizer(ch, EqualizerParameter::HmfFreq(to_u32(vals)))
571            }
572            (0x01, 0x06, 0x03) => {
573                InputCmd::Equalizer(ch, EqualizerParameter::HmfGain(to_f32(vals)))
574            }
575            (0x01, 0x06, 0x04) => {
576                InputCmd::Equalizer(ch, EqualizerParameter::HmfWidth(to_f32(vals)))
577            }
578
579            (0x01, 0x07, 0x00) => {
580                InputCmd::Equalizer(ch, EqualizerParameter::HfEnable(to_bool(vals)))
581            }
582            (0x01, 0x07, 0x01) => {
583                InputCmd::Equalizer(ch, EqualizerParameter::HfType(FilterType5::from(vals[0])))
584            }
585            (0x01, 0x07, 0x02) => InputCmd::Equalizer(ch, EqualizerParameter::HfFreq(to_u32(vals))),
586            (0x01, 0x07, 0x03) => InputCmd::Equalizer(ch, EqualizerParameter::HfGain(to_f32(vals))),
587            (0x01, 0x07, 0x04) => {
588                InputCmd::Equalizer(ch, EqualizerParameter::HfWidth(to_f32(vals)))
589            }
590
591            (0x01, 0x08, 0x00) => {
592                InputCmd::Equalizer(ch, EqualizerParameter::LpfEnable(to_bool(vals)))
593            }
594            (0x01, 0x08, 0x01) => InputCmd::Equalizer(
595                ch,
596                EqualizerParameter::LpfSlope(RollOffLevel::from(vals[0])),
597            ),
598            (0x01, 0x08, 0x02) => {
599                InputCmd::Equalizer(ch, EqualizerParameter::LpfFreq(to_u32(vals)))
600            }
601
602            (0x01, 0x09, 0x00) => InputCmd::Dynamics(ch, DynamicsParameter::Enable(to_bool(vals))),
603
604            (0x01, 0x0a, 0x00) => {
605                InputCmd::Dynamics(ch, DynamicsParameter::CompEnable(to_bool(vals)))
606            }
607            (0x01, 0x0a, 0x01) => {
608                InputCmd::Dynamics(ch, DynamicsParameter::CompThreshold(to_i32(vals)))
609            }
610            (0x01, 0x0a, 0x02) => {
611                InputCmd::Dynamics(ch, DynamicsParameter::CompRatio(to_f32(vals)))
612            }
613            (0x01, 0x0a, 0x03) => {
614                InputCmd::Dynamics(ch, DynamicsParameter::CompAttack(to_u32(vals)))
615            }
616            (0x01, 0x0a, 0x04) => {
617                InputCmd::Dynamics(ch, DynamicsParameter::CompRelease(to_u32(vals)))
618            }
619            (0x01, 0x0a, 0x05) => InputCmd::Dynamics(ch, DynamicsParameter::CompGain(to_f32(vals))),
620            (0x01, 0x0a, 0x06) => InputCmd::Dynamics(
621                ch,
622                DynamicsParameter::CompDetectMode(LevelDetectMode::from(vals[0])),
623            ),
624
625            (0x01, 0x0b, 0x00) => {
626                InputCmd::Dynamics(ch, DynamicsParameter::LevelerEnable(to_bool(vals)))
627            }
628            (0x01, 0x0b, 0x01) => InputCmd::Dynamics(
629                ch,
630                DynamicsParameter::LevelerMode(LevelerMode::from(vals[0])),
631            ),
632            (0x01, 0x0b, 0x02) => {
633                InputCmd::Dynamics(ch, DynamicsParameter::LevelerMakeup(to_u32(vals)))
634            }
635            (0x01, 0x0b, 0x03) => {
636                InputCmd::Dynamics(ch, DynamicsParameter::LevelerReduce(to_u32(vals)))
637            }
638
639            (0x01, 0x0c, 0x00) => InputCmd::ReverbSend(ch, to_f32(vals)),
640            (0x01, 0x0c, 0x02) => InputCmd::ReverbLrBalance(ch, to_f32(vals)),
641
642            // TODO: model dependent, I guess.
643            // (0x01, 0xfe, 0x00) => u8
644            // (0x01, 0xfe, 0x01) => i32
645            // (0x01, 0xfe, 0x02) => i32
646            // (0x01, 0xfe, 0x03) => u8
647            _ => InputCmd::Reserved(identifier.to_vec(), vals.to_vec()),
648        }
649    }
650
651    fn build(&self, raw: &mut Vec<u8>) {
652        match self {
653            InputCmd::Phase(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x00, *ch, *enabled),
654            InputCmd::Pair(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x01, *ch, *enabled),
655            InputCmd::Gain(ch, val) => append_i32(raw, 0x01, 0x00, 0x02, *ch, *val),
656            InputCmd::Swap(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x03, *ch, *enabled),
657            InputCmd::StereoMode(ch, pair_mode) => {
658                append_u8(raw, 0x01, 0x00, 0x04, *ch, *pair_mode)
659            }
660            InputCmd::Width(ch, val) => append_f32(raw, 0x01, 0x00, 0x05, *ch, *val),
661            InputCmd::Limitter(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x06, *ch, *enabled),
662            InputCmd::Lookahead(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x07, *ch, *enabled),
663            InputCmd::Softclip(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x08, *ch, *enabled),
664            InputCmd::Pad(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x09, *ch, *enabled),
665            InputCmd::NominalLevel(ch, level) => {
666                let val = if *level == NominalSignalLevel::Professional {
667                    0x01
668                } else {
669                    0x00
670                };
671                append_u8(raw, 0x01, 0x00, 0x0a, *ch, val)
672            }
673
674            InputCmd::Phantom(ch, enabled) => append_u8(raw, 0x01, 0x00, 0x0b, *ch, *enabled),
675
676            InputCmd::Equalizer(ch, EqualizerParameter::Enable(enabled)) => {
677                append_u8(raw, 0x01, 0x01, 0x00, *ch, *enabled)
678            }
679
680            InputCmd::Equalizer(ch, EqualizerParameter::HpfEnable(enabled)) => {
681                append_u8(raw, 0x01, 0x02, 0x00, *ch, *enabled)
682            }
683            InputCmd::Equalizer(ch, EqualizerParameter::HpfSlope(level)) => {
684                append_u8(raw, 0x01, 0x02, 0x01, *ch, *level)
685            }
686            InputCmd::Equalizer(ch, EqualizerParameter::HpfFreq(val)) => {
687                append_u32(raw, 0x01, 0x02, 0x02, *ch, *val)
688            }
689
690            InputCmd::Equalizer(ch, EqualizerParameter::LfEnable(enabled)) => {
691                append_u8(raw, 0x01, 0x03, 0x00, *ch, *enabled)
692            }
693            InputCmd::Equalizer(ch, EqualizerParameter::LfType(filter_type)) => {
694                append_u8(raw, 0x01, 0x03, 0x01, *ch, *filter_type)
695            }
696            InputCmd::Equalizer(ch, EqualizerParameter::LfFreq(val)) => {
697                append_u32(raw, 0x01, 0x03, 0x02, *ch, *val)
698            }
699            InputCmd::Equalizer(ch, EqualizerParameter::LfGain(val)) => {
700                append_f32(raw, 0x01, 0x03, 0x03, *ch, *val)
701            }
702            InputCmd::Equalizer(ch, EqualizerParameter::LfWidth(val)) => {
703                append_f32(raw, 0x01, 0x03, 0x04, *ch, *val)
704            }
705
706            InputCmd::Equalizer(ch, EqualizerParameter::LmfEnable(enabled)) => {
707                append_u8(raw, 0x01, 0x04, 0x00, *ch, *enabled)
708            }
709            InputCmd::Equalizer(ch, EqualizerParameter::LmfType(filter_type)) => {
710                append_u8(raw, 0x01, 0x04, 0x01, *ch, *filter_type)
711            }
712            InputCmd::Equalizer(ch, EqualizerParameter::LmfFreq(val)) => {
713                append_u32(raw, 0x01, 0x04, 0x02, *ch, *val)
714            }
715            InputCmd::Equalizer(ch, EqualizerParameter::LmfGain(val)) => {
716                append_f32(raw, 0x01, 0x04, 0x03, *ch, *val)
717            }
718            InputCmd::Equalizer(ch, EqualizerParameter::LmfWidth(val)) => {
719                append_f32(raw, 0x01, 0x04, 0x04, *ch, *val)
720            }
721
722            InputCmd::Equalizer(ch, EqualizerParameter::MfEnable(enabled)) => {
723                append_u8(raw, 0x01, 0x05, 0x00, *ch, *enabled)
724            }
725            InputCmd::Equalizer(ch, EqualizerParameter::MfType(filter_type)) => {
726                append_u8(raw, 0x01, 0x05, 0x01, *ch, *filter_type)
727            }
728            InputCmd::Equalizer(ch, EqualizerParameter::MfFreq(val)) => {
729                append_u32(raw, 0x01, 0x05, 0x02, *ch, *val)
730            }
731            InputCmd::Equalizer(ch, EqualizerParameter::MfGain(val)) => {
732                append_f32(raw, 0x01, 0x05, 0x03, *ch, *val)
733            }
734            InputCmd::Equalizer(ch, EqualizerParameter::MfWidth(val)) => {
735                append_f32(raw, 0x01, 0x05, 0x04, *ch, *val)
736            }
737
738            InputCmd::Equalizer(ch, EqualizerParameter::HmfEnable(enabled)) => {
739                append_u8(raw, 0x01, 0x06, 0x00, *ch, *enabled)
740            }
741            InputCmd::Equalizer(ch, EqualizerParameter::HmfType(filter_type)) => {
742                append_u8(raw, 0x01, 0x06, 0x01, *ch, *filter_type)
743            }
744            InputCmd::Equalizer(ch, EqualizerParameter::HmfFreq(val)) => {
745                append_u32(raw, 0x01, 0x06, 0x02, *ch, *val)
746            }
747            InputCmd::Equalizer(ch, EqualizerParameter::HmfGain(val)) => {
748                append_f32(raw, 0x01, 0x06, 0x03, *ch, *val)
749            }
750            InputCmd::Equalizer(ch, EqualizerParameter::HmfWidth(val)) => {
751                append_f32(raw, 0x01, 0x06, 0x04, *ch, *val)
752            }
753
754            InputCmd::Equalizer(ch, EqualizerParameter::HfEnable(enabled)) => {
755                append_u8(raw, 0x01, 0x07, 0x00, *ch, *enabled)
756            }
757            InputCmd::Equalizer(ch, EqualizerParameter::HfType(filter_type)) => {
758                append_u8(raw, 0x01, 0x07, 0x01, *ch, *filter_type)
759            }
760            InputCmd::Equalizer(ch, EqualizerParameter::HfFreq(val)) => {
761                append_u32(raw, 0x01, 0x07, 0x02, *ch, *val)
762            }
763            InputCmd::Equalizer(ch, EqualizerParameter::HfGain(val)) => {
764                append_f32(raw, 0x01, 0x07, 0x03, *ch, *val)
765            }
766            InputCmd::Equalizer(ch, EqualizerParameter::HfWidth(val)) => {
767                append_f32(raw, 0x01, 0x07, 0x04, *ch, *val)
768            }
769
770            InputCmd::Equalizer(ch, EqualizerParameter::LpfEnable(enabled)) => {
771                append_u8(raw, 0x01, 0x08, 0x00, *ch, *enabled)
772            }
773            InputCmd::Equalizer(ch, EqualizerParameter::LpfSlope(level)) => {
774                append_u8(raw, 0x01, 0x08, 0x01, *ch, *level)
775            }
776            InputCmd::Equalizer(ch, EqualizerParameter::LpfFreq(val)) => {
777                append_u32(raw, 0x01, 0x08, 0x02, *ch, *val)
778            }
779
780            InputCmd::Dynamics(ch, DynamicsParameter::Enable(enabled)) => {
781                append_u8(raw, 0x01, 0x09, 0x00, *ch, *enabled)
782            }
783
784            InputCmd::Dynamics(ch, DynamicsParameter::CompEnable(enabled)) => {
785                append_u8(raw, 0x01, 0x0a, 0x00, *ch, *enabled)
786            }
787            InputCmd::Dynamics(ch, DynamicsParameter::CompThreshold(val)) => {
788                append_i32(raw, 0x01, 0x0a, 0x01, *ch, *val)
789            }
790            InputCmd::Dynamics(ch, DynamicsParameter::CompRatio(val)) => {
791                append_f32(raw, 0x01, 0x0a, 0x02, *ch, *val)
792            }
793            InputCmd::Dynamics(ch, DynamicsParameter::CompAttack(val)) => {
794                append_u32(raw, 0x01, 0x0a, 0x03, *ch, *val)
795            }
796            InputCmd::Dynamics(ch, DynamicsParameter::CompRelease(val)) => {
797                append_u32(raw, 0x01, 0x0a, 0x04, *ch, *val)
798            }
799            InputCmd::Dynamics(ch, DynamicsParameter::CompGain(val)) => {
800                append_f32(raw, 0x01, 0x0a, 0x05, *ch, *val)
801            }
802            InputCmd::Dynamics(ch, DynamicsParameter::CompDetectMode(mode)) => {
803                append_u8(raw, 0x01, 0x0a, 0x06, *ch, *mode)
804            }
805
806            InputCmd::Dynamics(ch, DynamicsParameter::LevelerEnable(enabled)) => {
807                append_u8(raw, 0x01, 0x0b, 0x00, *ch, *enabled)
808            }
809            InputCmd::Dynamics(ch, DynamicsParameter::LevelerMode(mode)) => {
810                append_u8(raw, 0x01, 0x0b, 0x01, *ch, *mode)
811            }
812            InputCmd::Dynamics(ch, DynamicsParameter::LevelerMakeup(val)) => {
813                append_u32(raw, 0x01, 0x0b, 0x02, *ch, *val)
814            }
815            InputCmd::Dynamics(ch, DynamicsParameter::LevelerReduce(val)) => {
816                append_u32(raw, 0x01, 0x0b, 0x03, *ch, *val)
817            }
818
819            InputCmd::ReverbSend(ch, val) => append_f32(raw, 0x01, 0x0c, 0x00, *ch, *val),
820            InputCmd::ReverbLrBalance(ch, val) => append_f32(raw, 0x01, 0x0c, 0x02, *ch, *val),
821
822            InputCmd::Reserved(identifier, vals) => append_data(raw, identifier, vals),
823        }
824    }
825}
826
827/// The mode of stereo pair for source of mixer.
828#[derive(Debug, Copy, Clone, Eq, PartialEq)]
829pub enum SourceStereoPairMode {
830    Width,
831    LrBalance,
832    Reserved(u8),
833}
834
835impl Default for SourceStereoPairMode {
836    fn default() -> Self {
837        Self::Width
838    }
839}
840
841impl From<u8> for SourceStereoPairMode {
842    fn from(val: u8) -> Self {
843        match val {
844            0 => Self::Width,
845            1 => Self::LrBalance,
846            _ => Self::Reserved(val),
847        }
848    }
849}
850
851impl From<SourceStereoPairMode> for u8 {
852    fn from(mode: SourceStereoPairMode) -> Self {
853        match mode {
854            SourceStereoPairMode::Width => 0,
855            SourceStereoPairMode::LrBalance => 1,
856            SourceStereoPairMode::Reserved(val) => val,
857        }
858    }
859}
860
861/// The DSP command specific to mixer.
862#[derive(Debug, Clone, PartialEq)]
863pub enum MixerCmd {
864    OutputAssign(usize, usize),
865    OutputMute(usize, bool),
866    OutputVolume(usize, f32),
867    ReverbSend(usize, f32),
868    ReverbReturn(usize, f32),
869    SourceMute(usize, usize, bool),
870    SourceSolo(usize, usize, bool),
871    SourceMonauralLrBalance(usize, usize, f32),
872    SourceGain(usize, usize, f32),
873    SourceStereoMode(usize, usize, SourceStereoPairMode),
874    SourceStereoLrBalance(usize, usize, f32),
875    SourceStereoWidth(usize, usize, f32),
876    Reserved(Vec<u8>, Vec<u8>),
877}
878
879impl MixerCmd {
880    fn parse(identifier: &[u8], vals: &[u8]) -> Self {
881        assert_eq!(identifier.len(), 4);
882        assert!(vals.len() > 0);
883
884        let ch = identifier[0] as usize;
885        let mixer_src_ch = identifier[2] as usize;
886
887        match (identifier[3], identifier[2], identifier[1]) {
888            (0x02, 0x00, 0x00) => MixerCmd::OutputAssign(ch, to_usize(vals)),
889            (0x02, 0x00, 0x01) => MixerCmd::OutputMute(ch, to_bool(vals)),
890            (0x02, 0x00, 0x02) => MixerCmd::OutputVolume(ch, to_f32(vals)),
891
892            (0x02, 0x01, 0x00) => MixerCmd::ReverbSend(ch, to_f32(vals)),
893            (0x02, 0x01, 0x01) => MixerCmd::ReverbReturn(ch, to_f32(vals)),
894
895            (0x02, _, 0x00) => MixerCmd::SourceMute(ch, mixer_src_ch - 2, to_bool(vals)),
896            (0x02, _, 0x01) => MixerCmd::SourceSolo(ch, mixer_src_ch - 2, to_bool(vals)),
897            (0x02, _, 0x02) => {
898                MixerCmd::SourceMonauralLrBalance(ch, mixer_src_ch - 2, to_f32(vals))
899            }
900            (0x02, _, 0x03) => MixerCmd::SourceGain(ch, mixer_src_ch - 2, to_f32(vals)),
901            (0x02, _, 0x04) => MixerCmd::SourceStereoMode(
902                ch,
903                mixer_src_ch - 2,
904                SourceStereoPairMode::from(vals[0]),
905            ),
906            (0x02, _, 0x05) => MixerCmd::SourceStereoLrBalance(ch, mixer_src_ch - 2, to_f32(vals)),
907            (0x02, _, 0x06) => MixerCmd::SourceStereoWidth(ch, mixer_src_ch - 2, to_f32(vals)),
908            _ => MixerCmd::Reserved(identifier.to_vec(), vals.to_vec()),
909        }
910    }
911
912    fn build(&self, raw: &mut Vec<u8>) {
913        match self {
914            MixerCmd::OutputAssign(ch, target) => {
915                append_u8(raw, 0x02, 0x00, 0x00, *ch, *target as u8)
916            }
917            MixerCmd::OutputMute(ch, enabled) => append_u8(raw, 0x02, 0x00, 0x01, *ch, *enabled),
918            MixerCmd::OutputVolume(ch, val) => append_f32(raw, 0x02, 0x00, 0x02, *ch, *val),
919
920            MixerCmd::ReverbSend(ch, val) => append_f32(raw, 0x02, 0x01, 0x00, *ch, *val),
921            MixerCmd::ReverbReturn(ch, val) => append_f32(raw, 0x02, 0x01, 0x01, *ch, *val),
922
923            MixerCmd::SourceMute(ch, mixer_src_ch, enabled) => {
924                append_u8(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x00, *ch, *enabled)
925            }
926            MixerCmd::SourceSolo(ch, mixer_src_ch, enabled) => {
927                append_u8(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x01, *ch, *enabled)
928            }
929            MixerCmd::SourceMonauralLrBalance(ch, mixer_src_ch, val) => {
930                append_f32(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x02, *ch, *val)
931            }
932            MixerCmd::SourceGain(ch, mixer_src_ch, val) => {
933                append_f32(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x03, *ch, *val)
934            }
935            MixerCmd::SourceStereoMode(ch, mixer_src_ch, pair_mode) => {
936                append_u8(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x04, *ch, *pair_mode)
937            }
938            MixerCmd::SourceStereoLrBalance(ch, mixer_src_ch, val) => {
939                append_f32(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x05, *ch, *val)
940            }
941            MixerCmd::SourceStereoWidth(ch, mixer_src_ch, val) => {
942                append_f32(raw, 0x02, (*mixer_src_ch + 2) as u8, 0x06, *ch, *val)
943            }
944
945            MixerCmd::Reserved(identifier, vals) => append_data(raw, identifier, vals),
946        }
947    }
948}
949
950/// The DSP command specific to input.
951#[derive(Debug, Clone, PartialEq)]
952pub enum OutputCmd {
953    Equalizer(usize, EqualizerParameter),
954    Dynamics(usize, DynamicsParameter),
955    ReverbSend(usize, f32),
956    ReverbReturn(usize, f32),
957    MasterMonitor(usize, bool),
958    MasterTalkback(usize, bool),
959    MasterListenback(usize, bool),
960    Reserved(Vec<u8>, Vec<u8>),
961}
962
963impl OutputCmd {
964    fn parse(identifier: &[u8], vals: &[u8]) -> Self {
965        let ch = identifier[0] as usize;
966
967        match (identifier[3], identifier[2], identifier[1]) {
968            (0x03, 0x00, 0x00) => {
969                OutputCmd::Equalizer(ch, EqualizerParameter::Enable(to_bool(vals)))
970            }
971
972            (0x03, 0x01, 0x00) => {
973                OutputCmd::Equalizer(ch, EqualizerParameter::HpfEnable(to_bool(vals)))
974            }
975            (0x03, 0x01, 0x01) => OutputCmd::Equalizer(
976                ch,
977                EqualizerParameter::HpfSlope(RollOffLevel::from(vals[0])),
978            ),
979            (0x03, 0x01, 0x02) => {
980                OutputCmd::Equalizer(ch, EqualizerParameter::HpfFreq(to_u32(vals)))
981            }
982
983            (0x03, 0x02, 0x00) => {
984                OutputCmd::Equalizer(ch, EqualizerParameter::LfEnable(to_bool(vals)))
985            }
986            (0x03, 0x02, 0x01) => {
987                OutputCmd::Equalizer(ch, EqualizerParameter::LfType(FilterType5::from(vals[0])))
988            }
989            (0x03, 0x02, 0x02) => {
990                OutputCmd::Equalizer(ch, EqualizerParameter::LfFreq(to_u32(vals)))
991            }
992            (0x03, 0x02, 0x03) => {
993                OutputCmd::Equalizer(ch, EqualizerParameter::LfGain(to_f32(vals)))
994            }
995            (0x03, 0x02, 0x04) => {
996                OutputCmd::Equalizer(ch, EqualizerParameter::LfWidth(to_f32(vals)))
997            }
998
999            (0x03, 0x03, 0x00) => {
1000                OutputCmd::Equalizer(ch, EqualizerParameter::LmfEnable(to_bool(vals)))
1001            }
1002            (0x03, 0x03, 0x01) => {
1003                OutputCmd::Equalizer(ch, EqualizerParameter::LmfType(FilterType4::from(vals[0])))
1004            }
1005            (0x03, 0x03, 0x02) => {
1006                OutputCmd::Equalizer(ch, EqualizerParameter::LmfFreq(to_u32(vals)))
1007            }
1008            (0x03, 0x03, 0x03) => {
1009                OutputCmd::Equalizer(ch, EqualizerParameter::LmfGain(to_f32(vals)))
1010            }
1011            (0x03, 0x03, 0x04) => {
1012                OutputCmd::Equalizer(ch, EqualizerParameter::LmfWidth(to_f32(vals)))
1013            }
1014
1015            (0x03, 0x04, 0x00) => {
1016                OutputCmd::Equalizer(ch, EqualizerParameter::MfEnable(to_bool(vals)))
1017            }
1018            (0x03, 0x04, 0x01) => {
1019                OutputCmd::Equalizer(ch, EqualizerParameter::MfType(FilterType4::from(vals[0])))
1020            }
1021            (0x03, 0x04, 0x02) => {
1022                OutputCmd::Equalizer(ch, EqualizerParameter::MfFreq(to_u32(vals)))
1023            }
1024            (0x03, 0x04, 0x03) => {
1025                OutputCmd::Equalizer(ch, EqualizerParameter::MfGain(to_f32(vals)))
1026            }
1027            (0x03, 0x04, 0x04) => {
1028                OutputCmd::Equalizer(ch, EqualizerParameter::MfWidth(to_f32(vals)))
1029            }
1030
1031            (0x03, 0x05, 0x00) => {
1032                OutputCmd::Equalizer(ch, EqualizerParameter::HmfEnable(to_bool(vals)))
1033            }
1034            (0x03, 0x05, 0x01) => {
1035                OutputCmd::Equalizer(ch, EqualizerParameter::HmfType(FilterType4::from(vals[0])))
1036            }
1037            (0x03, 0x05, 0x02) => {
1038                OutputCmd::Equalizer(ch, EqualizerParameter::HmfFreq(to_u32(vals)))
1039            }
1040            (0x03, 0x05, 0x03) => {
1041                OutputCmd::Equalizer(ch, EqualizerParameter::HmfGain(to_f32(vals)))
1042            }
1043            (0x03, 0x05, 0x04) => {
1044                OutputCmd::Equalizer(ch, EqualizerParameter::HmfWidth(to_f32(vals)))
1045            }
1046
1047            (0x03, 0x06, 0x00) => {
1048                OutputCmd::Equalizer(ch, EqualizerParameter::HfEnable(to_bool(vals)))
1049            }
1050            (0x03, 0x06, 0x01) => {
1051                OutputCmd::Equalizer(ch, EqualizerParameter::HfType(FilterType5::from(vals[0])))
1052            }
1053            (0x03, 0x06, 0x02) => {
1054                OutputCmd::Equalizer(ch, EqualizerParameter::HfFreq(to_u32(vals)))
1055            }
1056            (0x03, 0x06, 0x03) => {
1057                OutputCmd::Equalizer(ch, EqualizerParameter::HfGain(to_f32(vals)))
1058            }
1059            (0x03, 0x06, 0x04) => {
1060                OutputCmd::Equalizer(ch, EqualizerParameter::HfWidth(to_f32(vals)))
1061            }
1062
1063            (0x03, 0x07, 0x00) => {
1064                OutputCmd::Equalizer(ch, EqualizerParameter::LpfEnable(to_bool(vals)))
1065            }
1066            (0x03, 0x07, 0x01) => OutputCmd::Equalizer(
1067                ch,
1068                EqualizerParameter::LpfSlope(RollOffLevel::from(vals[0])),
1069            ),
1070            (0x03, 0x07, 0x02) => {
1071                OutputCmd::Equalizer(ch, EqualizerParameter::LpfFreq(to_u32(vals)))
1072            }
1073
1074            (0x03, 0x08, 0x00) => OutputCmd::Dynamics(ch, DynamicsParameter::Enable(to_bool(vals))),
1075
1076            (0x03, 0x09, 0x00) => {
1077                OutputCmd::Dynamics(ch, DynamicsParameter::CompEnable(to_bool(vals)))
1078            }
1079            (0x03, 0x09, 0x01) => {
1080                OutputCmd::Dynamics(ch, DynamicsParameter::CompThreshold(to_i32(vals)))
1081            }
1082            (0x03, 0x09, 0x02) => {
1083                OutputCmd::Dynamics(ch, DynamicsParameter::CompRatio(to_f32(vals)))
1084            }
1085            (0x03, 0x09, 0x03) => {
1086                OutputCmd::Dynamics(ch, DynamicsParameter::CompAttack(to_u32(vals)))
1087            }
1088            (0x03, 0x09, 0x04) => {
1089                OutputCmd::Dynamics(ch, DynamicsParameter::CompRelease(to_u32(vals)))
1090            }
1091            (0x03, 0x09, 0x05) => {
1092                OutputCmd::Dynamics(ch, DynamicsParameter::CompGain(to_f32(vals)))
1093            }
1094            (0x03, 0x09, 0x06) => OutputCmd::Dynamics(
1095                ch,
1096                DynamicsParameter::CompDetectMode(LevelDetectMode::from(vals[0])),
1097            ),
1098
1099            (0x03, 0x0a, 0x00) => {
1100                OutputCmd::Dynamics(ch, DynamicsParameter::LevelerEnable(to_bool(vals)))
1101            }
1102            (0x03, 0x0a, 0x01) => OutputCmd::Dynamics(
1103                ch,
1104                DynamicsParameter::LevelerMode(LevelerMode::from(vals[0])),
1105            ),
1106            (0x03, 0x0a, 0x02) => {
1107                OutputCmd::Dynamics(ch, DynamicsParameter::LevelerMakeup(to_u32(vals)))
1108            }
1109            (0x03, 0x0a, 0x03) => {
1110                OutputCmd::Dynamics(ch, DynamicsParameter::LevelerReduce(to_u32(vals)))
1111            }
1112
1113            (0x03, 0x0b, 0x00) => OutputCmd::ReverbSend(ch, to_f32(vals)),
1114            (0x03, 0x0b, 0x01) => OutputCmd::ReverbReturn(ch, to_f32(vals)),
1115
1116            (0x03, 0x0c, 0x00) => OutputCmd::MasterMonitor(ch, to_bool(vals)),
1117            (0x03, 0x0c, 0x01) => OutputCmd::MasterTalkback(ch, to_bool(vals)),
1118            (0x03, 0x0c, 0x02) => OutputCmd::MasterListenback(ch, to_bool(vals)),
1119
1120            _ => OutputCmd::Reserved(identifier.to_vec(), vals.to_vec()),
1121        }
1122    }
1123
1124    fn build(&self, raw: &mut Vec<u8>) {
1125        match self {
1126            OutputCmd::Equalizer(ch, EqualizerParameter::Enable(enabled)) => {
1127                append_u8(raw, 0x03, 0x00, 0x00, *ch, *enabled)
1128            }
1129
1130            OutputCmd::Equalizer(ch, EqualizerParameter::HpfEnable(enabled)) => {
1131                append_u8(raw, 0x03, 0x01, 0x00, *ch, *enabled)
1132            }
1133            OutputCmd::Equalizer(ch, EqualizerParameter::HpfSlope(level)) => {
1134                append_u8(raw, 0x03, 0x01, 0x01, *ch, *level)
1135            }
1136            OutputCmd::Equalizer(ch, EqualizerParameter::HpfFreq(val)) => {
1137                append_u32(raw, 0x03, 0x01, 0x02, *ch, *val)
1138            }
1139
1140            OutputCmd::Equalizer(ch, EqualizerParameter::LfEnable(enabled)) => {
1141                append_u8(raw, 0x03, 0x02, 0x00, *ch, *enabled)
1142            }
1143            OutputCmd::Equalizer(ch, EqualizerParameter::LfType(filter_type)) => {
1144                append_u8(raw, 0x03, 0x02, 0x01, *ch, *filter_type)
1145            }
1146            OutputCmd::Equalizer(ch, EqualizerParameter::LfFreq(val)) => {
1147                append_u32(raw, 0x03, 0x02, 0x02, *ch, *val)
1148            }
1149            OutputCmd::Equalizer(ch, EqualizerParameter::LfGain(val)) => {
1150                append_f32(raw, 0x03, 0x02, 0x03, *ch, *val)
1151            }
1152            OutputCmd::Equalizer(ch, EqualizerParameter::LfWidth(val)) => {
1153                append_f32(raw, 0x03, 0x02, 0x04, *ch, *val)
1154            }
1155
1156            OutputCmd::Equalizer(ch, EqualizerParameter::LmfEnable(enabled)) => {
1157                append_u8(raw, 0x03, 0x03, 0x00, *ch, *enabled)
1158            }
1159            OutputCmd::Equalizer(ch, EqualizerParameter::LmfType(filter_type)) => {
1160                append_u8(raw, 0x03, 0x03, 0x01, *ch, *filter_type)
1161            }
1162            OutputCmd::Equalizer(ch, EqualizerParameter::LmfFreq(val)) => {
1163                append_u32(raw, 0x03, 0x03, 0x02, *ch, *val)
1164            }
1165            OutputCmd::Equalizer(ch, EqualizerParameter::LmfGain(val)) => {
1166                append_f32(raw, 0x03, 0x03, 0x03, *ch, *val)
1167            }
1168            OutputCmd::Equalizer(ch, EqualizerParameter::LmfWidth(val)) => {
1169                append_f32(raw, 0x03, 0x03, 0x04, *ch, *val)
1170            }
1171
1172            OutputCmd::Equalizer(ch, EqualizerParameter::MfEnable(enabled)) => {
1173                append_u8(raw, 0x03, 0x04, 0x00, *ch, *enabled)
1174            }
1175            OutputCmd::Equalizer(ch, EqualizerParameter::MfType(filter_type)) => {
1176                append_u8(raw, 0x03, 0x04, 0x01, *ch, *filter_type)
1177            }
1178            OutputCmd::Equalizer(ch, EqualizerParameter::MfFreq(val)) => {
1179                append_u32(raw, 0x03, 0x04, 0x02, *ch, *val)
1180            }
1181            OutputCmd::Equalizer(ch, EqualizerParameter::MfGain(val)) => {
1182                append_f32(raw, 0x03, 0x04, 0x03, *ch, *val)
1183            }
1184            OutputCmd::Equalizer(ch, EqualizerParameter::MfWidth(val)) => {
1185                append_f32(raw, 0x03, 0x04, 0x04, *ch, *val)
1186            }
1187
1188            OutputCmd::Equalizer(ch, EqualizerParameter::HmfEnable(enabled)) => {
1189                append_u8(raw, 0x03, 0x05, 0x00, *ch, *enabled)
1190            }
1191            OutputCmd::Equalizer(ch, EqualizerParameter::HmfType(filter_type)) => {
1192                append_u8(raw, 0x03, 0x05, 0x01, *ch, *filter_type)
1193            }
1194            OutputCmd::Equalizer(ch, EqualizerParameter::HmfFreq(val)) => {
1195                append_u32(raw, 0x03, 0x05, 0x02, *ch, *val)
1196            }
1197            OutputCmd::Equalizer(ch, EqualizerParameter::HmfGain(val)) => {
1198                append_f32(raw, 0x03, 0x05, 0x03, *ch, *val)
1199            }
1200            OutputCmd::Equalizer(ch, EqualizerParameter::HmfWidth(val)) => {
1201                append_f32(raw, 0x03, 0x05, 0x04, *ch, *val)
1202            }
1203
1204            OutputCmd::Equalizer(ch, EqualizerParameter::HfEnable(enabled)) => {
1205                append_u8(raw, 0x03, 0x06, 0x00, *ch, *enabled)
1206            }
1207            OutputCmd::Equalizer(ch, EqualizerParameter::HfType(filter_type)) => {
1208                append_u8(raw, 0x03, 0x06, 0x01, *ch, *filter_type)
1209            }
1210            OutputCmd::Equalizer(ch, EqualizerParameter::HfFreq(val)) => {
1211                append_u32(raw, 0x03, 0x06, 0x02, *ch, *val)
1212            }
1213            OutputCmd::Equalizer(ch, EqualizerParameter::HfGain(val)) => {
1214                append_f32(raw, 0x03, 0x06, 0x03, *ch, *val)
1215            }
1216            OutputCmd::Equalizer(ch, EqualizerParameter::HfWidth(val)) => {
1217                append_f32(raw, 0x03, 0x06, 0x04, *ch, *val)
1218            }
1219
1220            OutputCmd::Equalizer(ch, EqualizerParameter::LpfEnable(enabled)) => {
1221                append_u8(raw, 0x03, 0x07, 0x00, *ch, *enabled)
1222            }
1223            OutputCmd::Equalizer(ch, EqualizerParameter::LpfSlope(level)) => {
1224                append_u8(raw, 0x03, 0x07, 0x01, *ch, *level)
1225            }
1226            OutputCmd::Equalizer(ch, EqualizerParameter::LpfFreq(val)) => {
1227                append_u32(raw, 0x03, 0x07, 0x02, *ch, *val)
1228            }
1229
1230            OutputCmd::Dynamics(ch, DynamicsParameter::Enable(enabled)) => {
1231                append_u8(raw, 0x03, 0x08, 0x00, *ch, *enabled)
1232            }
1233
1234            OutputCmd::Dynamics(ch, DynamicsParameter::CompEnable(enabled)) => {
1235                append_u8(raw, 0x03, 0x09, 0x00, *ch, *enabled)
1236            }
1237            OutputCmd::Dynamics(ch, DynamicsParameter::CompThreshold(val)) => {
1238                append_i32(raw, 0x03, 0x09, 0x01, *ch, *val)
1239            }
1240            OutputCmd::Dynamics(ch, DynamicsParameter::CompRatio(val)) => {
1241                append_f32(raw, 0x03, 0x09, 0x02, *ch, *val)
1242            }
1243            OutputCmd::Dynamics(ch, DynamicsParameter::CompAttack(val)) => {
1244                append_u32(raw, 0x03, 0x09, 0x03, *ch, *val)
1245            }
1246            OutputCmd::Dynamics(ch, DynamicsParameter::CompRelease(val)) => {
1247                append_u32(raw, 0x03, 0x09, 0x04, *ch, *val)
1248            }
1249            OutputCmd::Dynamics(ch, DynamicsParameter::CompGain(val)) => {
1250                append_f32(raw, 0x03, 0x09, 0x05, *ch, *val)
1251            }
1252            OutputCmd::Dynamics(ch, DynamicsParameter::CompDetectMode(mode)) => {
1253                append_u8(raw, 0x03, 0x09, 0x06, *ch, *mode)
1254            }
1255
1256            OutputCmd::Dynamics(ch, DynamicsParameter::LevelerEnable(enabled)) => {
1257                append_u8(raw, 0x03, 0x0a, 0x00, *ch, *enabled)
1258            }
1259            OutputCmd::Dynamics(ch, DynamicsParameter::LevelerMode(mode)) => {
1260                append_u8(raw, 0x03, 0x0a, 0x01, *ch, *mode)
1261            }
1262            OutputCmd::Dynamics(ch, DynamicsParameter::LevelerMakeup(val)) => {
1263                append_u32(raw, 0x03, 0x0a, 0x02, *ch, *val)
1264            }
1265            OutputCmd::Dynamics(ch, DynamicsParameter::LevelerReduce(val)) => {
1266                append_u32(raw, 0x03, 0x0a, 0x03, *ch, *val)
1267            }
1268
1269            OutputCmd::ReverbSend(ch, val) => append_f32(raw, 0x03, 0x0b, 0x00, *ch, *val),
1270            OutputCmd::ReverbReturn(ch, val) => append_f32(raw, 0x03, 0x0b, 0x01, *ch, *val),
1271
1272            OutputCmd::MasterMonitor(ch, val) => append_u8(raw, 0x03, 0x0c, 0x00, *ch, *val),
1273            OutputCmd::MasterTalkback(ch, enabled) => {
1274                append_u8(raw, 0x03, 0x0c, 0x01, *ch, *enabled)
1275            }
1276            OutputCmd::MasterListenback(ch, enabled) => {
1277                append_u8(raw, 0x03, 0x0c, 0x02, *ch, *enabled)
1278            }
1279
1280            OutputCmd::Reserved(identifier, vals) => append_data(raw, identifier, vals),
1281        }
1282    }
1283}
1284
1285/// The mode of early reflection.
1286#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1287pub enum RoomShape {
1288    A,
1289    B,
1290    C,
1291    D,
1292    E,
1293    Reserved(u8),
1294}
1295
1296impl Default for RoomShape {
1297    fn default() -> Self {
1298        Self::A
1299    }
1300}
1301
1302impl From<u8> for RoomShape {
1303    fn from(val: u8) -> Self {
1304        match val {
1305            0 => Self::A,
1306            1 => Self::B,
1307            2 => Self::C,
1308            3 => Self::D,
1309            4 => Self::E,
1310            _ => Self::Reserved(val),
1311        }
1312    }
1313}
1314
1315impl From<RoomShape> for u8 {
1316    fn from(shape: RoomShape) -> Self {
1317        match shape {
1318            RoomShape::A => 0,
1319            RoomShape::B => 1,
1320            RoomShape::C => 2,
1321            RoomShape::D => 3,
1322            RoomShape::E => 4,
1323            RoomShape::Reserved(val) => val,
1324        }
1325    }
1326}
1327
1328/// The point of split.
1329#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1330pub enum SplitPoint {
1331    Output,
1332    Mixer,
1333    Reserved(u8),
1334}
1335
1336impl Default for SplitPoint {
1337    fn default() -> Self {
1338        Self::Output
1339    }
1340}
1341
1342impl From<u8> for SplitPoint {
1343    fn from(val: u8) -> Self {
1344        match val {
1345            0 => Self::Output,
1346            1 => Self::Mixer,
1347            _ => Self::Reserved(val),
1348        }
1349    }
1350}
1351
1352impl From<SplitPoint> for u8 {
1353    fn from(point: SplitPoint) -> Self {
1354        match point {
1355            SplitPoint::Output => 0,
1356            SplitPoint::Mixer => 1,
1357            SplitPoint::Reserved(val) => val,
1358        }
1359    }
1360}
1361
1362/// The DSP command specific to reverb effect.
1363#[derive(Debug, Clone, PartialEq)]
1364pub enum ReverbCmd {
1365    Enable(bool),
1366    Split(SplitPoint),
1367    PreDelay(u32),
1368    ShelfFilterFreq(u32),
1369    ShelfFilterAttenuation(i32),
1370    DecayTime(u32),
1371    LowFreqTime(u32),
1372    MiddleFreqTime(u32),
1373    HighFreqTime(u32),
1374    LowFreqCrossover(u32),
1375    HighFreqCrossover(u32),
1376    Width(f32),
1377    ReflectionMode(RoomShape),
1378    ReflectionSize(u32),
1379    ReflectionLevel(f32),
1380    Reserved(Vec<u8>, Vec<u8>),
1381}
1382
1383impl ReverbCmd {
1384    fn parse(identifier: &[u8], vals: &[u8]) -> Self {
1385        assert_eq!(identifier.len(), 4);
1386        assert!(vals.len() > 0);
1387
1388        match (identifier[3], identifier[2], identifier[1]) {
1389            (0x04, 0x00, 0x00) => ReverbCmd::Enable(to_bool(vals)),
1390            (0x04, 0x00, 0x01) => ReverbCmd::Split(SplitPoint::from(vals[0])),
1391            (0x04, 0x00, 0x02) => ReverbCmd::PreDelay(to_u32(vals)),
1392            (0x04, 0x00, 0x03) => ReverbCmd::ShelfFilterFreq(to_u32(vals)),
1393            (0x04, 0x00, 0x04) => ReverbCmd::ShelfFilterAttenuation(to_i32(vals)),
1394            (0x04, 0x00, 0x05) => ReverbCmd::DecayTime(to_u32(vals)),
1395            (0x04, 0x00, 0x06) => ReverbCmd::LowFreqTime(to_u32(vals)),
1396            (0x04, 0x00, 0x07) => ReverbCmd::MiddleFreqTime(to_u32(vals)),
1397            (0x04, 0x00, 0x08) => ReverbCmd::HighFreqTime(to_u32(vals)),
1398            (0x04, 0x00, 0x09) => ReverbCmd::LowFreqCrossover(to_u32(vals)),
1399            (0x04, 0x00, 0x0a) => ReverbCmd::HighFreqCrossover(to_u32(vals)),
1400            (0x04, 0x00, 0x0b) => ReverbCmd::Width(to_f32(vals)),
1401            (0x04, 0x00, 0x0c) => ReverbCmd::ReflectionMode(RoomShape::from(vals[0])),
1402            (0x04, 0x00, 0x0d) => ReverbCmd::ReflectionSize(to_u32(vals)),
1403            (0x04, 0x00, 0x0e) => ReverbCmd::ReflectionLevel(to_f32(vals)),
1404            _ => ReverbCmd::Reserved(identifier.to_vec(), vals.to_vec()),
1405        }
1406    }
1407
1408    fn build(&self, raw: &mut Vec<u8>) {
1409        match self {
1410            ReverbCmd::Enable(enabled) => append_u8(raw, 0x04, 0x00, 0x00, 0, *enabled),
1411            ReverbCmd::Split(point) => append_u8(raw, 0x04, 0x00, 0x01, 0, *point),
1412            ReverbCmd::PreDelay(val) => append_u32(raw, 0x04, 0x00, 0x02, 0, *val),
1413            ReverbCmd::ShelfFilterFreq(val) => append_u32(raw, 0x04, 0x00, 0x03, 0, *val),
1414            ReverbCmd::ShelfFilterAttenuation(val) => append_i32(raw, 0x04, 0x00, 0x04, 0, *val),
1415            ReverbCmd::DecayTime(val) => append_u32(raw, 0x04, 0x00, 0x05, 0, *val),
1416            ReverbCmd::LowFreqTime(val) => append_u32(raw, 0x04, 0x00, 0x06, 0, *val),
1417            ReverbCmd::MiddleFreqTime(val) => append_u32(raw, 0x04, 0x00, 0x07, 0, *val),
1418            ReverbCmd::HighFreqTime(val) => append_u32(raw, 0x04, 0x00, 0x08, 0, *val),
1419            ReverbCmd::LowFreqCrossover(val) => append_u32(raw, 0x04, 0x00, 0x09, 0, *val),
1420            ReverbCmd::HighFreqCrossover(val) => append_u32(raw, 0x04, 0x00, 0x0a, 0, *val),
1421            ReverbCmd::Width(val) => append_f32(raw, 0x04, 0x00, 0x0b, 0, *val),
1422            ReverbCmd::ReflectionMode(shape) => append_u8(raw, 0x04, 0x00, 0x0c, 0, *shape),
1423            ReverbCmd::ReflectionSize(val) => append_u32(raw, 0x04, 0x00, 0x0d, 0, *val),
1424            ReverbCmd::ReflectionLevel(val) => append_f32(raw, 0x04, 0x00, 0x0e, 0, *val),
1425            ReverbCmd::Reserved(identifier, vals) => append_data(raw, identifier, vals),
1426        }
1427    }
1428}
1429
1430/// The DSP command specific to usage of resource.
1431#[derive(Debug, Clone, PartialEq)]
1432pub enum ResourceCmd {
1433    Usage(f32, u8),
1434    Reserved(Vec<u8>),
1435}
1436
1437impl ResourceCmd {
1438    pub const USAGE_MIN: f32 = 0.0;
1439    pub const USAGE_MAX: f32 = 100.0;
1440
1441    fn parse(raw: &[u8]) -> Self {
1442        let mut quadlet = [0; 4];
1443        quadlet.copy_from_slice(&raw[1..5]);
1444        ResourceCmd::Usage(f32::from_le_bytes(quadlet), raw[5])
1445    }
1446
1447    fn build(&self, raw: &mut Vec<u8>) {
1448        match self {
1449            Self::Usage(usage, flag) => append_resource(raw, *usage, *flag),
1450            Self::Reserved(data) => raw.extend_from_slice(data),
1451        }
1452    }
1453}
1454
1455/// The DSP command.
1456#[derive(Debug, Clone, PartialEq)]
1457pub enum DspCmd {
1458    Monitor(MonitorCmd),
1459    Input(InputCmd),
1460    Mixer(MixerCmd),
1461    Output(OutputCmd),
1462    Reverb(ReverbCmd),
1463    Resource(ResourceCmd),
1464    Reserved(Vec<u8>),
1465}
1466
1467impl DspCmd {
1468    // MEMO: Eight types of command are used in transaction frame from/to the target device. Each
1469    // type is expressed in the first byte of command:
1470    //
1471    // 0x00: Type 0: padding bytes start
1472    // 0x23: Type 1: command with DSP resource.
1473    // 0x46: Type 2: command with multiple quadlet coefficients
1474    // 0x49: Type 3: command with multiple byte coefficients
1475    // 0x62: Type 4: command for draining previous commands in frame
1476    // 0x65: Type 5: end of command if appears
1477    // 0x66: Type 6: command with single quadlet coefficient.
1478    // 0x69: Type 7: command with single byte coefficient.
1479    //
1480    // The layout of each type of command which has own content is described below:
1481    //
1482    // Type 1 command:
1483    //
1484    // command[0]: 0x23
1485    // command[1..5]: current usage as quadlet data aligned to big-endianness
1486    // command[5]: 0x11: identifier
1487    //
1488    // Type 2 command:
1489    //
1490    // command[0]: 0x46
1491    // command[1]: the number of coefficients
1492    // command[2..6]: identifier
1493    // command[6..]: the list of coefficients aligned to big-endianness
1494    //
1495    // Type 3 command:
1496    //
1497    // command[0]: 0x49
1498    // command[1]: the number of coefficients
1499    // command[2..6]: identifier
1500    // command[6..]: the list of coefficients
1501    //
1502    // Type 6 command:
1503    //
1504    // command[0]: 0x66
1505    // command[1..5]: identifier
1506    // command[5..9]: quadlet coefficient aligned to big-endianness
1507    //
1508    // Type 7 command:
1509    //
1510    // command[0]: 0x69
1511    // command[1]: byte coefficient
1512    // command[2..6]: identifier
1513    //
1514    // The last field of identifier expresses the target of command at first level:
1515    //
1516    // 0x00: monitor
1517    // 0x01: input
1518    // 0x02: mixer
1519    // 0x03: output
1520    // 0x04: reverb
1521    //
1522    // The rest fields of identifier has unique purpose depending on the first level. For example,
1523    // in input command, the identifier has below fields:
1524    //
1525    // identifier[0]: channel number
1526    // identifier[1]: third level; e.g. 0x01 the type of filter for low frequency filter.
1527    // identifier[2]: second level; e.g. 0x03 for low frequency filter.
1528    // identifier[3]: 0x01: first level
1529    //
1530    pub fn parse(raw: &[u8], cmds: &mut Vec<DspCmd>) -> usize {
1531        match raw[0] {
1532            CMD_RESOURCE => {
1533                let r = &raw[..CMD_RESOURCE_LENGTH];
1534                let cmd = DspCmd::Resource(ResourceCmd::parse(r));
1535                cmds.push(cmd);
1536
1537                CMD_RESOURCE_LENGTH
1538            }
1539            CMD_BYTE_MULTIPLE => {
1540                let count = raw[1] as usize;
1541                let length = 6 + count;
1542
1543                let mut identifier = [0; 4];
1544                identifier.copy_from_slice(&raw[2..6]);
1545                let first_level = identifier[3];
1546
1547                if first_level <= 0x04 {
1548                    (0..count).for_each(|i| {
1549                        identifier[0] = i as u8;
1550                        let vals = &raw[(6 + i)..(6 + i + 1)];
1551                        let cmd = match first_level {
1552                            0x00 => DspCmd::Monitor(MonitorCmd::parse(&identifier, vals)),
1553                            0x01 => DspCmd::Input(InputCmd::parse(&identifier, vals)),
1554                            0x02 => DspCmd::Mixer(MixerCmd::parse(&identifier, vals)),
1555                            0x03 => DspCmd::Output(OutputCmd::parse(&identifier, vals)),
1556                            0x04 => DspCmd::Reverb(ReverbCmd::parse(&identifier, vals)),
1557                            _ => unreachable!(),
1558                        };
1559                        cmds.push(cmd);
1560                    });
1561                } else {
1562                    let cmd = DspCmd::Reserved(raw[..length].to_vec());
1563                    cmds.push(cmd);
1564                }
1565
1566                length
1567            }
1568            CMD_QUADLET_MULTIPLE => {
1569                let count = raw[1] as usize;
1570                let length = 6 + count * 4;
1571
1572                let mut identifier = [0; 4];
1573                identifier.copy_from_slice(&raw[2..6]);
1574                let first_level = identifier[3];
1575
1576                if first_level <= 0x04 {
1577                    (0..count).for_each(|i| {
1578                        identifier[0] = i as u8;
1579                        let vals = &raw[(6 + i * 4)..(6 + i * 4 + 4)];
1580                        let cmd = match first_level {
1581                            0x00 => DspCmd::Monitor(MonitorCmd::parse(&identifier, vals)),
1582                            0x01 => DspCmd::Input(InputCmd::parse(&identifier, vals)),
1583                            0x02 => DspCmd::Mixer(MixerCmd::parse(&identifier, vals)),
1584                            0x03 => DspCmd::Output(OutputCmd::parse(&identifier, vals)),
1585                            0x04 => DspCmd::Reverb(ReverbCmd::parse(&identifier, vals)),
1586                            _ => unreachable!(),
1587                        };
1588                        cmds.push(cmd);
1589                    });
1590                } else {
1591                    let cmd = DspCmd::Reserved(raw[..length].to_vec());
1592                    cmds.push(cmd);
1593                }
1594
1595                6 + count * 4
1596            }
1597            CMD_DRAIN => 1,
1598            CMD_END => raw.len(),
1599            CMD_BYTE_SINGLE => {
1600                let identifier = &raw[2..6];
1601                let vals = &raw[1..2];
1602
1603                let first_level = identifier[3];
1604                let r = &raw[..CMD_BYTE_SINGLE_LENGTH];
1605
1606                let cmd = match first_level {
1607                    0x00 => DspCmd::Monitor(MonitorCmd::parse(identifier, vals)),
1608                    0x01 => DspCmd::Input(InputCmd::parse(identifier, vals)),
1609                    0x02 => DspCmd::Mixer(MixerCmd::parse(identifier, vals)),
1610                    0x03 => DspCmd::Output(OutputCmd::parse(identifier, vals)),
1611                    0x04 => DspCmd::Reverb(ReverbCmd::parse(identifier, vals)),
1612                    _ => DspCmd::Reserved(r.to_vec()),
1613                };
1614                cmds.push(cmd);
1615
1616                CMD_BYTE_SINGLE_LENGTH
1617            }
1618            CMD_QUADLET_SINGLE => {
1619                let identifier = &raw[1..5];
1620                let vals = &raw[5..9];
1621
1622                let first_level = identifier[3];
1623                let r = &raw[..CMD_QUADLET_SINGLE_LENGTH];
1624
1625                let cmd = match first_level {
1626                    0x00 => DspCmd::Monitor(MonitorCmd::parse(identifier, vals)),
1627                    0x01 => DspCmd::Input(InputCmd::parse(identifier, vals)),
1628                    0x02 => DspCmd::Mixer(MixerCmd::parse(identifier, vals)),
1629                    0x03 => DspCmd::Output(OutputCmd::parse(identifier, vals)),
1630                    0x04 => DspCmd::Reverb(ReverbCmd::parse(identifier, vals)),
1631                    _ => DspCmd::Reserved(r.to_vec()),
1632                };
1633                cmds.push(cmd);
1634
1635                CMD_QUADLET_SINGLE_LENGTH
1636            }
1637            _ => 0,
1638        }
1639    }
1640
1641    pub fn build(&self, raw: &mut Vec<u8>) {
1642        match self {
1643            DspCmd::Monitor(cmd) => cmd.build(raw),
1644            DspCmd::Input(cmd) => cmd.build(raw),
1645            DspCmd::Mixer(cmd) => cmd.build(raw),
1646            DspCmd::Output(cmd) => cmd.build(raw),
1647            DspCmd::Reverb(cmd) => cmd.build(raw),
1648            DspCmd::Resource(cmd) => cmd.build(raw),
1649            DspCmd::Reserved(data) => raw.extend_from_slice(data),
1650        }
1651    }
1652}
1653
1654fn append_u8<T>(
1655    raw: &mut Vec<u8>,
1656    first_level: u8,
1657    second_level: u8,
1658    third_level: u8,
1659    ch: usize,
1660    val: T,
1661) where
1662    u8: From<T>,
1663{
1664    raw.push(CMD_BYTE_SINGLE);
1665    raw.push(u8::from(val));
1666    raw.push(ch as u8);
1667    raw.push(third_level);
1668    raw.push(second_level);
1669    raw.push(first_level);
1670}
1671
1672fn append_i32(
1673    raw: &mut Vec<u8>,
1674    first_level: u8,
1675    second_level: u8,
1676    third_level: u8,
1677    ch: usize,
1678    val: i32,
1679) {
1680    append_f32(raw, first_level, second_level, third_level, ch, val as f32)
1681}
1682
1683fn append_f32(
1684    raw: &mut Vec<u8>,
1685    first_level: u8,
1686    second_level: u8,
1687    third_level: u8,
1688    ch: usize,
1689    val: f32,
1690) {
1691    raw.push(CMD_QUADLET_SINGLE);
1692    raw.push(ch as u8);
1693    raw.push(third_level);
1694    raw.push(second_level);
1695    raw.push(first_level);
1696    raw.extend_from_slice(&val.to_le_bytes());
1697}
1698
1699fn append_u32(
1700    raw: &mut Vec<u8>,
1701    first_level: u8,
1702    second_level: u8,
1703    third_level: u8,
1704    ch: usize,
1705    val: u32,
1706) {
1707    append_f32(raw, first_level, second_level, third_level, ch, val as f32)
1708}
1709
1710fn append_resource(raw: &mut Vec<u8>, usage: f32, flag: u8) {
1711    raw.push(CMD_RESOURCE);
1712    raw.extend_from_slice(&usage.to_le_bytes());
1713    raw.push(flag);
1714}
1715
1716// MEMO: The transaction frame can be truncated according to maximum length of frame (248 bytes).
1717// When truncated, the rest of frame is delivered by subsequent transaction.
1718//
1719// The sequence number is independent of the sequence number in message from the peer.
1720//
1721fn send_message(
1722    req: &mut FwReq,
1723    node: &mut FwNode,
1724    tag: u8,
1725    sequence_number: &mut u8,
1726    mut msg: &[u8],
1727    timeout_ms: u32,
1728) -> Result<(), Error> {
1729    while msg.len() > 0 {
1730        let length = std::cmp::min(msg.len(), MAXIMUM_DSP_FRAME_SIZE - 2);
1731        let mut frame = Vec::with_capacity(2 + length);
1732        frame.push(tag);
1733        frame.push(*sequence_number);
1734        frame.extend_from_slice(&msg[..length]);
1735
1736        // The length of frame should be aligned to quadlet unit. If it's not, the unit becomes
1737        // not to transfer any messages voluntarily.
1738        while frame.len() % 4 > 0 {
1739            frame.push(0x00);
1740        }
1741
1742        req.transaction_sync(
1743            node,
1744            FwTcode::WriteBlockRequest,
1745            DSP_CMD_OFFSET,
1746            frame.len(),
1747            &mut frame,
1748            timeout_ms,
1749        )?;
1750
1751        *sequence_number += 1;
1752        *sequence_number %= 0xff;
1753
1754        msg = &msg[length..];
1755    }
1756
1757    Ok(())
1758}
1759
1760/// The trait for operation of command DSP.
1761pub trait CommandDspOperation {
1762    fn send_commands(
1763        req: &mut FwReq,
1764        node: &mut FwNode,
1765        sequence_number: &mut u8,
1766        cmds: &[DspCmd],
1767        timeout_ms: u32,
1768    ) -> Result<(), Error> {
1769        let mut frame = Vec::new();
1770        cmds.iter().for_each(|cmd| cmd.build(&mut frame));
1771        send_message(req, node, 0x02, sequence_number, &mut frame, timeout_ms)
1772    }
1773
1774    fn register_message_destination_address(
1775        resp: &mut FwResp,
1776        req: &mut FwReq,
1777        node: &mut FwNode,
1778        timeout_ms: u32,
1779    ) -> Result<(), Error> {
1780        if !resp.is_reserved() {
1781            resp.reserve_within_region(
1782                node,
1783                MSG_DST_OFFSET_BEGIN,
1784                MSG_DST_OFFSET_END,
1785                8 + MAXIMUM_DSP_FRAME_SIZE as u32,
1786            )?;
1787        }
1788
1789        let local_node_id = node.local_node_id() as u64;
1790        let addr = (local_node_id << 48) | resp.offset();
1791
1792        let high = (addr >> 32) as u32;
1793        write_quad(req, node, DSP_MSG_DST_HIGH_OFFSET, high, timeout_ms)?;
1794
1795        let low = (addr & 0xffffffff) as u32;
1796        write_quad(req, node, DSP_MSG_DST_LOW_OFFSET, low, timeout_ms)?;
1797
1798        Ok(())
1799    }
1800
1801    fn begin_messaging(
1802        req: &mut FwReq,
1803        node: &mut FwNode,
1804        sequence_number: &mut u8,
1805        timeout_ms: u32,
1806    ) -> Result<(), Error> {
1807        let frame = [0x00, 0x00];
1808        send_message(req, node, 0x01, sequence_number, &frame, timeout_ms)?;
1809
1810        let frame = [0x00, 0x00];
1811        send_message(req, node, 0x02, sequence_number, &frame, timeout_ms)?;
1812
1813        Ok(())
1814    }
1815
1816    fn cancel_messaging(
1817        req: &mut FwReq,
1818        node: &mut FwNode,
1819        sequence_number: &mut u8,
1820        timeout_ms: u32,
1821    ) -> Result<(), Error> {
1822        let frame = [0x00, 0x00];
1823        send_message(req, node, 0x00, sequence_number, &frame, timeout_ms)
1824    }
1825
1826    fn release_message_destination_address(
1827        resp: &mut FwResp,
1828        req: &mut FwReq,
1829        node: &mut FwNode,
1830        timeout_ms: u32,
1831    ) -> Result<(), Error> {
1832        write_quad(req, node, DSP_MSG_DST_HIGH_OFFSET, 0, timeout_ms)?;
1833        write_quad(req, node, DSP_MSG_DST_LOW_OFFSET, 0, timeout_ms)?;
1834
1835        if resp.is_reserved() {
1836            resp.release();
1837        }
1838
1839        Ok(())
1840    }
1841}
1842
1843/// State of message parser.
1844#[derive(Debug)]
1845pub struct CommandDspMessageHandler {
1846    state: ParserState,
1847    cache: Vec<u8>,
1848    seq_num: u8,
1849}
1850
1851#[derive(Debug, Eq, PartialEq)]
1852enum ParserState {
1853    Initialized,
1854    Prepared,
1855    InTruncatedMessage,
1856}
1857
1858impl Default for CommandDspMessageHandler {
1859    fn default() -> Self {
1860        Self {
1861            state: ParserState::Initialized,
1862            cache: Vec::with_capacity(MAXIMUM_DSP_FRAME_SIZE + 6),
1863            seq_num: 0,
1864        }
1865    }
1866}
1867
1868fn remove_padding(cache: &mut Vec<u8>) {
1869    let mut buf = &cache[..];
1870    let mut count = 0;
1871
1872    while buf.len() > 4 {
1873        let length = match buf[0] {
1874            CMD_RESOURCE => CMD_RESOURCE_LENGTH,
1875            CMD_QUADLET_MULTIPLE => 6 + 4 * buf[1] as usize,
1876            CMD_BYTE_MULTIPLE => 6 + buf[1] as usize,
1877            CMD_DRAIN => 1,
1878            CMD_END => 0,
1879            CMD_QUADLET_SINGLE => CMD_QUADLET_SINGLE_LENGTH,
1880            CMD_BYTE_SINGLE => CMD_BYTE_SINGLE_LENGTH,
1881            _ => 0,
1882        };
1883        if length == 0 {
1884            break;
1885        }
1886
1887        count += length;
1888        buf = &buf[length..];
1889    }
1890
1891    let _ = cache.drain(count..);
1892}
1893
1894fn increment_seq_num(seq_num: u8) -> u8 {
1895    if seq_num == u8::MAX {
1896        0
1897    } else {
1898        seq_num + 1
1899    }
1900}
1901
1902impl CommandDspMessageHandler {
1903    // MEMO: After initiating messaging function by sending command with 0x02 in its first byte, the
1904    // target device start transferring messages immediately. There are two types of messages:
1905    //
1906    // Type 1: active sensing message
1907    // Type 2: commands
1908    //
1909    // In both, the fransaction frame has two bytes prefixes which consists of:
1910    //
1911    // 0: 0x00/0x01/0x02. Unknown purpose.
1912    // 1: sequence number, incremented within 1 byte.
1913    //
1914    // When message is split to several transactions due to maximum length of frame (248 bytes),
1915    // Type 1 message is not delivered between subsequent transactions.
1916    //
1917    pub fn cache_dsp_messages(&mut self, frame: &[u8]) {
1918        let seq_num = frame[1];
1919
1920        if self.state == ParserState::Initialized {
1921            self.seq_num = seq_num;
1922            self.state = ParserState::Prepared;
1923        }
1924
1925        if self.seq_num == seq_num {
1926            self.seq_num = increment_seq_num(seq_num);
1927
1928            if self.state == ParserState::Prepared {
1929                // Check the type of first command in the message.
1930                if frame.len() > 4 && frame[2] != 0x00 {
1931                    self.cache.extend_from_slice(&frame[2..]);
1932
1933                    if frame.len() == MAXIMUM_DSP_FRAME_SIZE {
1934                        self.state = ParserState::InTruncatedMessage;
1935                    } else {
1936                        remove_padding(&mut self.cache);
1937                    }
1938                }
1939            } else if self.state == ParserState::InTruncatedMessage {
1940                self.cache.extend_from_slice(&frame[2..]);
1941
1942                if frame.len() < MAXIMUM_DSP_FRAME_SIZE {
1943                    remove_padding(&mut self.cache);
1944                    self.state = ParserState::Prepared;
1945                }
1946            }
1947        } else {
1948            self.cache.clear();
1949            self.state = ParserState::Prepared;
1950        }
1951    }
1952
1953    pub fn has_dsp_message(&self) -> bool {
1954        self.cache.len() > 0 && (self.state == ParserState::Prepared)
1955    }
1956
1957    pub fn decode_messages(&mut self) -> Vec<DspCmd> {
1958        let mut cmds = Vec::new();
1959
1960        while self.cache.len() > 0 {
1961            let consumed = DspCmd::parse(&self.cache, &mut cmds);
1962            if consumed == 0 {
1963                break;
1964            }
1965
1966            let _ = self.cache.drain(..consumed);
1967        }
1968
1969        cmds
1970    }
1971}
1972
1973/// The trait for parameter operations.
1974pub trait MotuCommandDspParametersOperation<T> {
1975    /// Build DSP commands for parameters.
1976    fn build_commands(params: &T) -> Vec<DspCmd>;
1977    /// Parse DSP command for parameters.
1978    fn parse_command(params: &mut T, command: &DspCmd) -> bool;
1979}
1980
1981/// The trait for DSP image operations.
1982pub trait MotuCommandDspImageOperation<T, U> {
1983    /// Parse image transferred in the series of isochronous packets.
1984    fn parse_image(params: &mut T, image: &U);
1985}
1986
1987/// The trait for operation to update parameters.
1988pub trait MotuCommandDspUpdatableParamsOperation<T> {
1989    /// Update the part of parameters.
1990    fn update_partially(
1991        req: &mut FwReq,
1992        node: &mut FwNode,
1993        sequence_number: &mut u8,
1994        params: &mut T,
1995        updates: T,
1996        timeout_ms: u32,
1997    ) -> Result<(), Error>;
1998}
1999
2000impl<O, T> MotuCommandDspUpdatableParamsOperation<T> for O
2001where
2002    O: CommandDspOperation + MotuCommandDspParametersOperation<T>,
2003{
2004    fn update_partially(
2005        req: &mut FwReq,
2006        node: &mut FwNode,
2007        sequence_number: &mut u8,
2008        params: &mut T,
2009        updates: T,
2010        timeout_ms: u32,
2011    ) -> Result<(), Error> {
2012        let mut new_cmds = O::build_commands(&updates);
2013        let old_cmds = O::build_commands(params);
2014        new_cmds.retain(|cmd| old_cmds.iter().find(|c| c.eq(&cmd)).is_none());
2015        Self::send_commands(req, node, sequence_number, &new_cmds, timeout_ms)
2016            .map(|_| *params = updates)
2017    }
2018}
2019
2020/// State of reverb function.
2021#[derive(Default, Debug, Copy, Clone, PartialEq)]
2022pub struct CommandDspReverbState {
2023    /// Whether to enable reverb effect.
2024    pub enable: bool,
2025    /// The split point to prevent feedback loop.
2026    pub split_point: SplitPoint,
2027    /// The time before first reflection.
2028    pub pre_delay: u32,
2029    /// The frequency of low pass in shelf filter.
2030    pub shelf_filter_freq: u32,
2031    /// The attenuation of low pass in shelf filter.
2032    pub shelf_filter_attenuation: i32,
2033    /// The time length of decay.
2034    pub decay_time: u32,
2035    /// The percentages against decay at low/middle/high frequencies.
2036    pub freq_time: [u32; 3],
2037    /// The point to cross over between low and middle, between middle and high,
2038    pub freq_crossover: [u32; 2],
2039    /// The width of stereo channels.
2040    pub width: f32,
2041    /// The mode of reflection.
2042    pub reflection_mode: RoomShape,
2043    /// The size of reflection.
2044    pub reflection_size: u32,
2045    /// The level of reflection.
2046    pub reflection_level: f32,
2047}
2048
2049/// The specification of reverb.
2050pub trait MotuCommandDspReverbSpecification {
2051    /// The minimum value of decay time.
2052    const DECAY_TIME_MIN: u32 = 100;
2053    /// The maximum value of decay time.
2054    const DECAY_TIME_MAX: u32 = 60000;
2055    /// The step value of decay time.
2056    const DECAY_TIME_STEP: u32 = 1;
2057
2058    /// The minimum value of pre decay time.
2059    const PRE_DELAY_MIN: u32 = 0;
2060    /// The maximum value of pre decay time.
2061    const PRE_DELAY_MAX: u32 = 100;
2062    /// The step value of pre decay time.
2063    const PRE_DELAY_STEP: u32 = 1;
2064
2065    /// The minimum value of shelf filter.
2066    const SHELF_FILTER_FREQ_MIN: u32 = 1000;
2067    /// The maximum value of shelf filter.
2068    const SHELF_FILTER_FREQ_MAX: u32 = 20000;
2069    /// The step value of shelf filter.
2070    const SHELF_FILTER_FREQ_STEP: u32 = 1;
2071
2072    /// The minimum value of shelf filter attenuation.
2073    const SHELF_FILTER_ATTR_MIN: i32 = -40;
2074    /// The maximum value of shelf filter attenuation.
2075    const SHELF_FILTER_ATTR_MAX: i32 = 0;
2076    /// The step value of shelf filter attenuation.
2077    const SHELF_FILTER_ATTR_STEP: i32 = 0;
2078
2079    /// The number of frequency times.
2080    const FREQ_TIME_COUNT: usize = 3;
2081    /// The minimum value of frequency time.
2082    const FREQ_TIME_MIN: u32 = 0;
2083    /// The maximum value of frequency time.
2084    const FREQ_TIME_MAX: u32 = 100;
2085    /// The step value of frequency time.
2086    const FREQ_TIME_STEP: u32 = 1;
2087
2088    /// The number of frequency crossovers.
2089    const FREQ_CROSSOVER_COUNT: usize = 2;
2090    /// The minimum value of frequency crossover.
2091    const FREQ_CROSSOVER_MIN: u32 = 100;
2092    /// The maximum value of frequency crossover.
2093    const FREQ_CROSSOVER_MAX: u32 = 20000;
2094    /// The step value of frequency crossover.
2095    const FREQ_CROSSOVER_STEP: u32 = 1;
2096
2097    /// The minimum value of width.
2098    const WIDTH_MIN: f32 = -1.0;
2099    /// The maximum value of width.
2100    const WIDTH_MAX: f32 = 1.0;
2101
2102    /// The minimum value of reflection size.
2103    const REFLECTION_SIZE_MIN: u32 = 50;
2104    /// The maximum value of reflection size.
2105    const REFLECTION_SIZE_MAX: u32 = 400;
2106    /// The step value of reflection size.
2107    const REFLECTION_SIZE_STEP: u32 = 1;
2108
2109    /// The minimum value of reflection level.
2110    const REFLECTION_LEVEL_MIN: f32 = 0.0;
2111    /// The maximum value of reflection level.
2112    const REFLECTION_LEVEL_MAX: f32 = 1.0;
2113}
2114
2115impl<O> MotuCommandDspParametersOperation<CommandDspReverbState> for O
2116where
2117    O: MotuCommandDspReverbSpecification,
2118{
2119    fn build_commands(params: &CommandDspReverbState) -> Vec<DspCmd> {
2120        vec![
2121            DspCmd::Reverb(ReverbCmd::Enable(params.enable)),
2122            DspCmd::Reverb(ReverbCmd::Split(params.split_point)),
2123            DspCmd::Reverb(ReverbCmd::PreDelay(params.pre_delay)),
2124            DspCmd::Reverb(ReverbCmd::ShelfFilterFreq(params.shelf_filter_freq)),
2125            DspCmd::Reverb(ReverbCmd::ShelfFilterAttenuation(
2126                params.shelf_filter_attenuation,
2127            )),
2128            DspCmd::Reverb(ReverbCmd::DecayTime(params.decay_time)),
2129            DspCmd::Reverb(ReverbCmd::LowFreqTime(params.freq_time[0])),
2130            DspCmd::Reverb(ReverbCmd::MiddleFreqTime(params.freq_time[1])),
2131            DspCmd::Reverb(ReverbCmd::HighFreqTime(params.freq_time[2])),
2132            DspCmd::Reverb(ReverbCmd::LowFreqCrossover(params.freq_crossover[0])),
2133            DspCmd::Reverb(ReverbCmd::HighFreqCrossover(params.freq_crossover[1])),
2134            DspCmd::Reverb(ReverbCmd::Width(params.width)),
2135            DspCmd::Reverb(ReverbCmd::ReflectionMode(params.reflection_mode)),
2136            DspCmd::Reverb(ReverbCmd::ReflectionSize(params.reflection_size)),
2137            DspCmd::Reverb(ReverbCmd::ReflectionLevel(params.reflection_level)),
2138        ]
2139    }
2140
2141    fn parse_command(params: &mut CommandDspReverbState, command: &DspCmd) -> bool {
2142        if let DspCmd::Reverb(cmd) = command {
2143            match cmd {
2144                ReverbCmd::Enable(val) => params.enable = *val,
2145                ReverbCmd::Split(val) => params.split_point = *val,
2146                ReverbCmd::PreDelay(val) => params.pre_delay = *val,
2147                ReverbCmd::ShelfFilterFreq(val) => params.shelf_filter_freq = *val,
2148                ReverbCmd::ShelfFilterAttenuation(val) => params.shelf_filter_attenuation = *val,
2149                ReverbCmd::DecayTime(val) => params.decay_time = *val,
2150                ReverbCmd::LowFreqTime(val) => params.freq_time[0] = *val,
2151                ReverbCmd::MiddleFreqTime(val) => params.freq_time[1] = *val,
2152                ReverbCmd::HighFreqTime(val) => params.freq_time[2] = *val,
2153                ReverbCmd::LowFreqCrossover(val) => params.freq_crossover[0] = *val,
2154                ReverbCmd::HighFreqCrossover(val) => params.freq_crossover[1] = *val,
2155                ReverbCmd::Width(val) => params.width = *val,
2156                ReverbCmd::ReflectionMode(val) => params.reflection_mode = *val,
2157                ReverbCmd::ReflectionSize(val) => params.reflection_size = *val,
2158                ReverbCmd::ReflectionLevel(val) => params.reflection_level = *val,
2159                _ => (),
2160            };
2161            true
2162        } else {
2163            false
2164        }
2165    }
2166}
2167
2168/// State of monitor function.
2169#[derive(Default, Debug, Copy, Clone, PartialEq)]
2170pub struct CommandDspMonitorState {
2171    /// The volume adjusted by main (master) knob. -inf (mute), -80.0 dB to 0.0 dB.
2172    pub main_volume: f32,
2173    /// Whether to enable talkback or not.
2174    pub talkback_enable: bool,
2175    /// Whether to listenback or not.
2176    pub listenback_enable: bool,
2177    /// The volume of talkback.
2178    pub talkback_volume: f32,
2179    /// The volume of listenback.
2180    pub listenback_volume: f32,
2181    /// Input or output to focus on.
2182    pub focus: FocusTarget,
2183    /// The target to focus on.
2184    pub assign_target: TargetPort,
2185}
2186
2187/// The trait for specification of monitor.
2188pub trait MotuCommandDspMonitorSpecification {
2189    /// The targets of mixer return.
2190    const RETURN_ASSIGN_TARGETS: &'static [TargetPort];
2191
2192    /// The minimum value of volume for monitor output.
2193    const VOLUME_MIN: f32 = 0.0;
2194    /// The maximum value of volume for monitor output.
2195    const VOLUME_MAX: f32 = 1.0;
2196}
2197
2198impl<O> MotuCommandDspParametersOperation<CommandDspMonitorState> for O
2199where
2200    O: MotuCommandDspMonitorSpecification,
2201{
2202    fn build_commands(params: &CommandDspMonitorState) -> Vec<DspCmd> {
2203        let pos = Self::RETURN_ASSIGN_TARGETS
2204            .iter()
2205            .position(|p| params.assign_target.eq(p))
2206            .unwrap_or_default();
2207
2208        vec![
2209            DspCmd::Monitor(MonitorCmd::Volume(params.main_volume)),
2210            DspCmd::Monitor(MonitorCmd::TalkbackEnable(params.talkback_enable)),
2211            DspCmd::Monitor(MonitorCmd::ListenbackEnable(params.listenback_enable)),
2212            DspCmd::Monitor(MonitorCmd::TalkbackVolume(params.talkback_volume)),
2213            DspCmd::Monitor(MonitorCmd::ListenbackVolume(params.listenback_volume)),
2214            DspCmd::Monitor(MonitorCmd::Focus(params.focus)),
2215            DspCmd::Monitor(MonitorCmd::ReturnAssign(pos)),
2216        ]
2217    }
2218
2219    fn parse_command(params: &mut CommandDspMonitorState, command: &DspCmd) -> bool {
2220        if let DspCmd::Monitor(cmd) = command {
2221            match cmd {
2222                MonitorCmd::Volume(val) => params.main_volume = *val,
2223                MonitorCmd::TalkbackEnable(val) => params.talkback_enable = *val,
2224                MonitorCmd::ListenbackEnable(val) => params.listenback_enable = *val,
2225                MonitorCmd::TalkbackVolume(val) => params.talkback_volume = *val,
2226                MonitorCmd::ListenbackVolume(val) => params.listenback_volume = *val,
2227                MonitorCmd::Focus(val) => params.focus = *val,
2228                MonitorCmd::ReturnAssign(val) => {
2229                    params.assign_target = Self::RETURN_ASSIGN_TARGETS
2230                        .iter()
2231                        .nth(*val as usize)
2232                        .map(|&p| p)
2233                        .unwrap_or_default();
2234                }
2235                _ => (),
2236            };
2237            true
2238        } else {
2239            false
2240        }
2241    }
2242}
2243
2244/// State of entry of mixer function.
2245#[derive(Default, Debug, Clone, PartialEq)]
2246pub struct CommandDspMixerSourceState {
2247    /// Whether to mute the source of mixer.
2248    pub mute: Vec<bool>,
2249    /// Whether to mute the other sources of mixer.
2250    pub solo: Vec<bool>,
2251    /// The gain for source of mixer.
2252    pub gain: Vec<f32>,
2253    /// The left and right balance for source of mixer.
2254    pub pan: Vec<f32>,
2255    /// The mode of stereo pair.
2256    pub stereo_mode: Vec<SourceStereoPairMode>,
2257    /// The left and right balance for source of mixer when paired.
2258    pub stereo_balance: Vec<f32>,
2259    /// The left and right width for source of mixer when paired.
2260    pub stereo_width: Vec<f32>,
2261}
2262
2263const MIXER_COUNT: usize = 8;
2264
2265/// State of mixer function.
2266#[derive(Default, Debug, Clone, PartialEq)]
2267pub struct CommandDspMixerState {
2268    /// The destination of mixer outputs.
2269    pub output_assign: [TargetPort; MIXER_COUNT],
2270    /// Whether to mute mixer outputs.
2271    pub output_mute: [bool; MIXER_COUNT],
2272    /// The volume of mixer outputs.
2273    pub output_volume: [f32; MIXER_COUNT],
2274    /// The volume to send to reverb effect.
2275    pub reverb_send: [f32; MIXER_COUNT],
2276    /// The gain to return from reverb effect.
2277    pub reverb_return: [f32; MIXER_COUNT],
2278    /// The parameters of mixer sources.
2279    pub source: [CommandDspMixerSourceState; MIXER_COUNT],
2280}
2281
2282/// The trait for specification of mixer.
2283pub trait MotuCommandDspMixerSpecification {
2284    /// The sources of mixer inputs.
2285    const SOURCE_PORTS: &'static [TargetPort];
2286    /// The destination of mixer outputs.
2287    const OUTPUT_PORTS: &'static [TargetPort];
2288
2289    /// The number of mixers.
2290    const MIXER_COUNT: usize = MIXER_COUNT;
2291
2292    /// The minimum value of volume for mixer output.
2293    const OUTPUT_VOLUME_MIN: f32 = 0.0;
2294    /// The maximum value of volume for mixer output.
2295    const OUTPUT_VOLUME_MAX: f32 = 1.0;
2296
2297    /// The minimum value of gain for mixer source.
2298    const SOURCE_GAIN_MIN: f32 = 0.0;
2299    /// The maximum value of gain for mixer source.
2300    const SOURCE_GAIN_MAX: f32 = 1.0;
2301
2302    /// The minimum value of left and right balance for mixer source.
2303    const SOURCE_PAN_MIN: f32 = -1.0;
2304    /// The maximum value of left and right balance for mixer source.
2305    const SOURCE_PAN_MAX: f32 = 1.0;
2306
2307    fn create_mixer_state() -> CommandDspMixerState {
2308        let mut state = CommandDspMixerState::default();
2309
2310        state.source.iter_mut().for_each(|src| {
2311            src.mute = vec![Default::default(); Self::SOURCE_PORTS.len()];
2312            src.solo = vec![Default::default(); Self::SOURCE_PORTS.len()];
2313            src.gain = vec![Default::default(); Self::SOURCE_PORTS.len()];
2314            src.pan = vec![Default::default(); Self::SOURCE_PORTS.len()];
2315            src.stereo_mode = vec![Default::default(); Self::SOURCE_PORTS.len()];
2316            src.stereo_balance = vec![Default::default(); Self::SOURCE_PORTS.len()];
2317            src.stereo_width = vec![Default::default(); Self::SOURCE_PORTS.len()];
2318        });
2319
2320        state
2321    }
2322}
2323
2324impl<O> MotuCommandDspParametersOperation<CommandDspMixerState> for O
2325where
2326    O: MotuCommandDspMixerSpecification,
2327{
2328    fn build_commands(params: &CommandDspMixerState) -> Vec<DspCmd> {
2329        let mut cmds = Vec::new();
2330
2331        (0..MIXER_COUNT).for_each(|mixer| {
2332            let pos = Self::OUTPUT_PORTS
2333                .iter()
2334                .position(|p| params.output_assign[mixer].eq(p))
2335                .unwrap_or_default();
2336            cmds.push(DspCmd::Mixer(MixerCmd::OutputAssign(mixer, pos)));
2337            cmds.push(DspCmd::Mixer(MixerCmd::OutputMute(
2338                mixer,
2339                params.output_mute[mixer],
2340            )));
2341            cmds.push(DspCmd::Mixer(MixerCmd::OutputVolume(
2342                mixer,
2343                params.output_volume[mixer],
2344            )));
2345            cmds.push(DspCmd::Mixer(MixerCmd::ReverbSend(
2346                mixer,
2347                params.reverb_send[mixer],
2348            )));
2349            cmds.push(DspCmd::Mixer(MixerCmd::ReverbReturn(
2350                mixer,
2351                params.reverb_return[mixer],
2352            )));
2353
2354            let src = &params.source[mixer];
2355            (0..Self::SOURCE_PORTS.len()).for_each(|ch| {
2356                cmds.push(DspCmd::Mixer(MixerCmd::SourceMute(mixer, ch, src.mute[ch])));
2357                cmds.push(DspCmd::Mixer(MixerCmd::SourceSolo(mixer, ch, src.solo[ch])));
2358                cmds.push(DspCmd::Mixer(MixerCmd::SourceGain(mixer, ch, src.gain[ch])));
2359                cmds.push(DspCmd::Mixer(MixerCmd::SourceMonauralLrBalance(
2360                    mixer,
2361                    ch,
2362                    src.pan[ch],
2363                )));
2364                cmds.push(DspCmd::Mixer(MixerCmd::SourceStereoMode(
2365                    mixer,
2366                    ch,
2367                    src.stereo_mode[ch],
2368                )));
2369                cmds.push(DspCmd::Mixer(MixerCmd::SourceStereoLrBalance(
2370                    mixer,
2371                    ch,
2372                    src.stereo_balance[ch],
2373                )));
2374                cmds.push(DspCmd::Mixer(MixerCmd::SourceStereoWidth(
2375                    mixer,
2376                    ch,
2377                    src.stereo_width[ch],
2378                )));
2379            });
2380        });
2381
2382        cmds
2383    }
2384
2385    fn parse_command(params: &mut CommandDspMixerState, command: &DspCmd) -> bool {
2386        if let DspCmd::Mixer(cmd) = command {
2387            match cmd {
2388                MixerCmd::OutputAssign(mixer, val) => {
2389                    params.output_assign[*mixer] = Self::OUTPUT_PORTS
2390                        .iter()
2391                        .nth(*val)
2392                        .map(|&p| p)
2393                        .unwrap_or_else(|| Self::OUTPUT_PORTS[0]);
2394                }
2395                MixerCmd::OutputMute(mixer, val) => params.output_mute[*mixer] = *val,
2396                MixerCmd::OutputVolume(mixer, val) => params.output_volume[*mixer] = *val,
2397                MixerCmd::ReverbSend(mixer, val) => params.reverb_send[*mixer] = *val,
2398                MixerCmd::ReverbReturn(mixer, val) => params.reverb_return[*mixer] = *val,
2399                MixerCmd::SourceMute(mixer, src, val) => params.source[*mixer].mute[*src] = *val,
2400                MixerCmd::SourceSolo(mixer, src, val) => params.source[*mixer].solo[*src] = *val,
2401                MixerCmd::SourceGain(mixer, src, val) => params.source[*mixer].gain[*src] = *val,
2402                MixerCmd::SourceMonauralLrBalance(mixer, src, val) => {
2403                    params.source[*mixer].pan[*src] = *val
2404                }
2405                MixerCmd::SourceStereoMode(mixer, src, val) => {
2406                    params.source[*mixer].stereo_mode[*src] = *val
2407                }
2408                MixerCmd::SourceStereoLrBalance(mixer, src, val) => {
2409                    params.source[*mixer].stereo_balance[*src] = *val
2410                }
2411                MixerCmd::SourceStereoWidth(mixer, src, val) => {
2412                    params.source[*mixer].stereo_width[*src] = *val
2413                }
2414                _ => (),
2415            };
2416            true
2417        } else {
2418            false
2419        }
2420    }
2421}
2422
2423/// State of equalizer.
2424#[derive(Default, Debug, Clone, PartialEq)]
2425pub struct CommandDspEqualizerState {
2426    /// Whether to enable whole equalizer.
2427    pub enable: Vec<bool>,
2428
2429    /// Whether to enable high pass filter.
2430    pub hpf_enable: Vec<bool>,
2431    /// The type of slope for high pass filter.
2432    pub hpf_slope: Vec<RollOffLevel>,
2433    /// The frequency of high pass filter.
2434    pub hpf_freq: Vec<u32>,
2435
2436    /// Whether to enable low pass filter.
2437    pub lpf_enable: Vec<bool>,
2438    /// The type of slope for loa pass filter.
2439    pub lpf_slope: Vec<RollOffLevel>,
2440    /// The frequency of low pass filter.
2441    pub lpf_freq: Vec<u32>,
2442
2443    /// Whether to enable equalizer at low frequency.
2444    pub lf_enable: Vec<bool>,
2445    /// The type of equalizer at low frequency.
2446    pub lf_type: Vec<FilterType5>,
2447    /// The center frequency of equalizer at low frequency.
2448    pub lf_freq: Vec<u32>,
2449    /// The gain of equalizer at low frequency.
2450    pub lf_gain: Vec<f32>,
2451    /// The width of equalizer at low frequency.
2452    pub lf_width: Vec<f32>,
2453
2454    /// Whether to enable equalizer at low-middle frequency
2455    pub lmf_enable: Vec<bool>,
2456    /// The type of equalizer at low-middle frequency
2457    pub lmf_type: Vec<FilterType4>,
2458    /// The center frequency of equalizer at low-middle frequency
2459    pub lmf_freq: Vec<u32>,
2460    /// The gain of equalizer at low-middle frequency
2461    pub lmf_gain: Vec<f32>,
2462    /// The width of equalizer at low-middle frequency
2463    pub lmf_width: Vec<f32>,
2464
2465    /// Whether to enable equalizer at middle frequency.
2466    pub mf_enable: Vec<bool>,
2467    /// The type of equalizer at middle frequency.
2468    pub mf_type: Vec<FilterType4>,
2469    /// The center frequency of equalizer at middle frequency.
2470    pub mf_freq: Vec<u32>,
2471    /// The gain of equalizer at middle frequency.
2472    pub mf_gain: Vec<f32>,
2473    /// The width of equalizer at middle frequency.
2474    pub mf_width: Vec<f32>,
2475
2476    /// Whether to enable equalizer at high-middle frequency.
2477    pub hmf_enable: Vec<bool>,
2478    /// The type of equalizer at high-middle frequency.
2479    pub hmf_type: Vec<FilterType4>,
2480    /// The center frequency of equalizer at high-middle frequency.
2481    pub hmf_freq: Vec<u32>,
2482    /// The gain of equalizer at high-middle frequency.
2483    pub hmf_gain: Vec<f32>,
2484    /// The width of equalizer at high-middle frequency.
2485    pub hmf_width: Vec<f32>,
2486
2487    /// Whether to enable equalizer at high frequency.
2488    pub hf_enable: Vec<bool>,
2489    /// The type of equalizer at high frequency.
2490    pub hf_type: Vec<FilterType5>,
2491    /// The center frequency of equalizer at high frequency.
2492    pub hf_freq: Vec<u32>,
2493    /// The gain of equalizer at high frequency.
2494    pub hf_gain: Vec<f32>,
2495    /// The width of equalizer at high frequency.
2496    pub hf_width: Vec<f32>,
2497}
2498
2499/// The trait for specification of equalizer effect.
2500pub trait MotuCommandDspEqualizerSpecification {
2501    /// The minimum value of frequency.
2502    const EQUALIZER_FREQ_MIN: u32 = 20;
2503    /// The maximum value of frequency.
2504    const EQUALIZER_FREQ_MAX: u32 = 20000;
2505    /// The step value of frequency.
2506    const EQUALIZER_FREQ_STEP: u32 = 1;
2507
2508    /// The minimum value of gain.
2509    const EQUALIZER_GAIN_MIN: f32 = -20.0;
2510    /// The maximum value of gain.
2511    const EQUALIZER_GAIN_MAX: f32 = 20.0;
2512
2513    /// The minimum value of width.
2514    const EQUALIZER_WIDTH_MIN: f32 = 0.01;
2515    /// The maximum value of width.
2516    const EQUALIZER_WIDTH_MAX: f32 = 3.0;
2517
2518    fn create_equalizer_parameters(
2519        state: &CommandDspEqualizerState,
2520        ch: usize,
2521    ) -> Vec<EqualizerParameter> {
2522        let mut params = Vec::new();
2523
2524        params.push(EqualizerParameter::Enable(state.enable[ch]));
2525
2526        params.push(EqualizerParameter::HpfEnable(state.hpf_enable[ch]));
2527        params.push(EqualizerParameter::HpfSlope(state.hpf_slope[ch]));
2528        params.push(EqualizerParameter::HpfFreq(state.hpf_freq[ch]));
2529
2530        params.push(EqualizerParameter::LpfEnable(state.lpf_enable[ch]));
2531        params.push(EqualizerParameter::LpfSlope(state.lpf_slope[ch]));
2532        params.push(EqualizerParameter::LpfFreq(state.lpf_freq[ch]));
2533
2534        params.push(EqualizerParameter::LfEnable(state.lf_enable[ch]));
2535        params.push(EqualizerParameter::LfType(state.lf_type[ch]));
2536        params.push(EqualizerParameter::LfFreq(state.lf_freq[ch]));
2537        params.push(EqualizerParameter::LfGain(state.lf_gain[ch]));
2538        params.push(EqualizerParameter::LfWidth(state.lf_width[ch]));
2539
2540        params.push(EqualizerParameter::LmfEnable(state.lmf_enable[ch]));
2541        params.push(EqualizerParameter::LmfType(state.lmf_type[ch]));
2542        params.push(EqualizerParameter::LmfFreq(state.lmf_freq[ch]));
2543        params.push(EqualizerParameter::LmfGain(state.lmf_gain[ch]));
2544        params.push(EqualizerParameter::LmfWidth(state.lmf_width[ch]));
2545
2546        params.push(EqualizerParameter::MfEnable(state.mf_enable[ch]));
2547        params.push(EqualizerParameter::MfType(state.mf_type[ch]));
2548        params.push(EqualizerParameter::MfFreq(state.mf_freq[ch]));
2549        params.push(EqualizerParameter::MfGain(state.mf_gain[ch]));
2550        params.push(EqualizerParameter::MfWidth(state.mf_width[ch]));
2551
2552        params.push(EqualizerParameter::HmfEnable(state.hmf_enable[ch]));
2553        params.push(EqualizerParameter::HmfType(state.hmf_type[ch]));
2554        params.push(EqualizerParameter::HmfFreq(state.hmf_freq[ch]));
2555        params.push(EqualizerParameter::HmfGain(state.hmf_gain[ch]));
2556        params.push(EqualizerParameter::HmfWidth(state.hmf_width[ch]));
2557
2558        params.push(EqualizerParameter::HfEnable(state.hf_enable[ch]));
2559        params.push(EqualizerParameter::HfType(state.hf_type[ch]));
2560        params.push(EqualizerParameter::HfFreq(state.hf_freq[ch]));
2561        params.push(EqualizerParameter::HfGain(state.hf_gain[ch]));
2562        params.push(EqualizerParameter::HfWidth(state.hf_width[ch]));
2563
2564        params
2565    }
2566
2567    fn parse_equalizer_parameter(
2568        state: &mut CommandDspEqualizerState,
2569        param: &EqualizerParameter,
2570        ch: usize,
2571    ) {
2572        match param {
2573            EqualizerParameter::Enable(val) => state.enable[ch] = *val,
2574
2575            EqualizerParameter::HpfEnable(val) => state.hpf_enable[ch] = *val,
2576            EqualizerParameter::HpfSlope(val) => state.hpf_slope[ch] = *val,
2577            EqualizerParameter::HpfFreq(val) => state.hpf_freq[ch] = *val,
2578
2579            EqualizerParameter::LpfEnable(val) => state.lpf_enable[ch] = *val,
2580            EqualizerParameter::LpfSlope(val) => state.lpf_slope[ch] = *val,
2581            EqualizerParameter::LpfFreq(val) => state.lpf_freq[ch] = *val,
2582
2583            EqualizerParameter::LfEnable(val) => state.lf_enable[ch] = *val,
2584            EqualizerParameter::LfType(val) => state.lf_type[ch] = *val,
2585            EqualizerParameter::LfFreq(val) => state.lf_freq[ch] = *val,
2586            EqualizerParameter::LfGain(val) => state.lf_gain[ch] = *val,
2587            EqualizerParameter::LfWidth(val) => state.lf_width[ch] = *val,
2588
2589            EqualizerParameter::LmfEnable(val) => state.lmf_enable[ch] = *val,
2590            EqualizerParameter::LmfType(val) => state.lmf_type[ch] = *val,
2591            EqualizerParameter::LmfFreq(val) => state.lmf_freq[ch] = *val,
2592            EqualizerParameter::LmfGain(val) => state.lmf_gain[ch] = *val,
2593            EqualizerParameter::LmfWidth(val) => state.lmf_width[ch] = *val,
2594
2595            EqualizerParameter::MfEnable(val) => state.mf_enable[ch] = *val,
2596            EqualizerParameter::MfType(val) => state.mf_type[ch] = *val,
2597            EqualizerParameter::MfFreq(val) => state.mf_freq[ch] = *val,
2598            EqualizerParameter::MfGain(val) => state.mf_gain[ch] = *val,
2599            EqualizerParameter::MfWidth(val) => state.mf_width[ch] = *val,
2600
2601            EqualizerParameter::HmfEnable(val) => state.hmf_enable[ch] = *val,
2602            EqualizerParameter::HmfType(val) => state.hmf_type[ch] = *val,
2603            EqualizerParameter::HmfFreq(val) => state.hmf_freq[ch] = *val,
2604            EqualizerParameter::HmfGain(val) => state.hmf_gain[ch] = *val,
2605            EqualizerParameter::HmfWidth(val) => state.hmf_width[ch] = *val,
2606
2607            EqualizerParameter::HfEnable(val) => state.hf_enable[ch] = *val,
2608            EqualizerParameter::HfType(val) => state.hf_type[ch] = *val,
2609            EqualizerParameter::HfFreq(val) => state.hf_freq[ch] = *val,
2610            EqualizerParameter::HfGain(val) => state.hf_gain[ch] = *val,
2611            EqualizerParameter::HfWidth(val) => state.hf_width[ch] = *val,
2612        }
2613    }
2614}
2615
2616/// State of dynamics.
2617#[derive(Default, Debug, Clone, PartialEq)]
2618pub struct CommandDspDynamicsState {
2619    /// whether to enable dynamics effects.
2620    pub enable: Vec<bool>,
2621
2622    /// Whether to enable compressors.
2623    pub comp_enable: Vec<bool>,
2624    /// The mode to detect level in compressors.
2625    pub comp_detect_mode: Vec<LevelDetectMode>,
2626    /// The threshold of compressors.
2627    pub comp_threshold: Vec<i32>,
2628    /// The ratio of compressors.
2629    pub comp_ratio: Vec<f32>,
2630    /// The attack of compressors.
2631    pub comp_attack: Vec<u32>,
2632    /// The release of compressors.
2633    pub comp_release: Vec<u32>,
2634    /// The gain of compressors.
2635    pub comp_gain: Vec<f32>,
2636
2637    /// Whether to enable levelers.
2638    pub leveler_enable: Vec<bool>,
2639    /// The mode of levelers.
2640    pub leveler_mode: Vec<LevelerMode>,
2641    /// The markup of levelers.
2642    pub leveler_makeup: Vec<u32>,
2643    /// The reduce of levelers.
2644    pub leveler_reduce: Vec<u32>,
2645}
2646
2647/// The trait for specification of dynamics effect.
2648pub trait MotuCommandDspDynamicsSpecification {
2649    /// The minimum value of threshold in compressor.
2650    const COMP_THRESHOLD_MIN: i32 = -48;
2651    /// The maximum value of threshold in compressor.
2652    const COMP_THRESHOLD_MAX: i32 = 0;
2653    /// The step value of threshold in compressor.
2654    const COMP_THRESHOLD_STEP: i32 = 1;
2655
2656    /// The minimum value of ration in compressor.
2657    const COMP_RATIO_MIN: f32 = 1.0;
2658    /// The maximum value of ration in compressor.
2659    const COMP_RATIO_MAX: f32 = 10.0;
2660
2661    /// The minimum value of attack in compressor.
2662    const COMP_ATTACK_MIN: i32 = 10;
2663    /// The maximum value of attack in compressor.
2664    const COMP_ATTACK_MAX: i32 = 100;
2665    /// The step value of attack in compressor.
2666    const COMP_ATTACK_STEP: i32 = 1;
2667
2668    /// The minimum value of release in compressor.
2669    const COMP_RELEASE_MIN: i32 = 10;
2670    /// The maximum value of release in compressor.
2671    const COMP_RELEASE_MAX: i32 = 100;
2672    /// The step value of release in compressor.
2673    const COMP_RELEASE_STEP: i32 = 1;
2674
2675    /// The minimum value of gain in compressor.
2676    const COMP_GAIN_MIN: f32 = -6.0;
2677    /// The maximum value of gain in compressor.
2678    const COMP_GAIN_MAX: f32 = 0.0;
2679
2680    /// The minimum value of percentge in leveler.
2681    const LEVELER_PERCENTAGE_MIN: u32 = 0;
2682    /// The maximum value of percentge in leveler.
2683    const LEVELER_PERCENTAGE_MAX: u32 = 100;
2684    /// The step value of percentge in leveler.
2685    const LEVELER_PERCENTAGE_STEP: u32 = 1;
2686
2687    fn create_dynamics_parameters(
2688        state: &CommandDspDynamicsState,
2689        ch: usize,
2690    ) -> Vec<DynamicsParameter> {
2691        let mut params = Vec::new();
2692
2693        params.push(DynamicsParameter::Enable(state.enable[ch]));
2694
2695        params.push(DynamicsParameter::CompEnable(state.comp_enable[ch]));
2696        params.push(DynamicsParameter::CompDetectMode(
2697            state.comp_detect_mode[ch],
2698        ));
2699        params.push(DynamicsParameter::CompThreshold(state.comp_threshold[ch]));
2700        params.push(DynamicsParameter::CompRatio(state.comp_ratio[ch]));
2701        params.push(DynamicsParameter::CompAttack(state.comp_attack[ch]));
2702        params.push(DynamicsParameter::CompRelease(state.comp_release[ch]));
2703        params.push(DynamicsParameter::CompGain(state.comp_gain[ch]));
2704
2705        params.push(DynamicsParameter::LevelerEnable(state.leveler_enable[ch]));
2706        params.push(DynamicsParameter::LevelerMode(state.leveler_mode[ch]));
2707        params.push(DynamicsParameter::LevelerMakeup(state.leveler_makeup[ch]));
2708        params.push(DynamicsParameter::LevelerReduce(state.leveler_reduce[ch]));
2709
2710        params
2711    }
2712
2713    fn parse_dynamics_parameter(
2714        state: &mut CommandDspDynamicsState,
2715        param: &DynamicsParameter,
2716        ch: usize,
2717    ) {
2718        match param {
2719            DynamicsParameter::Enable(val) => state.enable[ch] = *val,
2720
2721            DynamicsParameter::CompEnable(val) => state.comp_enable[ch] = *val,
2722            DynamicsParameter::CompDetectMode(val) => state.comp_detect_mode[ch] = *val,
2723            DynamicsParameter::CompThreshold(val) => state.comp_threshold[ch] = *val,
2724            DynamicsParameter::CompRatio(val) => state.comp_ratio[ch] = *val,
2725            DynamicsParameter::CompAttack(val) => state.comp_attack[ch] = *val,
2726            DynamicsParameter::CompRelease(val) => state.comp_release[ch] = *val,
2727            DynamicsParameter::CompGain(val) => state.comp_gain[ch] = *val,
2728
2729            DynamicsParameter::LevelerEnable(val) => state.leveler_enable[ch] = *val,
2730            DynamicsParameter::LevelerMode(val) => state.leveler_mode[ch] = *val,
2731            DynamicsParameter::LevelerMakeup(val) => state.leveler_makeup[ch] = *val,
2732            DynamicsParameter::LevelerReduce(val) => state.leveler_reduce[ch] = *val,
2733        }
2734    }
2735}
2736
2737/// State of input function.
2738#[derive(Default, Debug, Clone, PartialEq)]
2739pub struct CommandDspInputState {
2740    /// Whether to invert phase of input signals.
2741    pub phase: Vec<bool>,
2742    /// Whether to enable stereo pair.
2743    pub pair: Vec<bool>,
2744    /// The gain of inputs.
2745    pub gain: Vec<i32>,
2746    /// Whether to swap a pair of channels.
2747    pub swap: Vec<bool>,
2748    /// The mode of stereo pair.
2749    pub stereo_mode: Vec<InputStereoPairMode>,
2750    /// The left and right width of stereo pair.
2751    pub width: Vec<f32>,
2752
2753    /// The volume to send to reverb effect.
2754    pub reverb_send: Vec<f32>,
2755    /// The left and right balance to send to reverb effect.
2756    pub reverb_balance: Vec<f32>,
2757
2758    /// Whether to attenuate inputs.
2759    pub pad: Vec<bool>,
2760    /// The nominal level of inputs.
2761    pub nominal_level: Vec<NominalSignalLevel>,
2762    /// Whether to enable phantom powering for inputs.
2763    pub phantom: Vec<bool>,
2764    /// Whether to enable limitter.
2765    pub limitter: Vec<bool>,
2766    /// Whether to increase head room.
2767    pub lookahead: Vec<bool>,
2768    /// Whether to enable soft clipping.
2769    pub soft_clip: Vec<bool>,
2770}
2771
2772/// The trait for specification of inputs.
2773pub trait MotuCommandDspInputSpecification {
2774    /// The input ports.
2775    const INPUT_PORTS: &'static [TargetPort];
2776    /// The number of microphone inputs.
2777    const MIC_COUNT: usize;
2778    /// The number of line inputs.
2779    const LINE_INPUT_COUNT: usize;
2780
2781    /// The minimum value of gain.
2782    const INPUT_GAIN_MIN: i32 = -96;
2783    /// The maximum value of gain.
2784    const INPUT_GAIN_MAX: i32 = 22;
2785    /// The step value of gain.
2786    const INPUT_GAIN_STEP: i32 = 1;
2787
2788    /// The minimum value of width.
2789    const INPUT_WIDTH_MIN: f32 = 0.0;
2790    /// The maximum value of width.
2791    const INPUT_WIDTH_MAX: f32 = 1.0;
2792
2793    /// The minimum value of gain to send to reverb.
2794    const INPUT_REVERB_GAIN_MIN: f32 = 0.0;
2795    /// The maximum value of gain to send to reverb.
2796    const INPUT_REVERB_GAIN_MAX: f32 = 1.0;
2797
2798    /// The minimum value of left and right balance to send to reverb.
2799    const INPUT_REVERB_BALANCE_MIN: f32 = -1.0;
2800    /// The maximum value of left and right balance to send to reverb.
2801    const INPUT_REVERB_BALANCE_MAX: f32 = 1.0;
2802
2803    /// Instantiate input parameters.
2804    fn create_input_state() -> CommandDspInputState {
2805        CommandDspInputState {
2806            phase: vec![Default::default(); Self::INPUT_PORTS.len()],
2807            pair: vec![Default::default(); Self::INPUT_PORTS.len()],
2808            gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2809            swap: vec![Default::default(); Self::INPUT_PORTS.len()],
2810            stereo_mode: vec![Default::default(); Self::INPUT_PORTS.len()],
2811            width: vec![Default::default(); Self::INPUT_PORTS.len()],
2812            reverb_send: vec![Default::default(); Self::INPUT_PORTS.len()],
2813            reverb_balance: vec![Default::default(); Self::INPUT_PORTS.len()],
2814            pad: vec![Default::default(); Self::MIC_COUNT],
2815            phantom: vec![Default::default(); Self::MIC_COUNT],
2816            limitter: vec![Default::default(); Self::MIC_COUNT],
2817            lookahead: vec![Default::default(); Self::MIC_COUNT],
2818            soft_clip: vec![Default::default(); Self::MIC_COUNT],
2819            nominal_level: vec![Default::default(); Self::LINE_INPUT_COUNT],
2820        }
2821    }
2822
2823    /// Instantiate input equalizer parameters.
2824    fn create_input_equalizer_state() -> CommandDspInputEqualizerState {
2825        CommandDspInputEqualizerState(CommandDspEqualizerState {
2826            enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2827
2828            hpf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2829            hpf_slope: vec![Default::default(); Self::INPUT_PORTS.len()],
2830            hpf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2831
2832            lpf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2833            lpf_slope: vec![Default::default(); Self::INPUT_PORTS.len()],
2834            lpf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2835
2836            lf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2837            lf_type: vec![Default::default(); Self::INPUT_PORTS.len()],
2838            lf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2839            lf_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2840            lf_width: vec![Default::default(); Self::INPUT_PORTS.len()],
2841
2842            lmf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2843            lmf_type: vec![Default::default(); Self::INPUT_PORTS.len()],
2844            lmf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2845            lmf_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2846            lmf_width: vec![Default::default(); Self::INPUT_PORTS.len()],
2847
2848            mf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2849            mf_type: vec![Default::default(); Self::INPUT_PORTS.len()],
2850            mf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2851            mf_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2852            mf_width: vec![Default::default(); Self::INPUT_PORTS.len()],
2853
2854            hmf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2855            hmf_type: vec![Default::default(); Self::INPUT_PORTS.len()],
2856            hmf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2857            hmf_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2858            hmf_width: vec![Default::default(); Self::INPUT_PORTS.len()],
2859
2860            hf_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2861            hf_type: vec![Default::default(); Self::INPUT_PORTS.len()],
2862            hf_freq: vec![Default::default(); Self::INPUT_PORTS.len()],
2863            hf_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2864            hf_width: vec![Default::default(); Self::INPUT_PORTS.len()],
2865        })
2866    }
2867
2868    /// Instantiate input dynamics parameters.
2869    fn create_input_dynamics_state() -> CommandDspInputDynamicsState {
2870        CommandDspInputDynamicsState(CommandDspDynamicsState {
2871            enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2872
2873            comp_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2874            comp_detect_mode: vec![Default::default(); Self::INPUT_PORTS.len()],
2875            comp_threshold: vec![Default::default(); Self::INPUT_PORTS.len()],
2876            comp_ratio: vec![Default::default(); Self::INPUT_PORTS.len()],
2877            comp_attack: vec![Default::default(); Self::INPUT_PORTS.len()],
2878            comp_release: vec![Default::default(); Self::INPUT_PORTS.len()],
2879            comp_gain: vec![Default::default(); Self::INPUT_PORTS.len()],
2880
2881            leveler_enable: vec![Default::default(); Self::INPUT_PORTS.len()],
2882            leveler_mode: vec![Default::default(); Self::INPUT_PORTS.len()],
2883            leveler_makeup: vec![Default::default(); Self::INPUT_PORTS.len()],
2884            leveler_reduce: vec![Default::default(); Self::INPUT_PORTS.len()],
2885        })
2886    }
2887}
2888
2889impl<O> MotuCommandDspParametersOperation<CommandDspInputState> for O
2890where
2891    O: MotuCommandDspInputSpecification,
2892{
2893    fn build_commands(params: &CommandDspInputState) -> Vec<DspCmd> {
2894        let mut cmds = Vec::new();
2895
2896        (0..Self::INPUT_PORTS.len()).for_each(|ch| {
2897            cmds.push(DspCmd::Input(InputCmd::Phase(ch, params.phase[ch])));
2898            cmds.push(DspCmd::Input(InputCmd::Pair(ch, params.pair[ch])));
2899            cmds.push(DspCmd::Input(InputCmd::Gain(ch, params.gain[ch])));
2900            cmds.push(DspCmd::Input(InputCmd::Swap(ch, params.swap[ch])));
2901            cmds.push(DspCmd::Input(InputCmd::StereoMode(
2902                ch,
2903                params.stereo_mode[ch],
2904            )));
2905            cmds.push(DspCmd::Input(InputCmd::Width(ch, params.width[ch])));
2906
2907            cmds.push(DspCmd::Input(InputCmd::ReverbSend(
2908                ch,
2909                params.reverb_send[ch],
2910            )));
2911            cmds.push(DspCmd::Input(InputCmd::ReverbLrBalance(
2912                ch,
2913                params.reverb_balance[ch],
2914            )));
2915        });
2916
2917        (0..Self::MIC_COUNT).for_each(|ch| {
2918            cmds.push(DspCmd::Input(InputCmd::Pad(ch, params.pad[ch])));
2919            cmds.push(DspCmd::Input(InputCmd::Phantom(ch, params.phantom[ch])));
2920            cmds.push(DspCmd::Input(InputCmd::Limitter(ch, params.limitter[ch])));
2921            cmds.push(DspCmd::Input(InputCmd::Lookahead(ch, params.lookahead[ch])));
2922            cmds.push(DspCmd::Input(InputCmd::Softclip(ch, params.soft_clip[ch])));
2923        });
2924
2925        (0..Self::LINE_INPUT_COUNT).for_each(|ch| {
2926            cmds.push(DspCmd::Input(InputCmd::NominalLevel(
2927                Self::MIC_COUNT + ch,
2928                params.nominal_level[ch],
2929            )));
2930        });
2931
2932        cmds
2933    }
2934
2935    fn parse_command(params: &mut CommandDspInputState, command: &DspCmd) -> bool {
2936        if let DspCmd::Input(cmd) = command {
2937            match cmd {
2938                InputCmd::Phase(ch, val) => {
2939                    params.phase[*ch] = *val;
2940                    true
2941                }
2942                InputCmd::Pair(ch, val) => {
2943                    params.pair[*ch] = *val;
2944                    true
2945                }
2946                InputCmd::Gain(ch, val) => {
2947                    params.gain[*ch] = *val;
2948                    true
2949                }
2950                InputCmd::Swap(ch, val) => {
2951                    params.swap[*ch] = *val;
2952                    true
2953                }
2954                InputCmd::StereoMode(ch, val) => {
2955                    params.stereo_mode[*ch] = *val;
2956                    true
2957                }
2958                InputCmd::Width(ch, val) => {
2959                    params.width[*ch] = *val;
2960                    true
2961                }
2962                InputCmd::ReverbSend(ch, val) => {
2963                    params.reverb_send[*ch] = *val;
2964                    true
2965                }
2966                InputCmd::ReverbLrBalance(ch, val) => {
2967                    params.reverb_balance[*ch] = *val;
2968                    true
2969                }
2970                InputCmd::NominalLevel(ch, val) => {
2971                    if *ch >= params.pad.len()
2972                        && *ch < params.pad.len() + params.nominal_level.len()
2973                    {
2974                        params.nominal_level[*ch - params.pad.len()] = *val;
2975                        true
2976                    } else {
2977                        false
2978                    }
2979                }
2980                _ => false,
2981            }
2982        } else {
2983            false
2984        }
2985    }
2986}
2987
2988/// State of input equalizers.
2989#[derive(Default, Debug, Clone, PartialEq)]
2990pub struct CommandDspInputEqualizerState(pub CommandDspEqualizerState);
2991
2992impl AsRef<CommandDspEqualizerState> for CommandDspInputEqualizerState {
2993    fn as_ref(&self) -> &CommandDspEqualizerState {
2994        &self.0
2995    }
2996}
2997
2998impl AsMut<CommandDspEqualizerState> for CommandDspInputEqualizerState {
2999    fn as_mut(&mut self) -> &mut CommandDspEqualizerState {
3000        &mut self.0
3001    }
3002}
3003
3004impl<O> MotuCommandDspParametersOperation<CommandDspInputEqualizerState> for O
3005where
3006    O: MotuCommandDspInputSpecification + MotuCommandDspEqualizerSpecification,
3007{
3008    fn build_commands(params: &CommandDspInputEqualizerState) -> Vec<DspCmd> {
3009        let mut cmds = Vec::new();
3010
3011        (0..Self::INPUT_PORTS.len()).for_each(|ch| {
3012            Self::create_equalizer_parameters(&params.0, ch)
3013                .into_iter()
3014                .for_each(|param| {
3015                    let cmd = DspCmd::Input(InputCmd::Equalizer(ch, param));
3016                    cmds.push(cmd);
3017                });
3018        });
3019
3020        cmds
3021    }
3022
3023    fn parse_command(params: &mut CommandDspInputEqualizerState, command: &DspCmd) -> bool {
3024        if let DspCmd::Input(InputCmd::Equalizer(ch, param)) = command {
3025            Self::parse_equalizer_parameter(&mut params.0, param, *ch);
3026            true
3027        } else {
3028            false
3029        }
3030    }
3031}
3032
3033/// State of input dynamics.
3034#[derive(Default, Debug, Clone, PartialEq)]
3035pub struct CommandDspInputDynamicsState(pub CommandDspDynamicsState);
3036
3037impl AsRef<CommandDspDynamicsState> for CommandDspInputDynamicsState {
3038    fn as_ref(&self) -> &CommandDspDynamicsState {
3039        &self.0
3040    }
3041}
3042
3043impl AsMut<CommandDspDynamicsState> for CommandDspInputDynamicsState {
3044    fn as_mut(&mut self) -> &mut CommandDspDynamicsState {
3045        &mut self.0
3046    }
3047}
3048
3049impl<O> MotuCommandDspParametersOperation<CommandDspInputDynamicsState> for O
3050where
3051    O: MotuCommandDspInputSpecification + MotuCommandDspDynamicsSpecification,
3052{
3053    fn build_commands(params: &CommandDspInputDynamicsState) -> Vec<DspCmd> {
3054        let mut cmds = Vec::new();
3055
3056        (0..Self::INPUT_PORTS.len()).for_each(|ch| {
3057            Self::create_dynamics_parameters(&params.0, ch)
3058                .into_iter()
3059                .for_each(|param| {
3060                    let cmd = DspCmd::Input(InputCmd::Dynamics(ch, param));
3061                    cmds.push(cmd);
3062                });
3063        });
3064
3065        cmds
3066    }
3067
3068    fn parse_command(params: &mut CommandDspInputDynamicsState, command: &DspCmd) -> bool {
3069        if let DspCmd::Input(InputCmd::Dynamics(ch, param)) = command {
3070            Self::parse_dynamics_parameter(&mut params.0, param, *ch);
3071            true
3072        } else {
3073            false
3074        }
3075    }
3076}
3077
3078/// State of input function.
3079#[derive(Default, Debug, Clone, PartialEq)]
3080pub struct CommandDspOutputState {
3081    /// The gain to send to reverb effect.
3082    pub reverb_send: Vec<f32>,
3083    /// The volume to return from reverb effect.
3084    pub reverb_return: Vec<f32>,
3085
3086    /// Whether to monitor output in master.
3087    pub master_monitor: Vec<bool>,
3088    /// Whether to take talkback in master.
3089    pub master_talkback: Vec<bool>,
3090    /// Whether to take listenback in master.
3091    pub master_listenback: Vec<bool>,
3092}
3093
3094/// The trait for specification of output.
3095pub trait MotuCommandDspOutputSpecification {
3096    /// The destination port of outputs.
3097    const OUTPUT_PORTS: &'static [TargetPort];
3098
3099    /// The minimum value of gain for outputs.
3100    const OUTPUT_GAIN_MIN: f32 = 0.0;
3101    /// The maximum value of gain for outputs.
3102    const OUTPUT_GAIN_MAX: f32 = 1.0;
3103
3104    /// The minimum value of volume for outputs.
3105    const OUTPUT_VOLUME_MIN: f32 = 0.0;
3106    /// The maximum value of volume for outputs.
3107    const OUTPUT_VOLUME_MAX: f32 = 1.0;
3108
3109    fn create_output_state() -> CommandDspOutputState {
3110        CommandDspOutputState {
3111            reverb_send: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3112            reverb_return: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3113            master_monitor: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3114            master_talkback: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3115            master_listenback: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3116        }
3117    }
3118
3119    fn create_output_equalizer_state() -> CommandDspOutputEqualizerState {
3120        CommandDspOutputEqualizerState(CommandDspEqualizerState {
3121            enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3122
3123            hpf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3124            hpf_slope: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3125            hpf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3126
3127            lpf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3128            lpf_slope: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3129            lpf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3130
3131            lf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3132            lf_type: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3133            lf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3134            lf_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3135            lf_width: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3136
3137            lmf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3138            lmf_type: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3139            lmf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3140            lmf_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3141            lmf_width: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3142
3143            mf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3144            mf_type: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3145            mf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3146            mf_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3147            mf_width: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3148
3149            hmf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3150            hmf_type: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3151            hmf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3152            hmf_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3153            hmf_width: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3154
3155            hf_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3156            hf_type: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3157            hf_freq: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3158            hf_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3159            hf_width: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3160        })
3161    }
3162
3163    fn create_output_dynamics_state() -> CommandDspOutputDynamicsState {
3164        CommandDspOutputDynamicsState(CommandDspDynamicsState {
3165            enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3166
3167            comp_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3168            comp_detect_mode: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3169            comp_threshold: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3170            comp_ratio: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3171            comp_attack: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3172            comp_release: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3173            comp_gain: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3174
3175            leveler_enable: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3176            leveler_mode: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3177            leveler_makeup: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3178            leveler_reduce: vec![Default::default(); Self::OUTPUT_PORTS.len()],
3179        })
3180    }
3181}
3182
3183impl<O> MotuCommandDspParametersOperation<CommandDspOutputState> for O
3184where
3185    O: MotuCommandDspOutputSpecification,
3186{
3187    fn build_commands(params: &CommandDspOutputState) -> Vec<DspCmd> {
3188        let mut cmds = Vec::new();
3189
3190        (0..Self::OUTPUT_PORTS.len()).for_each(|ch| {
3191            cmds.push(DspCmd::Output(OutputCmd::ReverbSend(
3192                ch,
3193                params.reverb_send[ch],
3194            )));
3195            cmds.push(DspCmd::Output(OutputCmd::ReverbReturn(
3196                ch,
3197                params.reverb_return[ch],
3198            )));
3199
3200            cmds.push(DspCmd::Output(OutputCmd::MasterMonitor(
3201                ch,
3202                params.master_monitor[ch],
3203            )));
3204            cmds.push(DspCmd::Output(OutputCmd::MasterTalkback(
3205                ch,
3206                params.master_talkback[ch],
3207            )));
3208            cmds.push(DspCmd::Output(OutputCmd::MasterListenback(
3209                ch,
3210                params.master_listenback[ch],
3211            )));
3212        });
3213
3214        cmds
3215    }
3216
3217    fn parse_command(params: &mut CommandDspOutputState, command: &DspCmd) -> bool {
3218        if let DspCmd::Output(cmd) = command {
3219            match cmd {
3220                OutputCmd::ReverbSend(ch, val) => {
3221                    params.reverb_send[*ch] = *val;
3222                    true
3223                }
3224                OutputCmd::ReverbReturn(ch, val) => {
3225                    params.reverb_return[*ch] = *val;
3226                    true
3227                }
3228                OutputCmd::MasterMonitor(ch, val) => {
3229                    params.master_monitor[*ch] = *val;
3230                    true
3231                }
3232                OutputCmd::MasterTalkback(ch, val) => {
3233                    params.master_talkback[*ch] = *val;
3234                    true
3235                }
3236                OutputCmd::MasterListenback(ch, val) => {
3237                    params.master_listenback[*ch] = *val;
3238                    true
3239                }
3240                _ => false,
3241            }
3242        } else {
3243            false
3244        }
3245    }
3246}
3247
3248/// State of output equalizers.
3249#[derive(Default, Debug, Clone, PartialEq)]
3250pub struct CommandDspOutputEqualizerState(pub CommandDspEqualizerState);
3251
3252impl AsRef<CommandDspEqualizerState> for CommandDspOutputEqualizerState {
3253    fn as_ref(&self) -> &CommandDspEqualizerState {
3254        &self.0
3255    }
3256}
3257
3258impl AsMut<CommandDspEqualizerState> for CommandDspOutputEqualizerState {
3259    fn as_mut(&mut self) -> &mut CommandDspEqualizerState {
3260        &mut self.0
3261    }
3262}
3263
3264impl<O> MotuCommandDspParametersOperation<CommandDspOutputEqualizerState> for O
3265where
3266    O: MotuCommandDspOutputSpecification + MotuCommandDspEqualizerSpecification,
3267{
3268    fn build_commands(params: &CommandDspOutputEqualizerState) -> Vec<DspCmd> {
3269        let mut cmds = Vec::new();
3270
3271        (0..Self::OUTPUT_PORTS.len()).for_each(|ch| {
3272            Self::create_equalizer_parameters(&params.0, ch)
3273                .into_iter()
3274                .for_each(|param| {
3275                    let cmd = DspCmd::Output(OutputCmd::Equalizer(ch, param));
3276                    cmds.push(cmd);
3277                });
3278        });
3279
3280        cmds
3281    }
3282
3283    fn parse_command(params: &mut CommandDspOutputEqualizerState, command: &DspCmd) -> bool {
3284        if let DspCmd::Output(OutputCmd::Equalizer(ch, param)) = command {
3285            Self::parse_equalizer_parameter(&mut params.0, param, *ch);
3286            true
3287        } else {
3288            false
3289        }
3290    }
3291}
3292
3293/// State of output dynamics.
3294#[derive(Default, Debug, Clone, PartialEq)]
3295pub struct CommandDspOutputDynamicsState(pub CommandDspDynamicsState);
3296
3297impl AsRef<CommandDspDynamicsState> for CommandDspOutputDynamicsState {
3298    fn as_ref(&self) -> &CommandDspDynamicsState {
3299        &self.0
3300    }
3301}
3302
3303impl AsMut<CommandDspDynamicsState> for CommandDspOutputDynamicsState {
3304    fn as_mut(&mut self) -> &mut CommandDspDynamicsState {
3305        &mut self.0
3306    }
3307}
3308
3309impl<O> MotuCommandDspParametersOperation<CommandDspOutputDynamicsState> for O
3310where
3311    O: MotuCommandDspOutputSpecification + MotuCommandDspDynamicsSpecification,
3312{
3313    fn build_commands(params: &CommandDspOutputDynamicsState) -> Vec<DspCmd> {
3314        let mut cmds = Vec::new();
3315
3316        (0..Self::OUTPUT_PORTS.len()).for_each(|ch| {
3317            Self::create_dynamics_parameters(&params.0, ch)
3318                .into_iter()
3319                .for_each(|param| {
3320                    let cmd = DspCmd::Output(OutputCmd::Dynamics(ch, param));
3321                    cmds.push(cmd);
3322                });
3323        });
3324
3325        cmds
3326    }
3327
3328    fn parse_command(params: &mut CommandDspOutputDynamicsState, command: &DspCmd) -> bool {
3329        if let DspCmd::Output(OutputCmd::Dynamics(ch, param)) = command {
3330            Self::parse_dynamics_parameter(&mut params.0, param, *ch);
3331            true
3332        } else {
3333            false
3334        }
3335    }
3336}
3337
3338/// State of hardware meter.
3339#[derive(Default, Debug, Clone, PartialEq)]
3340pub struct CommandDspMeterState {
3341    pub inputs: Vec<f32>,
3342    pub outputs: Vec<f32>,
3343    // TODO: other fields.
3344}
3345
3346const METER_IMAGE_SIZE: usize = 400;
3347
3348/// The trait for specification of meter.
3349pub trait MotuCommandDspMeterSpecification {
3350    const INPUT_PORTS: &'static [(TargetPort, usize)];
3351    const OUTPUT_PORTS: &'static [(TargetPort, usize)];
3352
3353    const LEVEL_MIN: f32 = 0.0;
3354    const LEVEL_MAX: f32 = 1.0;
3355
3356    const METER_IMAGE_SIZE: usize = 400;
3357
3358    fn create_meter_state() -> CommandDspMeterState {
3359        CommandDspMeterState {
3360            inputs: vec![0.0; Self::INPUT_PORTS.len()],
3361            outputs: vec![0.0; Self::OUTPUT_PORTS.len()],
3362        }
3363    }
3364}
3365
3366impl<O> MotuCommandDspImageOperation<CommandDspMeterState, [f32; METER_IMAGE_SIZE]> for O
3367where
3368    O: MotuCommandDspMeterSpecification,
3369{
3370    fn parse_image(params: &mut CommandDspMeterState, image: &[f32; METER_IMAGE_SIZE]) {
3371        params
3372            .inputs
3373            .iter_mut()
3374            .zip(Self::INPUT_PORTS)
3375            .for_each(|(m, &(_, pos))| *m = image[pos]);
3376        params
3377            .outputs
3378            .iter_mut()
3379            .zip(Self::OUTPUT_PORTS)
3380            .for_each(|(m, &(_, pos))| *m = image[pos]);
3381    }
3382}
3383
3384#[cfg(test)]
3385mod test {
3386    use super::*;
3387
3388    #[test]
3389    fn test_u8_cmds() {
3390        [
3391            DspCmd::Monitor(MonitorCmd::ReturnAssign(0x69)),
3392            DspCmd::Monitor(MonitorCmd::TalkbackEnable(true)),
3393            DspCmd::Monitor(MonitorCmd::ListenbackEnable(true)),
3394            DspCmd::Input(InputCmd::Phase(0x59, true)),
3395            DspCmd::Input(InputCmd::Pair(0x0, false)),
3396            DspCmd::Input(InputCmd::Swap(0x24, false)),
3397            DspCmd::Input(InputCmd::StereoMode(
3398                0x35,
3399                InputStereoPairMode::MonauralStereo,
3400            )),
3401            DspCmd::Input(InputCmd::Limitter(0xad, true)),
3402            DspCmd::Input(InputCmd::Lookahead(0xdd, true)),
3403            DspCmd::Input(InputCmd::Softclip(0xfc, false)),
3404            DspCmd::Input(InputCmd::Pad(0x91, true)),
3405            DspCmd::Input(InputCmd::NominalLevel(
3406                0x91,
3407                NominalSignalLevel::Professional,
3408            )),
3409            DspCmd::Input(InputCmd::Phantom(0x13, false)),
3410            DspCmd::Input(InputCmd::Equalizer(0x14, EqualizerParameter::Enable(false))),
3411            DspCmd::Input(InputCmd::Equalizer(
3412                0x23,
3413                EqualizerParameter::HpfEnable(true),
3414            )),
3415            DspCmd::Input(InputCmd::Equalizer(
3416                0x32,
3417                EqualizerParameter::HpfSlope(RollOffLevel::L30),
3418            )),
3419            DspCmd::Input(InputCmd::Equalizer(
3420                0x41,
3421                EqualizerParameter::LfEnable(false),
3422            )),
3423            DspCmd::Input(InputCmd::Equalizer(
3424                0x59,
3425                EqualizerParameter::LfType(FilterType5::Shelf),
3426            )),
3427            DspCmd::Input(InputCmd::Equalizer(
3428                0x68,
3429                EqualizerParameter::LmfEnable(true),
3430            )),
3431            DspCmd::Input(InputCmd::Equalizer(
3432                0x77,
3433                EqualizerParameter::LmfType(FilterType4::T4),
3434            )),
3435            DspCmd::Input(InputCmd::Equalizer(
3436                0x86,
3437                EqualizerParameter::MfEnable(false),
3438            )),
3439            DspCmd::Input(InputCmd::Equalizer(
3440                0x95,
3441                EqualizerParameter::MfType(FilterType4::T3),
3442            )),
3443            DspCmd::Input(InputCmd::Equalizer(
3444                0xaf,
3445                EqualizerParameter::HmfEnable(true),
3446            )),
3447            DspCmd::Input(InputCmd::Equalizer(
3448                0xbe,
3449                EqualizerParameter::HmfType(FilterType4::T2),
3450            )),
3451            DspCmd::Input(InputCmd::Equalizer(
3452                0xcd,
3453                EqualizerParameter::HfEnable(false),
3454            )),
3455            DspCmd::Input(InputCmd::Equalizer(
3456                0xdc,
3457                EqualizerParameter::HfType(FilterType5::T1),
3458            )),
3459            DspCmd::Input(InputCmd::Equalizer(
3460                0xeb,
3461                EqualizerParameter::LpfEnable(true),
3462            )),
3463            DspCmd::Input(InputCmd::Equalizer(
3464                0xfa,
3465                EqualizerParameter::LpfSlope(RollOffLevel::L24),
3466            )),
3467            DspCmd::Input(InputCmd::Dynamics(0xf0, DynamicsParameter::Enable(false))),
3468            DspCmd::Input(InputCmd::Dynamics(
3469                0xe1,
3470                DynamicsParameter::CompEnable(true),
3471            )),
3472            DspCmd::Input(InputCmd::Dynamics(
3473                0xd2,
3474                DynamicsParameter::CompDetectMode(LevelDetectMode::Rms),
3475            )),
3476            DspCmd::Input(InputCmd::Dynamics(
3477                0xc3,
3478                DynamicsParameter::LevelerEnable(false),
3479            )),
3480            DspCmd::Input(InputCmd::Dynamics(
3481                0xb4,
3482                DynamicsParameter::LevelerMode(LevelerMode::Limit),
3483            )),
3484            DspCmd::Mixer(MixerCmd::OutputAssign(0xa5, 0x91)),
3485            DspCmd::Mixer(MixerCmd::OutputMute(0x96, true)),
3486            DspCmd::Mixer(MixerCmd::SourceMute(0x87, 0x13, false)),
3487            DspCmd::Mixer(MixerCmd::SourceSolo(0x78, 0x31, true)),
3488            DspCmd::Mixer(MixerCmd::SourceStereoMode(
3489                0x69,
3490                0x11,
3491                SourceStereoPairMode::LrBalance,
3492            )),
3493            DspCmd::Output(OutputCmd::Equalizer(
3494                0x5a,
3495                EqualizerParameter::Enable(false),
3496            )),
3497            DspCmd::Output(OutputCmd::Equalizer(
3498                0x4b,
3499                EqualizerParameter::HpfEnable(true),
3500            )),
3501            DspCmd::Output(OutputCmd::Equalizer(
3502                0x3c,
3503                EqualizerParameter::HpfSlope(RollOffLevel::L6),
3504            )),
3505            DspCmd::Output(OutputCmd::Equalizer(
3506                0x2d,
3507                EqualizerParameter::LfEnable(false),
3508            )),
3509            DspCmd::Output(OutputCmd::Equalizer(
3510                0x1e,
3511                EqualizerParameter::LfType(FilterType5::Shelf),
3512            )),
3513            DspCmd::Output(OutputCmd::Equalizer(
3514                0x0f,
3515                EqualizerParameter::LmfEnable(true),
3516            )),
3517            DspCmd::Output(OutputCmd::Equalizer(
3518                0xf1,
3519                EqualizerParameter::LmfType(FilterType4::T4),
3520            )),
3521            DspCmd::Output(OutputCmd::Equalizer(
3522                0xe2,
3523                EqualizerParameter::MfEnable(false),
3524            )),
3525            DspCmd::Output(OutputCmd::Equalizer(
3526                0xd3,
3527                EqualizerParameter::MfType(FilterType4::T3),
3528            )),
3529            DspCmd::Output(OutputCmd::Equalizer(
3530                0xc4,
3531                EqualizerParameter::HmfEnable(true),
3532            )),
3533            DspCmd::Output(OutputCmd::Equalizer(
3534                0xb5,
3535                EqualizerParameter::HmfType(FilterType4::T2),
3536            )),
3537            DspCmd::Output(OutputCmd::Equalizer(
3538                0xa6,
3539                EqualizerParameter::HfEnable(false),
3540            )),
3541            DspCmd::Output(OutputCmd::Equalizer(
3542                0x97,
3543                EqualizerParameter::HfType(FilterType5::T1),
3544            )),
3545            DspCmd::Output(OutputCmd::Equalizer(
3546                0x88,
3547                EqualizerParameter::LpfEnable(true),
3548            )),
3549            DspCmd::Output(OutputCmd::Equalizer(
3550                0x79,
3551                EqualizerParameter::LpfSlope(RollOffLevel::L18),
3552            )),
3553            DspCmd::Output(OutputCmd::Dynamics(0xff, DynamicsParameter::Enable(false))),
3554            DspCmd::Output(OutputCmd::Dynamics(
3555                0xee,
3556                DynamicsParameter::CompEnable(true),
3557            )),
3558            DspCmd::Output(OutputCmd::Dynamics(
3559                0xdd,
3560                DynamicsParameter::CompDetectMode(LevelDetectMode::Peak),
3561            )),
3562            DspCmd::Output(OutputCmd::Dynamics(
3563                0xcc,
3564                DynamicsParameter::LevelerEnable(false),
3565            )),
3566            DspCmd::Output(OutputCmd::Dynamics(
3567                0xbb,
3568                DynamicsParameter::LevelerMode(LevelerMode::Compress),
3569            )),
3570            DspCmd::Output(OutputCmd::MasterMonitor(0x97, true)),
3571            DspCmd::Output(OutputCmd::MasterTalkback(0xec, false)),
3572            DspCmd::Output(OutputCmd::MasterListenback(0xd5, true)),
3573            DspCmd::Reverb(ReverbCmd::Enable(false)),
3574            DspCmd::Reverb(ReverbCmd::Split(SplitPoint::Mixer)),
3575            DspCmd::Reverb(ReverbCmd::ReflectionMode(RoomShape::D)),
3576            DspCmd::Reserved(vec![0x69, 0xed, 0xba, 0x98, 0xec, 0x75]),
3577        ]
3578        .iter()
3579        .for_each(|cmd| {
3580            let mut raw = Vec::new();
3581            cmd.build(&mut raw);
3582            let mut c = Vec::new();
3583            assert_eq!(DspCmd::parse(&raw, &mut c), CMD_BYTE_SINGLE_LENGTH);
3584            assert_eq!(&c[0], cmd);
3585        });
3586    }
3587
3588    #[test]
3589    fn test_i32_cmds() {
3590        [
3591            DspCmd::Monitor(MonitorCmd::Focus(FocusTarget::Output(11))),
3592            DspCmd::Input(InputCmd::Gain(0xe4, 0x01)),
3593            DspCmd::Input(InputCmd::Dynamics(
3594                0xb1,
3595                DynamicsParameter::CompThreshold(97531),
3596            )),
3597            DspCmd::Output(OutputCmd::Dynamics(
3598                0x45,
3599                DynamicsParameter::CompThreshold(86420),
3600            )),
3601            DspCmd::Reverb(ReverbCmd::ShelfFilterAttenuation(98765)),
3602        ]
3603        .iter()
3604        .for_each(|cmd| {
3605            let mut raw = Vec::new();
3606            cmd.build(&mut raw);
3607            let mut c = Vec::new();
3608            assert_eq!(DspCmd::parse(&raw, &mut c), CMD_QUADLET_SINGLE_LENGTH);
3609            assert_eq!(&c[0], cmd);
3610        });
3611    }
3612
3613    #[test]
3614    fn test_u32_cmds() {
3615        [
3616            DspCmd::Input(InputCmd::Equalizer(0xc2, EqualizerParameter::HpfFreq(10))),
3617            DspCmd::Input(InputCmd::Equalizer(0xb1, EqualizerParameter::LfFreq(20))),
3618            DspCmd::Input(InputCmd::Equalizer(0x8e, EqualizerParameter::LmfFreq(30))),
3619            DspCmd::Input(InputCmd::Equalizer(0x5b, EqualizerParameter::MfFreq(40))),
3620            DspCmd::Input(InputCmd::Equalizer(0x28, EqualizerParameter::HmfFreq(50))),
3621            DspCmd::Input(InputCmd::Equalizer(0xf5, EqualizerParameter::HfFreq(60))),
3622            DspCmd::Input(InputCmd::Equalizer(0xc2, EqualizerParameter::LpfFreq(70))),
3623            DspCmd::Input(InputCmd::Dynamics(0x9f, DynamicsParameter::CompAttack(100))),
3624            DspCmd::Input(InputCmd::Dynamics(
3625                0x8e,
3626                DynamicsParameter::CompRelease(200),
3627            )),
3628            DspCmd::Output(OutputCmd::Dynamics(
3629                0x7f,
3630                DynamicsParameter::LevelerMakeup(1000),
3631            )),
3632            DspCmd::Output(OutputCmd::Dynamics(
3633                0xf2,
3634                DynamicsParameter::LevelerReduce(2000),
3635            )),
3636            DspCmd::Output(OutputCmd::Equalizer(0xa8, EqualizerParameter::HpfFreq(103))),
3637            DspCmd::Output(OutputCmd::Equalizer(0x39, EqualizerParameter::LfFreq(105))),
3638            DspCmd::Output(OutputCmd::Equalizer(0x5b, EqualizerParameter::LmfFreq(107))),
3639            DspCmd::Output(OutputCmd::Equalizer(0xbc, EqualizerParameter::MfFreq(109))),
3640            DspCmd::Output(OutputCmd::Equalizer(0xf7, EqualizerParameter::HmfFreq(111))),
3641            DspCmd::Output(OutputCmd::Equalizer(0xc0, EqualizerParameter::HfFreq(113))),
3642            DspCmd::Output(OutputCmd::Equalizer(0x29, EqualizerParameter::LpfFreq(115))),
3643            DspCmd::Output(OutputCmd::Dynamics(
3644                0x1b,
3645                DynamicsParameter::CompAttack(111),
3646            )),
3647            DspCmd::Output(OutputCmd::Dynamics(
3648                0x49,
3649                DynamicsParameter::CompRelease(113),
3650            )),
3651            DspCmd::Input(InputCmd::Dynamics(
3652                0x6c,
3653                DynamicsParameter::LevelerMakeup(1111),
3654            )),
3655            DspCmd::Input(InputCmd::Dynamics(
3656                0x5b,
3657                DynamicsParameter::LevelerReduce(1113),
3658            )),
3659            DspCmd::Reverb(ReverbCmd::PreDelay(11111)),
3660            DspCmd::Reverb(ReverbCmd::ShelfFilterFreq(111113)),
3661            DspCmd::Reverb(ReverbCmd::DecayTime(111115)),
3662            DspCmd::Reverb(ReverbCmd::LowFreqTime(111117)),
3663            DspCmd::Reverb(ReverbCmd::MiddleFreqTime(111119)),
3664            DspCmd::Reverb(ReverbCmd::HighFreqTime(111121)),
3665            DspCmd::Reverb(ReverbCmd::LowFreqCrossover(111123)),
3666            DspCmd::Reverb(ReverbCmd::HighFreqCrossover(111125)),
3667            DspCmd::Reverb(ReverbCmd::ReflectionSize(111127)),
3668            DspCmd::Reserved(vec![0x66, 0x00, 0x01, 0x02, 0x80, 0x04, 0x05, 0x06, 0x07]),
3669        ]
3670        .iter()
3671        .for_each(|cmd| {
3672            let mut raw = Vec::new();
3673            cmd.build(&mut raw);
3674            let mut c = Vec::new();
3675            assert_eq!(DspCmd::parse(&raw, &mut c), CMD_QUADLET_SINGLE_LENGTH);
3676            assert_eq!(&c[0], cmd);
3677        });
3678    }
3679
3680    #[test]
3681    fn test_f32_cmds() {
3682        [
3683            DspCmd::Monitor(MonitorCmd::Volume(9.012345678)),
3684            DspCmd::Monitor(MonitorCmd::ListenbackVolume(9.234567891)),
3685            DspCmd::Monitor(MonitorCmd::TalkbackVolume(9.345678912)),
3686            DspCmd::Input(InputCmd::Width(0xd3, 0.0987654321)),
3687            DspCmd::Input(InputCmd::Equalizer(
3688                0xa0,
3689                EqualizerParameter::LfGain(0.123456789),
3690            )),
3691            DspCmd::Input(InputCmd::Equalizer(
3692                0x9f,
3693                EqualizerParameter::LfWidth(0.987654321),
3694            )),
3695            DspCmd::Input(InputCmd::Equalizer(
3696                0x7d,
3697                EqualizerParameter::LmfGain(0.234567891),
3698            )),
3699            DspCmd::Input(InputCmd::Equalizer(
3700                0x6c,
3701                EqualizerParameter::LmfWidth(0.876543219),
3702            )),
3703            DspCmd::Input(InputCmd::Equalizer(
3704                0x4a,
3705                EqualizerParameter::MfGain(0.345678912),
3706            )),
3707            DspCmd::Input(InputCmd::Equalizer(
3708                0x39,
3709                EqualizerParameter::MfWidth(0.765432198),
3710            )),
3711            DspCmd::Input(InputCmd::Equalizer(
3712                0x17,
3713                EqualizerParameter::HmfGain(0.456789123),
3714            )),
3715            DspCmd::Input(InputCmd::Equalizer(
3716                0x06,
3717                EqualizerParameter::HmfWidth(0.654321987),
3718            )),
3719            DspCmd::Input(InputCmd::Equalizer(
3720                0xe4,
3721                EqualizerParameter::HfGain(0.567891234),
3722            )),
3723            DspCmd::Input(InputCmd::Equalizer(
3724                0xd3,
3725                EqualizerParameter::HfWidth(0.543219876),
3726            )),
3727            DspCmd::Input(InputCmd::Dynamics(
3728                0xa0,
3729                DynamicsParameter::CompRatio(0.678912345),
3730            )),
3731            DspCmd::Input(InputCmd::Dynamics(
3732                0x7d,
3733                DynamicsParameter::CompGain(0.432198765),
3734            )),
3735            DspCmd::Input(InputCmd::ReverbSend(0x33, 0.789123456)),
3736            DspCmd::Input(InputCmd::ReverbLrBalance(0xcc, 0.891234567)),
3737            DspCmd::Mixer(MixerCmd::OutputVolume(0x4a, 1.2345678)),
3738            DspCmd::Mixer(MixerCmd::ReverbSend(0x39, 1.3456789)),
3739            DspCmd::Mixer(MixerCmd::ReverbReturn(0x28, 1.456789)),
3740            DspCmd::Mixer(MixerCmd::SourceMonauralLrBalance(0x17, 0xc8, 1.987654)),
3741            DspCmd::Mixer(MixerCmd::SourceGain(0x06, 0x11, 1.876543)),
3742            DspCmd::Mixer(MixerCmd::SourceStereoLrBalance(0xe5, 0x13, 1.7654321)),
3743            DspCmd::Mixer(MixerCmd::SourceStereoWidth(0xd4, 0x1a, 1.654321)),
3744            DspCmd::Output(OutputCmd::Equalizer(
3745                0x11,
3746                EqualizerParameter::LfGain(2.123456789),
3747            )),
3748            DspCmd::Output(OutputCmd::Equalizer(
3749                0x5a,
3750                EqualizerParameter::LfWidth(2.987654321),
3751            )),
3752            DspCmd::Output(OutputCmd::Equalizer(
3753                0x98,
3754                EqualizerParameter::LmfGain(2.234567891),
3755            )),
3756            DspCmd::Output(OutputCmd::Equalizer(
3757                0x74,
3758                EqualizerParameter::LmfWidth(2.876543219),
3759            )),
3760            DspCmd::Output(OutputCmd::Equalizer(
3761                0x32,
3762                EqualizerParameter::MfGain(2.345678912),
3763            )),
3764            DspCmd::Output(OutputCmd::Equalizer(
3765                0x20,
3766                EqualizerParameter::MfWidth(2.765432198),
3767            )),
3768            DspCmd::Output(OutputCmd::Equalizer(
3769                0xc0,
3770                EqualizerParameter::HmfGain(2.456789123),
3771            )),
3772            DspCmd::Output(OutputCmd::Equalizer(
3773                0xf5,
3774                EqualizerParameter::HmfWidth(2.654321987),
3775            )),
3776            DspCmd::Output(OutputCmd::Equalizer(
3777                0x01,
3778                EqualizerParameter::HfGain(2.567891234),
3779            )),
3780            DspCmd::Output(OutputCmd::Equalizer(
3781                0xdb,
3782                EqualizerParameter::HfWidth(2.543219876),
3783            )),
3784            DspCmd::Output(OutputCmd::Dynamics(
3785                0x5e,
3786                DynamicsParameter::CompRatio(2.678912345),
3787            )),
3788            DspCmd::Output(OutputCmd::Dynamics(
3789                0xba,
3790                DynamicsParameter::CompGain(2.432198765),
3791            )),
3792            DspCmd::Output(OutputCmd::ReverbSend(0x99, 2.78912345)),
3793            DspCmd::Output(OutputCmd::ReverbReturn(0x88, 2.321987654)),
3794            DspCmd::Reverb(ReverbCmd::Width(123.456)),
3795            DspCmd::Reverb(ReverbCmd::ReflectionLevel(234.561)),
3796        ]
3797        .iter()
3798        .for_each(|cmd| {
3799            let mut raw = Vec::new();
3800            cmd.build(&mut raw);
3801            let mut c = Vec::new();
3802            assert_eq!(DspCmd::parse(&raw, &mut c), CMD_QUADLET_SINGLE_LENGTH);
3803            assert_eq!(&c[0], cmd);
3804        });
3805    }
3806
3807    #[test]
3808    fn test_resource() {
3809        let cmd = DspCmd::Resource(ResourceCmd::Usage(99.99999, 0x17));
3810        let mut raw = Vec::new();
3811        cmd.build(&mut raw);
3812        let mut c = Vec::new();
3813        assert_eq!(DspCmd::parse(&raw, &mut c), CMD_RESOURCE_LENGTH);
3814        assert_eq!(c[0], cmd);
3815    }
3816
3817    #[test]
3818    fn message_decode_test() {
3819        let raw = [
3820            0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x69, 0x00, 0x00, 0x0a, 0x00,
3821            0x00, 0x69, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x66, 0x00, 0x07, 0x00, 0xff, 0x00, 0x00,
3822            0x00, 0x01, 0x62, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x49,
3823            0x07, 0x00, 0x02, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02,
3824            0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x46,
3825            0x00, 0xa0, 0x8c, 0x46, 0x00, 0xa0, 0x8c,
3826        ];
3827        let mut handler = CommandDspMessageHandler::default();
3828        handler.cache.extend_from_slice(&raw);
3829        let cmds = handler.decode_messages();
3830        assert_eq!(cmds[0], DspCmd::Monitor(MonitorCmd::Volume(1.0)));
3831        assert_eq!(
3832            cmds[1],
3833            DspCmd::Monitor(MonitorCmd::Reserved(
3834                vec![0x00, 0x0a, 0x00, 0x00],
3835                vec![0x00]
3836            ))
3837        );
3838        assert_eq!(
3839            cmds[2],
3840            DspCmd::Monitor(MonitorCmd::Reserved(
3841                vec![0x00, 0x0b, 0x00, 0x00],
3842                vec![0x00]
3843            ))
3844        );
3845        assert_eq!(
3846            cmds[3],
3847            DspCmd::Reserved(vec![0x66, 0x00, 0x07, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01])
3848        );
3849        assert_eq!(cmds[4], DspCmd::Monitor(MonitorCmd::Volume(1.0)));
3850        assert_eq!(
3851            cmds[5],
3852            DspCmd::Output(OutputCmd::MasterListenback(0, false))
3853        );
3854        assert_eq!(
3855            cmds[6],
3856            DspCmd::Output(OutputCmd::MasterListenback(1, false))
3857        );
3858        assert_eq!(
3859            cmds[7],
3860            DspCmd::Output(OutputCmd::MasterListenback(2, false))
3861        );
3862        assert_eq!(
3863            cmds[8],
3864            DspCmd::Output(OutputCmd::MasterListenback(3, false))
3865        );
3866        assert_eq!(
3867            cmds[9],
3868            DspCmd::Output(OutputCmd::MasterListenback(4, false))
3869        );
3870        assert_eq!(
3871            cmds[10],
3872            DspCmd::Output(OutputCmd::MasterListenback(5, false))
3873        );
3874        assert_eq!(
3875            cmds[11],
3876            DspCmd::Output(OutputCmd::MasterListenback(6, false))
3877        );
3878        assert_eq!(cmds[12], DspCmd::Input(InputCmd::Width(0, 0.0)));
3879        assert_eq!(cmds[13], DspCmd::Input(InputCmd::Width(1, 0.0)));
3880        assert_eq!(cmds.len(), 14);
3881    }
3882}