firewire_bebob_protocols/apogee/
ensemble.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol implementation for Apogee Electronics Ensemble FireWire.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Apogee Electronics Ensemble FireWire.
8//!
9//! DM1500 ASIC is used for Apogee Ensemble FireWire.
10//!
11//! ## Diagram of internal signal flow for Apogee Ensemble FireWire
12//!
13//! ```text
14//!                                ++==========++
15//! analog-inputs (8 channels) --> ||  18x18   ||
16//! spdif-inputs (2 channels) ---> ||  capture || --> stream-outputs (18 channels)
17//! adat-inputs (8 channels) ----> ||  router  ||
18//!                                ++==========++
19//!
20//!                                ++==========++
21//! analog-inputs (8 channels) --> ||  36x4    ||
22//! spdif-inputs (2 channels) ---> ||          || --> mixer-outputs (4 channels)
23//! adat-inputs (8 channels) ----> ||  mixer   ||
24//! stream-inputs (18 channels) -> ||          ||
25//!                                ++==========++
26//!
27//!                                ++==========++
28//! analog-inputs (8 channels) --> ||  40x18   ||
29//! spdif-inputs (2 channels) ---> ||          || --> analog-outputs (8 channels)
30//! adat-inputs (8 channels) ----> || playback || --> spdif-outputs (2 channels)
31//! stream-inputs (18 channels) -> ||          || --> adat-outputs (8 channels)
32//! mixer-outputs (4 channels) --> ||  router  ||
33//!                                ++==========++
34//!
35//! (source) ----------------------------> spdif-output-1/2
36//!                           ^
37//!                           |
38//!                 ++================++
39//!                 || rate converter || (optional)
40//!                 ++================++
41//!                           |
42//!                           v
43//! spdif-input-1/2 ------------------------> (destination)
44//!
45//! analog-input-1/2 ------------------------> (destination)
46//! analog-input-3/4 ------------------------> (destination)
47//! analog-input-5/6 ------------------------> (destination)
48//! analog-input-7/8 ------------------------> (destination)
49//! spdif-input-1/2 -------------------------> (destination)
50//!                           ^
51//!                           |
52//!                ++==================++
53//!                || format converter || (optional)
54//!                ++==================++
55//!                           |
56//!                           v
57//! (source) ------------------------------> spdif-output-1/2
58//! ```
59//!
60//! The protocol implementation for Apogee Ensemble FireWire was written with firmware version
61//! below:
62//!
63//! ```sh
64//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
65//! protocol:
66//!   version: 3
67//! bootloader:
68//!   timestamp: 2006-04-07T11:13:17+0000
69//!   version: 0.0.0
70//! hardware:
71//!   GUID: 0x0000f1a50003db05
72//!   model ID: 0x000000
73//!   revision: 0.0.0
74//! software:
75//!   timestamp: 2008-11-08T12:36:10+0000
76//!   ID: 0x0001eeee
77//!   revision: 0.0.5297
78//! image:
79//!   base address: 0x400c0080
80//!   maximum size: 0x156aa8
81//! ```
82
83use super::*;
84
85/// The protocol implementation for media and sampling clock of Ensemble FireWire.
86#[derive(Default, Debug)]
87pub struct EnsembleClkProtocol;
88
89impl MediaClockFrequencyOperation for EnsembleClkProtocol {
90    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000, 176400, 192000];
91}
92
93impl SamplingClockSourceOperation for EnsembleClkProtocol {
94    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
95        subunit: MUSIC_SUBUNIT_0,
96        plug_id: 7,
97    });
98
99    const SRC_LIST: &'static [SignalAddr] = &[
100        // Internal
101        SignalAddr::Subunit(SignalSubunitAddr {
102            subunit: MUSIC_SUBUNIT_0,
103            plug_id: 7,
104        }),
105        // S/PDIF-coax
106        SignalAddr::Unit(SignalUnitAddr::Ext(4)),
107        // Optical
108        SignalAddr::Unit(SignalUnitAddr::Ext(5)),
109        // Word clock
110        SignalAddr::Unit(SignalUnitAddr::Ext(6)),
111    ];
112}
113
114/// Parameters of sample format converter.
115#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
116pub struct EnsembleConverterParameters {
117    /// The target of sample format converter.
118    pub format_target: FormatConvertTarget,
119    /// The target of sampling rate converter.
120    pub rate_target: RateConvertTarget,
121    /// The sampling rate to convert.
122    pub converted_rate: RateConvertRate,
123    /// The mode of CD (44.1 kHz/16 bit sample).
124    pub cd_mode: bool,
125}
126
127/// Protocol implementation for converter parameters.
128#[derive(Default, Debug)]
129pub struct EnsembleConverterProtocol;
130
131impl EnsembleParametersConverter<EnsembleConverterParameters> for EnsembleConverterProtocol {
132    fn parse_cmds(params: &mut EnsembleConverterParameters, cmds: &[EnsembleCmd]) {
133        cmds.iter().for_each(|cmd| match cmd {
134            &EnsembleCmd::FormatConvert(format_target) => {
135                params.format_target = format_target;
136            }
137            &EnsembleCmd::RateConvert(rate_target, converted_rate) => {
138                params.rate_target = rate_target;
139                params.converted_rate = converted_rate;
140            }
141            &EnsembleCmd::Hw(HwCmd::CdMode(cd_mode)) => {
142                params.cd_mode = cd_mode;
143            }
144            _ => (),
145        });
146    }
147
148    fn build_cmds(params: &EnsembleConverterParameters) -> Vec<EnsembleCmd> {
149        vec![
150            EnsembleCmd::FormatConvert(params.format_target),
151            EnsembleCmd::RateConvert(params.rate_target, params.converted_rate),
152            EnsembleCmd::Hw(HwCmd::CdMode(params.cd_mode)),
153        ]
154    }
155}
156
157impl From<&EnsembleConverterParameters> for Vec<EnsembleCmd> {
158    fn from(params: &EnsembleConverterParameters) -> Self {
159        EnsembleConverterProtocol::build_cmds(params)
160    }
161}
162
163impl EnsembleParameterProtocol<EnsembleConverterParameters> for EnsembleConverterProtocol {}
164
165/// Parameters of display meters.
166#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
167pub struct EnsembleDisplayParameters {
168    /// Whether to enable/disable display.
169    pub enabled: bool,
170    /// Force to illuminate display.
171    pub illuminate: bool,
172    /// The target for meter display.
173    pub target: DisplayMeterTarget,
174    /// Whether to enable/disable overhold of peak detection.
175    pub overhold: bool,
176}
177
178/// Protocol implementation for display parameters.
179#[derive(Default, Debug)]
180pub struct EnsembleDisplayProtocol;
181
182impl EnsembleParametersConverter<EnsembleDisplayParameters> for EnsembleDisplayProtocol {
183    fn parse_cmds(params: &mut EnsembleDisplayParameters, cmds: &[EnsembleCmd]) {
184        cmds.iter().for_each(|cmd| match cmd {
185            &EnsembleCmd::Hw(HwCmd::DisplayMode(enabled)) => {
186                params.enabled = enabled;
187            }
188            &EnsembleCmd::Hw(HwCmd::DisplayIlluminate(illuminate)) => {
189                params.illuminate = illuminate;
190            }
191            &EnsembleCmd::Hw(HwCmd::DisplayTarget(target)) => {
192                params.target = target;
193            }
194            &EnsembleCmd::Hw(HwCmd::DisplayOverhold(overhold)) => {
195                params.overhold = overhold;
196            }
197            _ => (),
198        })
199    }
200
201    fn build_cmds(params: &EnsembleDisplayParameters) -> Vec<EnsembleCmd> {
202        vec![
203            EnsembleCmd::Hw(HwCmd::DisplayMode(params.enabled)),
204            EnsembleCmd::Hw(HwCmd::DisplayIlluminate(params.illuminate)),
205            EnsembleCmd::Hw(HwCmd::DisplayTarget(params.target)),
206            EnsembleCmd::Hw(HwCmd::DisplayOverhold(params.overhold)),
207        ]
208    }
209}
210
211impl From<&EnsembleDisplayParameters> for Vec<EnsembleCmd> {
212    fn from(params: &EnsembleDisplayParameters) -> Self {
213        EnsembleDisplayProtocol::build_cmds(params)
214    }
215}
216
217impl EnsembleParameterProtocol<EnsembleDisplayParameters> for EnsembleDisplayProtocol {}
218
219/// Parameters of analog/digital inputs. The gains, phantoms, and polarities parameters
220/// are available when channel 0-3 levels are for mic.
221#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
222pub struct EnsembleInputParameters {
223    /// Whether to enable/disable limitter of analog inputs.
224    pub limits: [bool; 8],
225    /// The nominal level of analog inputs.
226    pub levels: [InputNominalLevel; 8],
227    /// The gain of microphone inputs.
228    pub gains: [u8; 4],
229    /// Whether to enable/disable phantom powering for microphone inputs.
230    pub phantoms: [bool; 4],
231    /// Whether to invert polarity for microphone inputs.
232    pub polarities: [bool; 4],
233    /// The mode of optical inputs.
234    pub opt_iface_mode: OptIfaceMode,
235}
236
237/// Protocol implementation for input parameters.
238#[derive(Default, Debug)]
239pub struct EnsembleInputProtocol;
240
241impl EnsembleInputProtocol {
242    /// The maximum value of gain.
243    pub const GAIN_MIN: u8 = 0;
244
245    /// The minimum value of gain.
246    pub const GAIN_MAX: u8 = 75;
247
248    /// The step of value of gain.
249    pub const GAIN_STEP: u8 = 1;
250}
251
252impl EnsembleParametersConverter<EnsembleInputParameters> for EnsembleInputProtocol {
253    fn parse_cmds(params: &mut EnsembleInputParameters, cmds: &[EnsembleCmd]) {
254        cmds.iter().for_each(|cmd| match cmd {
255            &EnsembleCmd::InputLimit(i, limit) => {
256                if i < params.limits.len() {
257                    params.limits[i] = limit;
258                }
259            }
260            &EnsembleCmd::InputNominalLevel(i, level) => {
261                if i < params.levels.len() {
262                    params.levels[i] = level;
263                }
264            }
265            &EnsembleCmd::MicGain(i, gain) => {
266                if i < params.gains.len() {
267                    params.gains[i] = gain;
268                }
269            }
270            &EnsembleCmd::MicPower(i, phantom) => {
271                if i < params.phantoms.len() {
272                    params.phantoms[i] = phantom;
273                }
274            }
275            &EnsembleCmd::MicPolarity(i, polarity) => {
276                if i < params.polarities.len() {
277                    params.polarities[i] = polarity;
278                }
279            }
280            &EnsembleCmd::InputOptIface(opt_iface_mode) => {
281                params.opt_iface_mode = opt_iface_mode;
282            }
283            _ => (),
284        })
285    }
286
287    fn build_cmds(params: &EnsembleInputParameters) -> Vec<EnsembleCmd> {
288        let mut cmds = Vec::new();
289
290        params
291            .limits
292            .iter()
293            .enumerate()
294            .for_each(|(i, &limit)| cmds.push(EnsembleCmd::InputLimit(i, limit)));
295
296        params
297            .levels
298            .iter()
299            .enumerate()
300            .for_each(|(i, &level)| cmds.push(EnsembleCmd::InputNominalLevel(i, level)));
301
302        params
303            .gains
304            .iter()
305            .enumerate()
306            .for_each(|(i, &gain)| cmds.push(EnsembleCmd::MicGain(i, gain)));
307
308        params
309            .phantoms
310            .iter()
311            .enumerate()
312            .for_each(|(i, &phantom)| cmds.push(EnsembleCmd::MicPower(i, phantom)));
313
314        params
315            .polarities
316            .iter()
317            .enumerate()
318            .for_each(|(i, &polarity)| cmds.push(EnsembleCmd::MicPolarity(i, polarity)));
319
320        cmds.push(EnsembleCmd::InputOptIface(params.opt_iface_mode));
321
322        cmds
323    }
324}
325
326impl From<&EnsembleInputParameters> for Vec<EnsembleCmd> {
327    fn from(params: &EnsembleInputParameters) -> Self {
328        EnsembleInputProtocol::build_cmds(params)
329    }
330}
331
332impl EnsembleParameterProtocol<EnsembleInputParameters> for EnsembleInputProtocol {}
333
334/// Parameters of analog/digital outputs.
335#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
336pub struct EnsembleOutputParameters {
337    /// The volume of 1st pair of analog outputs.
338    pub vol: u8,
339    /// The volume of headphone outputs.
340    pub headphone_vols: [u8; 2],
341    /// The nominal level of outputs.
342    pub levels: [OutputNominalLevel; 8],
343    /// The mode of optical outputs.
344    pub opt_iface_mode: OptIfaceMode,
345}
346
347/// Protocol implementation for output parameters.
348#[derive(Default, Debug)]
349pub struct EnsembleOutputProtocol;
350
351impl EnsembleOutputProtocol {
352    /// The minimum value of volume.
353    pub const VOL_MIN: u8 = 0;
354
355    /// The maximum value of volume.
356    pub const VOL_MAX: u8 = 0x7f;
357
358    /// The step of value of volume.
359    pub const VOL_STEP: u8 = 1;
360
361    fn coef_from_vol(vol: u8) -> u8 {
362        Self::VOL_MAX - vol
363    }
364
365    fn coef_to_vol(coef: u8) -> u8 {
366        Self::VOL_MAX - coef
367    }
368}
369
370impl EnsembleParametersConverter<EnsembleOutputParameters> for EnsembleOutputProtocol {
371    fn parse_cmds(params: &mut EnsembleOutputParameters, cmds: &[EnsembleCmd]) {
372        cmds.iter().for_each(|cmd| match cmd {
373            &EnsembleCmd::OutVol(i, coef) => {
374                let vol = Self::coef_to_vol(coef);
375                match i {
376                    0 => params.vol = vol,
377                    1..=2 => {
378                        params.headphone_vols[i - 1] = vol;
379                    }
380                    _ => (),
381                }
382            }
383            &EnsembleCmd::OutputNominalLevel(i, level) => {
384                if i < params.levels.len() {
385                    params.levels[i] = level;
386                }
387            }
388            &EnsembleCmd::OutputOptIface(opt_iface_mode) => {
389                params.opt_iface_mode = opt_iface_mode;
390            }
391            _ => (),
392        })
393    }
394
395    fn build_cmds(params: &EnsembleOutputParameters) -> Vec<EnsembleCmd> {
396        let mut cmds = Vec::new();
397
398        [params.vol]
399            .iter()
400            .chain(&params.headphone_vols)
401            .enumerate()
402            .for_each(|(i, &vol)| cmds.push(EnsembleCmd::OutVol(i, Self::coef_from_vol(vol))));
403
404        params
405            .levels
406            .iter()
407            .enumerate()
408            .for_each(|(i, &level)| cmds.push(EnsembleCmd::OutputNominalLevel(i, level)));
409
410        cmds.push(EnsembleCmd::OutputOptIface(params.opt_iface_mode));
411
412        cmds
413    }
414}
415
416impl From<&EnsembleOutputParameters> for Vec<EnsembleCmd> {
417    fn from(params: &EnsembleOutputParameters) -> Self {
418        EnsembleOutputProtocol::build_cmds(params)
419    }
420}
421
422impl EnsembleParameterProtocol<EnsembleOutputParameters> for EnsembleOutputProtocol {}
423
424/// Parameters of input/output source.
425#[derive(Debug, Copy, Clone, PartialEq, Eq)]
426pub struct EnsembleSourceParameters {
427    /// To (18):
428    ///   analog-output-0, analog-output-1, analog-output-2, analog-output-3,
429    ///   analog-output-4, analog-output-5, analog-output-6, analog-output-7,
430    ///   spdif-output-0, spdif-output-1,
431    ///   adat-output-0, adat-output-1, adat-input-output-2, adat-output-3,
432    ///   adat-output-4, adat-output-5, adat-input-output-6, adat-output-7
433    /// From (40):
434    ///   analog-input-0, analog-input-1, analog-input-2, analog-input-3,
435    ///   analog-input-4, analog-input-5, analog-input-6, analog-input-7,
436    ///   stream-input-0, stream-input-1, stream-input-2, stream-input-3,
437    ///   stream-input-4, stream-input-5, stream-input-6, stream-input-7,
438    ///   stream-input-8, stream-input-9, stream-input-10, stream-input-11,
439    ///   stream-input-12, stream-input-13, stream-input-14, stream-input-15,
440    ///   stream-input-16, stream-input-17,
441    ///   spdif-input-0, spdif-input-1,
442    ///   adat-input-0, adat-input-1, adat-input-2, adat-input-3,
443    ///   adat-input-4, adat-input-5, adat-input-6, adat-input-7,
444    ///   mixer-output-1, mixer-output-2, mixer-output-3, mixer-output-4,
445    pub output_sources: [usize; 18],
446    /// To (18):
447    ///  stream-output-0, stream-output-1, stream-output-2, stream-output-3,
448    ///  stream-output-4, stream-output-5, stream-output-6, stream-output-7,
449    ///  stream-output-8, stream-output-9, stream-output-10, stream-output-11,
450    ///  stream-output-12, stream-output-13, stream-output-14, stream-output-15,
451    ///  stream-output-16, stream-output-17,
452    /// From(18):
453    ///  analog-input-0, analog-input-1, analog-input-2, analog-input-3,
454    ///  analog-input-4, analog-input-5, analog-input-6, analog-input-7,
455    ///  spdif-input-0, spdif-input-1,
456    ///  adat-input-0, adat-input-1, adat-input-2, adat-input-3,
457    ///  adat-input-4, adat-input-5, adat-input-6, adat-input-7,
458    pub capture_sources: [usize; 18],
459    /// From (6):
460    ///  analog-output-1/2, analog-output-3/4, analog-output-5/6, analog-output-7/8,
461    ///  spdif-output-1/2, none,
462    pub headphone_sources: [usize; 2],
463}
464
465impl Default for EnsembleSourceParameters {
466    fn default() -> Self {
467        let mut output_sources = [0; 18];
468        output_sources
469            .iter_mut()
470            .enumerate()
471            .for_each(|(i, v)| *v = i + 8);
472
473        let mut capture_sources = [0; 18];
474        capture_sources
475            .iter_mut()
476            .enumerate()
477            .for_each(|(i, v)| *v = i);
478
479        let mut headphone_sources = [0; 2];
480        headphone_sources
481            .iter_mut()
482            .enumerate()
483            .for_each(|(i, v)| *v = i);
484
485        EnsembleSourceParameters {
486            output_sources,
487            capture_sources,
488            headphone_sources,
489        }
490    }
491}
492
493/// Protocol implementation for source parameters.
494#[derive(Default, Debug)]
495pub struct EnsembleSourceProtocol;
496
497impl EnsembleSourceProtocol {
498    fn dst_id_from_out_idx(idx: usize) -> Option<usize> {
499        match idx {
500            0..=7 => Some(idx),
501            8..=17 => Some(idx + 18),
502            _ => None,
503        }
504    }
505
506    fn dst_id_to_out_idx(dst_id: usize) -> Option<usize> {
507        match dst_id {
508            0..=7 => Some(dst_id),
509            8..=25 => None,
510            26..=35 => Some(dst_id - 18),
511            _ => None,
512        }
513    }
514
515    fn dst_id_from_cap_idx(idx: usize) -> Option<usize> {
516        if idx < 18 {
517            Some(idx + 8)
518        } else {
519            None
520        }
521    }
522
523    fn dst_id_to_cap_idx(dst_id: usize) -> Option<usize> {
524        match dst_id {
525            0..=7 => None,
526            8..=25 => Some(dst_id - 8),
527            _ => None,
528        }
529    }
530
531    fn src_id_from_stream_idx(src_id: usize) -> Option<usize> {
532        match src_id {
533            0..=7 => Some(src_id),
534            8..=17 => Some(src_id + 18),
535            _ => None,
536        }
537    }
538
539    fn src_id_to_stream_idx(idx: usize) -> Option<usize> {
540        match idx {
541            0..=7 => Some(idx),
542            8..=25 => None,
543            26..=35 => Some(idx - 18),
544            _ => None,
545        }
546    }
547}
548
549impl EnsembleParametersConverter<EnsembleSourceParameters> for EnsembleSourceProtocol {
550    fn build_cmds(params: &EnsembleSourceParameters) -> Vec<EnsembleCmd> {
551        let mut cmds = Vec::new();
552
553        params
554            .output_sources
555            .iter()
556            .enumerate()
557            .for_each(|(out_idx, &src_id)| {
558                Self::dst_id_from_out_idx(out_idx).map(|dst_id| {
559                    cmds.push(EnsembleCmd::IoRouting(dst_id, src_id));
560                });
561            });
562
563        params
564            .capture_sources
565            .iter()
566            .enumerate()
567            .for_each(|(cap_idx, &stream_idx)| {
568                Self::dst_id_from_cap_idx(cap_idx).map(|dst_id| {
569                    Self::src_id_from_stream_idx(stream_idx).map(|src_id| {
570                        cmds.push(EnsembleCmd::IoRouting(dst_id, src_id));
571                    });
572                });
573            });
574
575        params
576            .headphone_sources
577            .iter()
578            .enumerate()
579            .for_each(|(dst_id, &src_id)| {
580                cmds.push(EnsembleCmd::HpSrc(dst_id, src_id));
581            });
582
583        cmds
584    }
585
586    fn parse_cmds(params: &mut EnsembleSourceParameters, cmds: &[EnsembleCmd]) {
587        cmds.iter().for_each(|cmd| match cmd {
588            &EnsembleCmd::IoRouting(dst_id, src_id) => {
589                if let Some(cap_idx) = Self::dst_id_to_cap_idx(dst_id) {
590                    Self::src_id_to_stream_idx(src_id).map(|stream_idx| {
591                        params.capture_sources[cap_idx] = stream_idx;
592                    });
593                } else if let Some(out_idx) = Self::dst_id_to_out_idx(dst_id) {
594                    params.output_sources[out_idx] = src_id;
595                }
596            }
597            &EnsembleCmd::HpSrc(dst_id, src_id) => {
598                if dst_id < params.headphone_sources.len() {
599                    params.headphone_sources[dst_id] = src_id;
600                }
601            }
602            _ => (),
603        })
604    }
605}
606impl From<&EnsembleSourceParameters> for Vec<EnsembleCmd> {
607    fn from(params: &EnsembleSourceParameters) -> Self {
608        EnsembleSourceProtocol::build_cmds(params)
609    }
610}
611
612impl EnsembleParameterProtocol<EnsembleSourceParameters> for EnsembleSourceProtocol {}
613
614/// Parameters of signal multiplexer.
615#[derive(Debug, Copy, Clone, PartialEq, Eq)]
616pub struct EnsembleMixerParameters {
617    /// To (4):
618    ///   mixer-output-0, mixer-output-1, mixer-output-2, mixer-output-3
619    ///
620    /// From (36):
621    /// analog-input-0, analog-input-1, analog-input-2, analog-input-3,
622    /// analog-input-4, analog-input-5, analog-input-6, analog-input-7,
623    /// stream-input-0, stream-input-1, stream-input-2, stream-input-3,
624    /// stream-input-4, stream-input-5, stream-input-6, stream-input-7,
625    /// stream-input-8,stream-input-9, stream-input-10, stream-input-11,
626    /// stream-input-12, stream-input-13, stream-input-14, stream-input-15,
627    /// stream-input-16, stream-input-17,
628    /// adat-input-0, adat-input-1, adat-input-2, adat-input-3,
629    /// adat-input-4, adat-input-5, adat-input-6, adat-input-7,
630    /// spdif-input-0, spdif-input-1,
631    pub src_gains: [[i16; 36]; 4],
632}
633
634impl Default for EnsembleMixerParameters {
635    fn default() -> Self {
636        let mut src_gains = [[0; 36]; 4];
637
638        src_gains.iter_mut().enumerate().for_each(|(i, gains)| {
639            gains
640                .iter_mut()
641                .enumerate()
642                .filter(|(j, _)| i % 2 == j % 2)
643                .for_each(|(_, gain)| *gain = EnsembleMixerProtocol::GAIN_MAX);
644        });
645        Self { src_gains }
646    }
647}
648
649/// Mixer protocol.
650#[derive(Default, Debug)]
651pub struct EnsembleMixerProtocol;
652
653impl EnsembleMixerProtocol {
654    /// The minimum value of gain.
655    pub const GAIN_MIN: i16 = 0;
656
657    /// The maximum value of gain.
658    pub const GAIN_MAX: i16 = 0xff;
659
660    /// The step of value of gain.
661    pub const GAIN_STEP: i16 = 0x01;
662
663    fn array_indices_to_cmd_indices(dst_idx: usize, src_idx: usize) -> (usize, usize, usize) {
664        let pair_idx = dst_idx / 2;
665        let target_idx = src_idx / 9;
666        let coef_idx = (dst_idx % 2) + (src_idx % 9) * 2;
667
668        (pair_idx, target_idx, coef_idx)
669    }
670
671    fn array_indices_from_cmd_indices(
672        pair_idx: usize,
673        target_idx: usize,
674        coef_idx: usize,
675    ) -> (usize, usize) {
676        let dst_idx = pair_idx * 2 + coef_idx % 2;
677        let src_idx = target_idx * 9 + coef_idx / 2;
678
679        (dst_idx, src_idx)
680    }
681
682    fn partial_update_by_cmd_content(
683        params: &mut EnsembleMixerParameters,
684        pair_idx: usize,
685        target_idx: usize,
686        gains: &[i16],
687    ) {
688        assert!(pair_idx < 2);
689        assert!(target_idx < 4);
690        assert_eq!(gains.len(), MIXER_COEFFICIENT_COUNT);
691
692        gains.iter().enumerate().for_each(|(coef_idx, &gain)| {
693            let (dst_idx, src_idx) =
694                Self::array_indices_from_cmd_indices(pair_idx, target_idx, coef_idx);
695            params.src_gains[dst_idx][src_idx] = gain;
696        });
697    }
698}
699
700// NOTE:
701//
702// Array layout:
703//
704//                src_idx 0-35
705//              +--------------------------------+
706//    dst_idx 0 |   A (L)  B (L)  C (L)  D (L)   |
707//              +--------------------------------+
708//    dst_idx 1 |   A (R)  B (R)  C (R)  D (R)   |
709//              +--------------------------------+
710//    dst_idx 2 |   E (L)  F (L)  G (L)  H (L)   |
711//              +--------------------------------+
712//    dst_idx 3 |   E (R)  F (R)  G (R)  H (R)   |
713//              +--------------------------------+
714//
715// Command content layout:
716//
717//                 pair_idx 0        pair_idx 1
718//               coef_idx 0-18     coef_idx 0-18
719//              +--------------+  +--------------+
720// target_idx 0 |  A (LRLR..)  |  |  E (LRLR..)  |
721//              +--------------+  +--------------+
722// target_idx 1 |  B (LRLR..)  |  |  F (LRLR..)  |
723//              +--------------+  +--------------+
724// target_idx 2 |  C (LRLR..)  |  |  G (LRLR..)  |
725//              +--------------+  +--------------+
726// target_idx 3 |  D (LRLR..)  |  |  H (LRLR..)  |
727//              +--------------+  +--------------+
728//
729impl EnsembleParametersConverter<EnsembleMixerParameters> for EnsembleMixerProtocol {
730    fn build_cmds(params: &EnsembleMixerParameters) -> Vec<EnsembleCmd> {
731        let mut cmds = Vec::new();
732
733        (0..2).for_each(|pair_idx| {
734            let mut src0_gains = [0; MIXER_COEFFICIENT_COUNT];
735            let mut src1_gains = [0; MIXER_COEFFICIENT_COUNT];
736            let mut src2_gains = [0; MIXER_COEFFICIENT_COUNT];
737            let mut src3_gains = [0; MIXER_COEFFICIENT_COUNT];
738
739            params
740                .src_gains
741                .iter()
742                .skip(pair_idx * 2)
743                .take(2)
744                .enumerate()
745                .for_each(|(idx, gains)| {
746                    let dst_idx = pair_idx * 2 + idx;
747                    gains.iter().enumerate().for_each(|(src_idx, &gain)| {
748                        let (_, target_idx, coef_idx) =
749                            Self::array_indices_to_cmd_indices(dst_idx, src_idx);
750                        let src_gains = match target_idx {
751                            0 => &mut src0_gains,
752                            1 => &mut src1_gains,
753                            2 => &mut src2_gains,
754                            3 => &mut src3_gains,
755                            _ => unreachable!(),
756                        };
757                        src_gains[coef_idx] = gain;
758                    });
759                });
760
761            cmds.push(EnsembleCmd::MixerSrc0(pair_idx, src0_gains));
762            cmds.push(EnsembleCmd::MixerSrc1(pair_idx, src1_gains));
763            cmds.push(EnsembleCmd::MixerSrc2(pair_idx, src2_gains));
764            cmds.push(EnsembleCmd::MixerSrc3(pair_idx, src3_gains));
765        });
766
767        cmds
768    }
769
770    fn parse_cmds(params: &mut EnsembleMixerParameters, cmds: &[EnsembleCmd]) {
771        cmds.iter().for_each(|cmd| match cmd {
772            EnsembleCmd::MixerSrc0(pair_idx, gains) => {
773                Self::partial_update_by_cmd_content(params, *pair_idx, 0, gains);
774            }
775            EnsembleCmd::MixerSrc1(pair_idx, gains) => {
776                Self::partial_update_by_cmd_content(params, *pair_idx, 1, gains);
777            }
778            EnsembleCmd::MixerSrc2(pair_idx, gains) => {
779                Self::partial_update_by_cmd_content(params, *pair_idx, 2, gains);
780            }
781            EnsembleCmd::MixerSrc3(pair_idx, gains) => {
782                Self::partial_update_by_cmd_content(params, *pair_idx, 3, gains);
783            }
784            _ => (),
785        });
786    }
787}
788impl From<&EnsembleMixerParameters> for Vec<EnsembleCmd> {
789    fn from(params: &EnsembleMixerParameters) -> Self {
790        EnsembleMixerProtocol::build_cmds(params)
791    }
792}
793
794impl EnsembleParameterProtocol<EnsembleMixerParameters> for EnsembleMixerProtocol {}
795
796/// Parameters of stream mode.
797#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
798pub struct EnsembleStreamParameters {
799    /// The mode of isochronous stream in IEEE 1394 bus.
800    pub mode: StreamMode,
801}
802
803/// Protocol implementation for stream mode parameters.
804#[derive(Default, Debug)]
805pub struct EnsembleStreamProtocol;
806
807impl EnsembleParametersConverter<EnsembleStreamParameters> for EnsembleStreamProtocol {
808    fn build_cmds(params: &EnsembleStreamParameters) -> Vec<EnsembleCmd> {
809        vec![EnsembleCmd::Hw(HwCmd::StreamMode(params.mode))]
810    }
811
812    fn parse_cmds(params: &mut EnsembleStreamParameters, cmds: &[EnsembleCmd]) {
813        cmds.iter().for_each(|cmd| match cmd {
814            &EnsembleCmd::Hw(HwCmd::StreamMode(mode)) => {
815                params.mode = mode;
816            }
817            _ => (),
818        });
819    }
820}
821
822impl From<&EnsembleStreamParameters> for Vec<EnsembleCmd> {
823    fn from(params: &EnsembleStreamParameters) -> Self {
824        EnsembleStreamProtocol::build_cmds(params)
825    }
826}
827
828impl EnsembleParameterProtocol<EnsembleStreamParameters> for EnsembleStreamProtocol {
829    fn whole_update(
830        avc: &BebobAvc,
831        params: &mut EnsembleStreamParameters,
832        timeout_ms: u32,
833    ) -> Result<(), Error> {
834        let plug_addr =
835            BcoPlugAddr::new_for_unit(BcoPlugDirection::Output, BcoPlugAddrUnitType::Isoc, 0);
836        let mut op = ExtendedStreamFormatSingle::new(&plug_addr);
837        avc.status(&AvcAddr::Unit, &mut op, timeout_ms)?;
838
839        let info = op
840            .stream_format
841            .as_bco_compound_am824_stream()
842            .ok_or_else(|| {
843                let label = "Bco Compound AM824 stream is not available for the unit";
844                Error::new(FileError::Nxio, &label)
845            })?;
846        let count = info
847            .entries
848            .iter()
849            .filter(|entry| entry.format == BcoCompoundAm824StreamFormat::MultiBitLinearAudioRaw)
850            .fold(0, |count, entry| count + entry.count as usize);
851        params.mode = match count {
852            18 => StreamMode::Format18x18,
853            10 => StreamMode::Format10x10,
854            _ => StreamMode::Format8x8,
855        };
856
857        Ok(())
858    }
859}
860
861/// The converter between parameters and specific commands.
862pub trait EnsembleParametersConverter<T: Copy> {
863    /// Parse the given vector of commands for parameters.
864    fn parse_cmds(params: &mut T, cmds: &[EnsembleCmd]);
865    /// Build vector of commands by the given parameters.
866    fn build_cmds(params: &T) -> Vec<EnsembleCmd>;
867}
868
869/// The trait for parameter protocol.
870pub trait EnsembleParameterProtocol<T: Copy>: EnsembleParametersConverter<T> {
871    /// Update the hardware for all of the parameters
872    fn whole_update(avc: &BebobAvc, params: &mut T, timeout_ms: u32) -> Result<(), Error> {
873        Self::build_cmds(params).into_iter().try_for_each(|cmd| {
874            let mut op = EnsembleOperation::new(cmd);
875            avc.control(&AvcAddr::Unit, &mut op, timeout_ms)
876        })
877    }
878
879    /// Update the hardware for the part of parameters.
880    fn partial_update(avc: &BebobAvc, new: &T, old: &mut T, timeout_ms: u32) -> Result<(), Error> {
881        Self::build_cmds(new)
882            .into_iter()
883            .zip(Self::build_cmds(old))
884            .filter(|(n, o)| !n.eq(o))
885            .try_for_each(|(n, _)| {
886                let mut op = EnsembleOperation::new(n);
887                avc.control(&AvcAddr::Unit, &mut op, timeout_ms)
888            })
889            .map(|_| *old = *new)
890    }
891}
892
893/// The target of input for knob.
894#[derive(Debug, Copy, Clone, PartialEq, Eq)]
895pub enum KnobInputTarget {
896    /// 1st microphone.
897    Mic0,
898    /// 2nd microphone.
899    Mic1,
900    /// 3rd microphone.
901    Mic2,
902    /// 4th microphone.
903    Mic3,
904}
905
906impl Default for KnobInputTarget {
907    fn default() -> Self {
908        Self::Mic1
909    }
910}
911
912/// The target of output for knob.
913#[derive(Debug, Copy, Clone, PartialEq, Eq)]
914pub enum KnobOutputTarget {
915    /// 1st pair of analog outputs.
916    AnalogOutputPair0,
917    /// 1st pair of headphone outputs.
918    HeadphonePair0,
919    /// 2nd pair of headphone outputs.
920    HeadphonePair1,
921}
922
923impl Default for KnobOutputTarget {
924    fn default() -> Self {
925        Self::AnalogOutputPair0
926    }
927}
928
929/// Information of hardware metering.
930#[derive(Debug, Copy, Clone, PartialEq, Eq)]
931pub struct EnsembleMeter {
932    /// The target of hardware input knob.
933    pub knob_input_target: KnobInputTarget,
934    /// The target of hardware output knob.
935    pub knob_output_target: KnobOutputTarget,
936    /// The value of hardware input knob.
937    pub knob_input_vals: [u8; 4],
938    /// The value of hardware output knob.
939    pub knob_output_vals: [u8; 3],
940    /// The detected signal level of inputs.
941    pub phys_inputs: [u8; 18],
942    /// The detected signal level of outputs.
943    pub phys_outputs: [u8; 16],
944}
945
946impl Default for EnsembleMeter {
947    fn default() -> Self {
948        Self {
949            knob_input_target: Default::default(),
950            knob_output_target: Default::default(),
951            knob_input_vals: Default::default(),
952            knob_output_vals: Default::default(),
953            phys_inputs: [0; 18],
954            phys_outputs: [0; 16],
955        }
956    }
957}
958
959/// The protocol implementation for meter information.
960#[derive(Default, Debug)]
961pub struct EnsembleMeterProtocol;
962
963const KNOB_IN_TARGET_MASK: u8 = 0x03;
964const KNOB_IN_TARGET_SHIFT: usize = 3;
965
966const KNOB_OUT_TARGET_MASK: u8 = 0x07;
967const KNOB_OUT_TARGET_SHIFT: usize = 0;
968
969// 33-34: mixer-out-3/4
970// 35: unknown
971// 36-52: stream-in-0..16, missing 17
972const SELECT_POS: usize = 4;
973const IN_GAIN_POS: [usize; 4] = [0, 1, 2, 3];
974const OUT_VOL_POS: [usize; 3] = [7, 6, 5];
975const IN_METER_POS: [usize; 18] = [
976    12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
977];
978const OUT_METER_POS: [usize; 16] = [
979    35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
980];
981
982/// Protocol implementation for hardware metering.
983impl EnsembleMeterProtocol {
984    /// The minimum value of hardware output knob.
985    pub const OUT_KNOB_VAL_MIN: u8 = EnsembleOutputProtocol::VOL_MIN;
986    /// The maximum value of hardware output knob.
987    pub const OUT_KNOB_VAL_MAX: u8 = EnsembleOutputProtocol::VOL_MAX;
988    /// The step value of hardware output knob.
989    pub const OUT_KNOB_VAL_STEP: u8 = EnsembleOutputProtocol::VOL_STEP;
990
991    /// The minimum value of hardware input knob.
992    pub const IN_KNOB_VAL_MIN: u8 = EnsembleInputProtocol::GAIN_MIN;
993    /// The maximum value of hardware input knob.
994    pub const IN_KNOB_VAL_MAX: u8 = EnsembleInputProtocol::GAIN_MAX;
995    /// The step value of hardware input knob.
996    pub const IN_KNOB_VAL_STEP: u8 = EnsembleInputProtocol::GAIN_STEP;
997
998    /// The minimum value of detected signal level.
999    pub const LEVEL_MIN: u8 = u8::MIN;
1000    /// The maximum value of detected signal level.
1001    pub const LEVEL_MAX: u8 = u8::MAX;
1002    /// The step value of detected signal level.
1003    pub const LEVEL_STEP: u8 = 1;
1004
1005    /// Update the given parameters by the state of hardware.
1006    pub fn whole_update(
1007        avc: &BebobAvc,
1008        meter: &mut EnsembleMeter,
1009        timeout_ms: u32,
1010    ) -> Result<(), Error> {
1011        let cmd = EnsembleCmd::HwStatusLong([0; METER_LONG_FRAME_SIZE]);
1012        let mut op = EnsembleOperation::new(cmd);
1013        avc.control(&AvcAddr::Unit, &mut op, timeout_ms).map(|_| {
1014            if let EnsembleCmd::HwStatusLong(frame) = &op.cmd {
1015                let val = (frame[SELECT_POS] >> KNOB_IN_TARGET_SHIFT) & KNOB_IN_TARGET_MASK;
1016                meter.knob_input_target = match val & 0x03 {
1017                    3 => KnobInputTarget::Mic3,
1018                    2 => KnobInputTarget::Mic2,
1019                    1 => KnobInputTarget::Mic1,
1020                    _ => KnobInputTarget::Mic0,
1021                };
1022
1023                let val = (frame[SELECT_POS] >> KNOB_OUT_TARGET_SHIFT) & KNOB_OUT_TARGET_MASK;
1024                meter.knob_output_target = match val {
1025                    4 => KnobOutputTarget::HeadphonePair1,
1026                    2 => KnobOutputTarget::HeadphonePair0,
1027                    _ => KnobOutputTarget::AnalogOutputPair0,
1028                };
1029
1030                IN_GAIN_POS
1031                    .iter()
1032                    .zip(&mut meter.knob_input_vals)
1033                    .for_each(|(&i, m)| *m = frame[i]);
1034
1035                OUT_VOL_POS
1036                    .iter()
1037                    .zip(&mut meter.knob_output_vals)
1038                    .for_each(|(&i, m)| {
1039                        *m = Self::OUT_KNOB_VAL_MAX - (frame[i] & Self::OUT_KNOB_VAL_MAX);
1040                    });
1041
1042                IN_METER_POS
1043                    .iter()
1044                    .zip(&mut meter.phys_inputs)
1045                    .for_each(|(&i, m)| *m = frame[i]);
1046
1047                OUT_METER_POS
1048                    .iter()
1049                    .zip(&mut meter.phys_outputs)
1050                    .for_each(|(&i, m)| *m = frame[i]);
1051            }
1052        })
1053    }
1054}
1055
1056/// The nominal level of analog input 0-7.
1057#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1058pub enum InputNominalLevel {
1059    /// +4 dBu.
1060    Professional,
1061    /// -10 dBV.
1062    Consumer,
1063    /// Widely adjustable with preamp. Available only for channel 0-3.
1064    Microphone,
1065}
1066
1067impl Default for InputNominalLevel {
1068    fn default() -> Self {
1069        Self::Professional
1070    }
1071}
1072
1073/// The nominal level of analog ouput 0-7.
1074#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1075pub enum OutputNominalLevel {
1076    /// +4 dBu.
1077    Professional,
1078    /// -10 dBV.
1079    Consumer,
1080}
1081
1082impl Default for OutputNominalLevel {
1083    fn default() -> Self {
1084        Self::Professional
1085    }
1086}
1087
1088/// The mode of signal in optical interface.
1089#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1090pub enum OptIfaceMode {
1091    Spdif,
1092    Adat,
1093}
1094
1095impl Default for OptIfaceMode {
1096    fn default() -> Self {
1097        Self::Spdif
1098    }
1099}
1100
1101fn opt_iface_mode_to_val(mode: &OptIfaceMode) -> u8 {
1102    if mode.eq(&OptIfaceMode::Adat) {
1103        1
1104    } else {
1105        0
1106    }
1107}
1108
1109fn opt_iface_mode_from_val(val: u8) -> OptIfaceMode {
1110    if val > 0 {
1111        OptIfaceMode::Adat
1112    } else {
1113        OptIfaceMode::Spdif
1114    }
1115}
1116
1117/// The target to convert sample format from 24 bit to 16 bit.
1118#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1119pub enum FormatConvertTarget {
1120    /// Disabled.
1121    Disabled,
1122    /// 1st pair of analog inputs.
1123    AnalogInputPair0,
1124    /// 2nd pair of analog inputs.
1125    AnalogInputPair1,
1126    /// 3rd pair of analog inputs.
1127    AnalogInputPair2,
1128    /// 4th pair of analog inputs.
1129    AnalogInputPair3,
1130    /// The pair of S/PDIF input in optical interface.
1131    SpdifOpticalInputPair0,
1132    /// The pair of S/PDIF input in coaxial interface.
1133    SpdifCoaxialInputPair0,
1134    /// The pair of S/PDIF output in optical interface.
1135    SpdifCoaxialOutputPair0,
1136    /// The pair of S/PDIF output in coaxial interface.
1137    SpdifOpticalOutputPair0,
1138}
1139
1140impl Default for FormatConvertTarget {
1141    fn default() -> Self {
1142        Self::Disabled
1143    }
1144}
1145
1146/// The target to convert sample rate.
1147#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1148pub enum RateConvertTarget {
1149    /// Disabled.
1150    Disabled,
1151    /// The pair of S/PDIF output in optical interface.
1152    SpdifOpticalOutputPair0,
1153    /// The pair of S/PDIF output in coaxial interface.
1154    SpdifCoaxialOutputPair0,
1155    /// The pair of S/PDIF input in optical interface.
1156    SpdifOpticalInputPair0,
1157    /// The pair of S/PDIF input in coaxial interface.
1158    SpdifCoaxialInputPair0,
1159}
1160
1161impl Default for RateConvertTarget {
1162    fn default() -> Self {
1163        Self::Disabled
1164    }
1165}
1166
1167/// The rate to convert sample rate.
1168#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1169pub enum RateConvertRate {
1170    /// To 44.1 kHz.
1171    R44100,
1172    /// To 48.0 kHz.
1173    R48000,
1174    /// To 88.2 kHz.
1175    R88200,
1176    /// To 96.0 kHz.
1177    R96000,
1178    /// To 176.0 kHz.
1179    R176400,
1180    /// To 192.0 kHz.
1181    R192000,
1182}
1183
1184impl Default for RateConvertRate {
1185    fn default() -> Self {
1186        Self::R44100
1187    }
1188}
1189
1190const METER_SHORT_FRAME_SIZE: usize = 17;
1191const METER_LONG_FRAME_SIZE: usize = 56;
1192const MIXER_COEFFICIENT_COUNT: usize = 18;
1193
1194/// Command specific to Apogee Ensemble.
1195#[derive(Debug, Clone, Eq, PartialEq)]
1196pub enum EnsembleCmd {
1197    /// Limitter for 8 analog inputs.
1198    InputLimit(usize, bool), // index, state
1199    /// Phantom powering for 4 microphone inputs.
1200    MicPower(usize, bool), // index, state
1201    /// The nominal level of 8 analog inputs.
1202    InputNominalLevel(usize, InputNominalLevel),
1203    /// The nominal level of 8 analog outputs.
1204    OutputNominalLevel(usize, OutputNominalLevel),
1205    /// The routing between source and destination.
1206    IoRouting(usize, usize), // destination, source
1207    /// Hardware type of command.
1208    Hw(HwCmd),
1209    /// The source of 4 headphone outputs.
1210    HpSrc(usize, usize), // destination, source
1211    /// The source of 1st nine pairs of coefficients for two pairs of mixers.
1212    MixerSrc0(usize, [i16; MIXER_COEFFICIENT_COUNT]),
1213    /// The source of 2nd nine pairs of coefficients for two pairs of mixers.
1214    MixerSrc1(usize, [i16; MIXER_COEFFICIENT_COUNT]),
1215    /// The source of 3rd nine pairs of coefficients for two pairs of mixers.
1216    MixerSrc2(usize, [i16; MIXER_COEFFICIENT_COUNT]),
1217    /// The source of 4th nine pairs of coefficients for two pairs of mixers.
1218    MixerSrc3(usize, [i16; MIXER_COEFFICIENT_COUNT]),
1219    /// The gain of 4 microphone inputs, between 10 and 75.
1220    MicGain(usize, u8), // 1/2/3/4, dB(10-75), also available as knob control
1221    /// The mode of output in optical interface.
1222    OutputOptIface(OptIfaceMode),
1223    /// The mode of input in optical interface.
1224    InputOptIface(OptIfaceMode),
1225    /// The target of converter for sample format.
1226    FormatConvert(FormatConvertTarget),
1227    /// The parameters of sampling rate converter.
1228    RateConvert(RateConvertTarget, RateConvertRate),
1229    /// The polarity for 4 microphone inputs.
1230    MicPolarity(usize, bool), // index, state
1231    /// The volume of output.
1232    OutVol(usize, u8), // main/hp0/hp1, dB(127-0), also available as knob control
1233    /// The short expression of hardware status.
1234    HwStatusShort([u8; METER_SHORT_FRAME_SIZE]),
1235    /// The long expression of hardware status.
1236    HwStatusLong([u8; METER_LONG_FRAME_SIZE]),
1237    Reserved(Vec<u8>),
1238}
1239
1240impl Default for EnsembleCmd {
1241    fn default() -> Self {
1242        Self::Reserved(Vec::new())
1243    }
1244}
1245
1246impl EnsembleCmd {
1247    const INPUT_LIMIT: u8 = 0xe4;
1248    const MIC_POWER: u8 = 0xe5;
1249    const IO_NOMINAL_LEVEL: u8 = 0xe8;
1250    const IO_ROUTING: u8 = 0xef;
1251    const HW: u8 = 0xeb;
1252    const HP_SRC: u8 = 0xab;
1253    const MIXER_SRC0: u8 = 0xb0;
1254    const MIXER_SRC1: u8 = 0xb1;
1255    const MIXER_SRC2: u8 = 0xb2;
1256    const MIXER_SRC3: u8 = 0xb3;
1257    const MIC_GAIN: u8 = 0xe6;
1258    const OPT_IFACE_MODE: u8 = 0xf1;
1259    const FORMAT_CONVERT: u8 = 0xf2;
1260    const RATE_CONVERT: u8 = 0xf3;
1261    const MIC_POLARITY: u8 = 0xf5;
1262    const OUT_VOL: u8 = 0xf6;
1263    const HW_STATUS: u8 = 0xff;
1264}
1265
1266impl From<&EnsembleCmd> for Vec<u8> {
1267    fn from(cmd: &EnsembleCmd) -> Self {
1268        match cmd {
1269            EnsembleCmd::InputLimit(ch, state) => {
1270                vec![EnsembleCmd::INPUT_LIMIT, *ch as u8, *state as u8]
1271            }
1272            EnsembleCmd::MicPower(ch, state) => {
1273                vec![EnsembleCmd::MIC_POWER, *ch as u8, *state as u8]
1274            }
1275            EnsembleCmd::InputNominalLevel(ch, state) => {
1276                let val = match state {
1277                    InputNominalLevel::Professional => 0,
1278                    InputNominalLevel::Consumer => 1,
1279                    InputNominalLevel::Microphone => 2,
1280                };
1281                vec![EnsembleCmd::IO_NOMINAL_LEVEL, *ch as u8, 0x01, val]
1282            }
1283            EnsembleCmd::OutputNominalLevel(ch, state) => {
1284                let val = match state {
1285                    OutputNominalLevel::Professional => 0,
1286                    OutputNominalLevel::Consumer => 1,
1287                };
1288                vec![EnsembleCmd::IO_NOMINAL_LEVEL, *ch as u8, 0x00, val]
1289            }
1290            EnsembleCmd::IoRouting(dst, src) => {
1291                vec![EnsembleCmd::IO_ROUTING, *dst as u8, *src as u8]
1292            }
1293            EnsembleCmd::Hw(op) => {
1294                let mut params = Into::<Vec<u8>>::into(op);
1295                params.insert(0, EnsembleCmd::HW);
1296                params
1297            }
1298            EnsembleCmd::HpSrc(dst, src) => {
1299                vec![
1300                    EnsembleCmd::HP_SRC,
1301                    (1 + *dst as u8) % 2,
1302                    (1 + 2 * *src as u8),
1303                ]
1304            }
1305            EnsembleCmd::MixerSrc0(pair, coefs) => {
1306                let mut data = Vec::with_capacity(2 + 2 * MIXER_COEFFICIENT_COUNT);
1307                data.push(EnsembleCmd::MIXER_SRC0);
1308                data.push(*pair as u8);
1309                coefs
1310                    .iter()
1311                    .for_each(|coef| data.extend_from_slice(&coef.to_be_bytes()));
1312                data
1313            }
1314            EnsembleCmd::MixerSrc1(pair, coefs) => {
1315                let mut data = Vec::with_capacity(2 + 2 * MIXER_COEFFICIENT_COUNT);
1316                data.push(EnsembleCmd::MIXER_SRC1);
1317                data.push(*pair as u8);
1318                coefs
1319                    .iter()
1320                    .for_each(|coef| data.extend_from_slice(&coef.to_be_bytes()));
1321                data
1322            }
1323            EnsembleCmd::MixerSrc2(pair, coefs) => {
1324                let mut data = Vec::with_capacity(2 + 2 * MIXER_COEFFICIENT_COUNT);
1325                data.push(EnsembleCmd::MIXER_SRC2);
1326                data.push(*pair as u8);
1327                coefs
1328                    .iter()
1329                    .for_each(|coef| data.extend_from_slice(&coef.to_be_bytes()));
1330                data
1331            }
1332            EnsembleCmd::MixerSrc3(pair, coefs) => {
1333                let mut data = Vec::with_capacity(2 + 2 * MIXER_COEFFICIENT_COUNT);
1334                data.push(EnsembleCmd::MIXER_SRC3);
1335                data.push(*pair as u8);
1336                coefs
1337                    .iter()
1338                    .for_each(|coef| data.extend_from_slice(&coef.to_be_bytes()));
1339                data
1340            }
1341            EnsembleCmd::MicGain(target, val) => {
1342                vec![EnsembleCmd::MIC_GAIN, *target as u8, *val]
1343            }
1344            EnsembleCmd::OutputOptIface(mode) => {
1345                vec![EnsembleCmd::OPT_IFACE_MODE, 0, opt_iface_mode_to_val(mode)]
1346            }
1347            EnsembleCmd::InputOptIface(mode) => {
1348                vec![EnsembleCmd::OPT_IFACE_MODE, 1, opt_iface_mode_to_val(mode)]
1349            }
1350            EnsembleCmd::FormatConvert(state) => {
1351                let val = match state {
1352                    FormatConvertTarget::Disabled => 0,
1353                    FormatConvertTarget::AnalogInputPair0 => 1,
1354                    FormatConvertTarget::AnalogInputPair1 => 2,
1355                    FormatConvertTarget::AnalogInputPair2 => 3,
1356                    FormatConvertTarget::AnalogInputPair3 => 4,
1357                    FormatConvertTarget::SpdifOpticalInputPair0 => 5,
1358                    FormatConvertTarget::SpdifCoaxialInputPair0 => 6,
1359                    FormatConvertTarget::SpdifCoaxialOutputPair0 => 7,
1360                    FormatConvertTarget::SpdifOpticalOutputPair0 => 8,
1361                };
1362                vec![EnsembleCmd::FORMAT_CONVERT, val]
1363            }
1364            EnsembleCmd::RateConvert(target, rate) => {
1365                let triplet = match target {
1366                    RateConvertTarget::Disabled => [0, 0, 0],
1367                    RateConvertTarget::SpdifOpticalOutputPair0 => [1, 0, 0],
1368                    RateConvertTarget::SpdifCoaxialOutputPair0 => [1, 1, 0],
1369                    RateConvertTarget::SpdifOpticalInputPair0 => [1, 0, 1],
1370                    RateConvertTarget::SpdifCoaxialInputPair0 => [1, 1, 1],
1371                };
1372                let val = match rate {
1373                    RateConvertRate::R44100 => 0,
1374                    RateConvertRate::R48000 => 1,
1375                    RateConvertRate::R88200 => 2,
1376                    RateConvertRate::R96000 => 3,
1377                    RateConvertRate::R176400 => 4,
1378                    RateConvertRate::R192000 => 5,
1379                };
1380                vec![
1381                    EnsembleCmd::RATE_CONVERT,
1382                    triplet[0],
1383                    triplet[1],
1384                    triplet[2],
1385                    val,
1386                ]
1387            }
1388            EnsembleCmd::MicPolarity(ch, state) => {
1389                vec![EnsembleCmd::MIC_POLARITY, *ch as u8, *state as u8]
1390            }
1391            EnsembleCmd::OutVol(target, vol) => {
1392                vec![EnsembleCmd::OUT_VOL, *target as u8, *vol]
1393            }
1394            EnsembleCmd::HwStatusShort(_) => vec![EnsembleCmd::HW_STATUS, 0],
1395            EnsembleCmd::HwStatusLong(_) => vec![EnsembleCmd::HW_STATUS, 1],
1396            EnsembleCmd::Reserved(r) => r.to_vec(),
1397        }
1398    }
1399}
1400
1401impl From<&[u8]> for EnsembleCmd {
1402    fn from(raw: &[u8]) -> Self {
1403        match raw[0] {
1404            Self::INPUT_LIMIT => Self::InputLimit(raw[1] as usize, raw[2] > 0),
1405            Self::MIC_POWER => Self::MicPower(raw[1] as usize, raw[2] > 0),
1406            Self::IO_NOMINAL_LEVEL => {
1407                if raw[2] > 0 {
1408                    let state = match raw[3] {
1409                        2 => InputNominalLevel::Microphone,
1410                        1 => InputNominalLevel::Consumer,
1411                        _ => InputNominalLevel::Professional,
1412                    };
1413                    Self::InputNominalLevel(raw[1] as usize, state)
1414                } else {
1415                    let state = match raw[3] {
1416                        1 => OutputNominalLevel::Consumer,
1417                        _ => OutputNominalLevel::Professional,
1418                    };
1419                    Self::OutputNominalLevel(raw[1] as usize, state)
1420                }
1421            }
1422            Self::IO_ROUTING => Self::IoRouting(raw[1] as usize, raw[2] as usize),
1423            Self::HW => Self::Hw(HwCmd::from(&raw[1..])),
1424            Self::HP_SRC => Self::HpSrc((1 + raw[1] as usize) % 2, (raw[2] as usize) / 2),
1425            Self::MIXER_SRC0 => {
1426                let mut doublet = [0; 2];
1427                let mut coefs = [0; MIXER_COEFFICIENT_COUNT];
1428                coefs.iter_mut().enumerate().for_each(|(i, coef)| {
1429                    let pos = 2 + i * 2;
1430                    doublet.copy_from_slice(&raw[pos..(pos + 2)]);
1431                    *coef = i16::from_be_bytes(doublet);
1432                });
1433                Self::MixerSrc0(raw[1] as usize, coefs)
1434            }
1435            Self::MIXER_SRC1 => {
1436                let mut doublet = [0; 2];
1437                let mut coefs = [0; MIXER_COEFFICIENT_COUNT];
1438                coefs.iter_mut().enumerate().for_each(|(i, coef)| {
1439                    let pos = 2 + i * 2;
1440                    doublet.copy_from_slice(&raw[pos..(pos + 2)]);
1441                    *coef = i16::from_be_bytes(doublet);
1442                });
1443                Self::MixerSrc1(raw[1] as usize, coefs)
1444            }
1445            Self::MIXER_SRC2 => {
1446                let mut doublet = [0; 2];
1447                let mut coefs = [0; MIXER_COEFFICIENT_COUNT];
1448                coefs.iter_mut().enumerate().for_each(|(i, coef)| {
1449                    let pos = 2 + i * 2;
1450                    doublet.copy_from_slice(&raw[pos..(pos + 2)]);
1451                    *coef = i16::from_be_bytes(doublet);
1452                });
1453                Self::MixerSrc2(raw[1] as usize, coefs)
1454            }
1455            Self::MIXER_SRC3 => {
1456                let mut doublet = [0; 2];
1457                let mut coefs = [0; MIXER_COEFFICIENT_COUNT];
1458                coefs.iter_mut().enumerate().for_each(|(i, coef)| {
1459                    let pos = 2 + i * 2;
1460                    doublet.copy_from_slice(&raw[pos..(pos + 2)]);
1461                    *coef = i16::from_be_bytes(doublet);
1462                });
1463                Self::MixerSrc3(raw[1] as usize, coefs)
1464            }
1465            Self::MIC_GAIN => Self::MicGain(raw[1] as usize, raw[2]),
1466            Self::OPT_IFACE_MODE => {
1467                let mode = opt_iface_mode_from_val(raw[2]);
1468                if raw[1] > 0 {
1469                    Self::InputOptIface(mode)
1470                } else {
1471                    Self::OutputOptIface(mode)
1472                }
1473            }
1474            Self::FORMAT_CONVERT => {
1475                let target = match raw[1] {
1476                    8 => FormatConvertTarget::SpdifOpticalOutputPair0,
1477                    7 => FormatConvertTarget::SpdifCoaxialOutputPair0,
1478                    6 => FormatConvertTarget::SpdifCoaxialInputPair0,
1479                    5 => FormatConvertTarget::SpdifOpticalInputPair0,
1480                    4 => FormatConvertTarget::AnalogInputPair3,
1481                    3 => FormatConvertTarget::AnalogInputPair2,
1482                    2 => FormatConvertTarget::AnalogInputPair1,
1483                    1 => FormatConvertTarget::AnalogInputPair0,
1484                    _ => FormatConvertTarget::Disabled,
1485                };
1486                Self::FormatConvert(target)
1487            }
1488            Self::RATE_CONVERT => {
1489                let target = if raw[1] == 0 {
1490                    RateConvertTarget::Disabled
1491                } else {
1492                    match (raw[2], raw[3]) {
1493                        (0, 0) => RateConvertTarget::SpdifOpticalOutputPair0,
1494                        (1, 0) => RateConvertTarget::SpdifCoaxialOutputPair0,
1495                        (0, 1) => RateConvertTarget::SpdifOpticalInputPair0,
1496                        (1, 1) => RateConvertTarget::SpdifCoaxialInputPair0,
1497                        _ => RateConvertTarget::Disabled,
1498                    }
1499                };
1500                let rate = match raw[4] {
1501                    5 => RateConvertRate::R192000,
1502                    4 => RateConvertRate::R176400,
1503                    3 => RateConvertRate::R96000,
1504                    2 => RateConvertRate::R88200,
1505                    1 => RateConvertRate::R48000,
1506                    _ => RateConvertRate::R44100,
1507                };
1508                Self::RateConvert(target, rate)
1509            }
1510            Self::MIC_POLARITY => Self::MicPolarity(raw[1] as usize, raw[2] > 0),
1511            Self::OUT_VOL => Self::OutVol(raw[1] as usize, raw[2]),
1512            Self::HW_STATUS => {
1513                if raw[1] > 0 {
1514                    let mut params = [0; METER_LONG_FRAME_SIZE];
1515                    params.copy_from_slice(&raw[1..]);
1516                    Self::HwStatusLong(params)
1517                } else {
1518                    let mut params = [0; METER_SHORT_FRAME_SIZE];
1519                    params.copy_from_slice(&raw[1..]);
1520                    Self::HwStatusShort(params)
1521                }
1522            }
1523            _ => Self::Reserved(raw.to_vec()),
1524        }
1525    }
1526}
1527
1528/// The mode of stream format.
1529#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1530pub enum StreamMode {
1531    /// For 18 channels capture and 18 channels playback.
1532    Format18x18,
1533    /// For 10 channels capture and 10 channels playback.
1534    Format10x10,
1535    /// For 8 channels capture and 8 channels playback.
1536    Format8x8,
1537}
1538
1539impl Default for StreamMode {
1540    fn default() -> Self {
1541        StreamMode::Format8x8
1542    }
1543}
1544
1545/// The target of display meter.
1546#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1547pub enum DisplayMeterTarget {
1548    /// For outputs.
1549    Output,
1550    /// For inputs.
1551    Input,
1552}
1553
1554impl Default for DisplayMeterTarget {
1555    fn default() -> Self {
1556        Self::Output
1557    }
1558}
1559
1560/// Command for functions in hardware category specific to Apogee Ensemble.
1561#[derive(Debug, Clone, PartialEq, Eq)]
1562pub enum HwCmd {
1563    /// The mode of isochronous stream in IEEE 1394 bus. Any change generates bus reset in the bus.
1564    StreamMode(StreamMode),
1565    /// Whether to illuminate display.
1566    DisplayIlluminate(bool),
1567    /// Whether to enable/disable display.
1568    DisplayMode(bool),
1569    /// The target of metering display.
1570    DisplayTarget(DisplayMeterTarget),
1571    /// Whether to enable/disable overhold of peak detection.
1572    DisplayOverhold(bool),
1573    /// Reset display metering.
1574    MeterReset,
1575    /// The mode of CD (44.1 kHz/16 bit sample).
1576    CdMode(bool),
1577    Reserved(Vec<u8>),
1578}
1579
1580impl HwCmd {
1581    const STREAM_MODE: u8 = 0x06;
1582    const DISPLAY_ILLUMINATE: u8 = 0x08;
1583    const DISPLAY_MODE: u8 = 0x09;
1584    const DISPLAY_TARGET: u8 = 0x0a;
1585    const DISPLAY_OVERHOLD: u8 = 0x0e;
1586    const METER_RESET: u8 = 0x0f;
1587    const CD_MODE: u8 = 0xf5;
1588}
1589
1590impl From<&HwCmd> for Vec<u8> {
1591    fn from(op: &HwCmd) -> Self {
1592        match op {
1593            HwCmd::StreamMode(mode) => {
1594                let val = match mode {
1595                    StreamMode::Format18x18 => 0,
1596                    StreamMode::Format10x10 => 1,
1597                    StreamMode::Format8x8 => 2,
1598                };
1599                vec![HwCmd::STREAM_MODE, val]
1600            }
1601            HwCmd::DisplayIlluminate(state) => vec![HwCmd::DISPLAY_ILLUMINATE, *state as u8],
1602            HwCmd::DisplayMode(state) => vec![HwCmd::DISPLAY_MODE, *state as u8],
1603            HwCmd::DisplayTarget(target) => {
1604                let val = match target {
1605                    DisplayMeterTarget::Output => 0,
1606                    DisplayMeterTarget::Input => 1,
1607                };
1608                vec![HwCmd::DISPLAY_TARGET, val]
1609            }
1610            HwCmd::DisplayOverhold(state) => vec![HwCmd::DISPLAY_OVERHOLD, *state as u8],
1611            HwCmd::MeterReset => vec![HwCmd::METER_RESET],
1612            HwCmd::CdMode(state) => vec![HwCmd::CD_MODE, *state as u8],
1613            HwCmd::Reserved(val) => val.to_vec(),
1614        }
1615    }
1616}
1617
1618impl From<&[u8]> for HwCmd {
1619    fn from(vals: &[u8]) -> HwCmd {
1620        match vals[0] {
1621            HwCmd::STREAM_MODE => {
1622                let mode = match vals[1] {
1623                    2 => StreamMode::Format8x8,
1624                    1 => StreamMode::Format10x10,
1625                    _ => StreamMode::Format18x18,
1626                };
1627                HwCmd::StreamMode(mode)
1628            }
1629            HwCmd::DISPLAY_ILLUMINATE => HwCmd::DisplayIlluminate(vals[1] > 0),
1630            HwCmd::DISPLAY_MODE => HwCmd::DisplayMode(vals[1] > 0),
1631            HwCmd::DISPLAY_TARGET => {
1632                let target = if vals[1] > 0 {
1633                    DisplayMeterTarget::Input
1634                } else {
1635                    DisplayMeterTarget::Output
1636                };
1637                HwCmd::DisplayTarget(target)
1638            }
1639            HwCmd::DISPLAY_OVERHOLD => HwCmd::DisplayOverhold(vals[1] > 0),
1640            HwCmd::METER_RESET => HwCmd::MeterReset,
1641            HwCmd::CD_MODE => HwCmd::CdMode(vals[1] > 0),
1642            _ => HwCmd::Reserved(vals.to_vec()),
1643        }
1644    }
1645}
1646
1647/// The protocol implementation of AV/C vendor-dependent command specific to Apogee Ensemble.
1648#[derive(Debug)]
1649pub struct EnsembleOperation {
1650    pub cmd: EnsembleCmd,
1651    op: VendorDependent,
1652}
1653
1654impl Default for EnsembleOperation {
1655    fn default() -> Self {
1656        Self {
1657            cmd: Default::default(),
1658            op: VendorDependent {
1659                company_id: APOGEE_OUI,
1660                data: Default::default(),
1661            },
1662        }
1663    }
1664}
1665
1666impl EnsembleOperation {
1667    pub fn new(cmd: EnsembleCmd) -> Self {
1668        Self {
1669            cmd,
1670            ..Default::default()
1671        }
1672    }
1673}
1674
1675impl AvcOp for EnsembleOperation {
1676    const OPCODE: u8 = VendorDependent::OPCODE;
1677}
1678
1679impl AvcControl for EnsembleOperation {
1680    fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
1681        self.op.data = Into::<Vec<u8>>::into(&self.cmd);
1682
1683        // At least, 6 bytes should be required to align to 3 quadlets. Unless, the target unit is freezed.
1684        while self.op.data.len() < 6 {
1685            self.op.data.push(0xff);
1686        }
1687
1688        AvcControl::build_operands(&mut self.op, addr)
1689    }
1690
1691    fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
1692        AvcControl::parse_operands(&mut self.op, addr, operands).map(|_| {
1693            // NOTE: parameters are retrieved by HwStatus command only.
1694            match &mut self.cmd {
1695                EnsembleCmd::HwStatusShort(buf) => buf.copy_from_slice(&self.op.data[2..]),
1696                EnsembleCmd::HwStatusLong(buf) => buf.copy_from_slice(&self.op.data[2..]),
1697                _ => (),
1698            }
1699        })
1700    }
1701}
1702
1703#[cfg(test)]
1704mod test {
1705    use super::*;
1706
1707    #[test]
1708    fn converter_params_and_cmds() {
1709        let params = EnsembleConverterParameters {
1710            format_target: FormatConvertTarget::SpdifCoaxialInputPair0,
1711            rate_target: RateConvertTarget::SpdifOpticalOutputPair0,
1712            converted_rate: RateConvertRate::R88200,
1713            cd_mode: true,
1714        };
1715        let cmds = EnsembleConverterProtocol::build_cmds(&params);
1716        let mut p = EnsembleConverterParameters::default();
1717        EnsembleConverterProtocol::parse_cmds(&mut p, &cmds);
1718
1719        assert_eq!(params, p);
1720    }
1721
1722    #[test]
1723    fn display_params_and_cmds() {
1724        let params = EnsembleDisplayParameters {
1725            enabled: false,
1726            illuminate: true,
1727            target: DisplayMeterTarget::Output,
1728            overhold: true,
1729        };
1730        let cmds = EnsembleDisplayProtocol::build_cmds(&params);
1731        let mut p = EnsembleDisplayParameters::default();
1732        EnsembleDisplayProtocol::parse_cmds(&mut p, &cmds);
1733
1734        assert_eq!(params, p);
1735    }
1736
1737    #[test]
1738    fn input_params_and_cmds() {
1739        let params = EnsembleInputParameters {
1740            limits: [false, true, false, true, true, false, true, false],
1741            levels: [
1742                InputNominalLevel::Professional,
1743                InputNominalLevel::Consumer,
1744                InputNominalLevel::Microphone,
1745                InputNominalLevel::Professional,
1746                InputNominalLevel::Consumer,
1747                InputNominalLevel::Microphone,
1748                InputNominalLevel::Professional,
1749                InputNominalLevel::Consumer,
1750            ],
1751            gains: [10, 20, 30, 40],
1752            phantoms: [true, false, false, true],
1753            polarities: [false, true, true, false],
1754            opt_iface_mode: OptIfaceMode::Adat,
1755        };
1756        let cmds = EnsembleInputProtocol::build_cmds(&params);
1757        let mut p = EnsembleInputParameters::default();
1758        EnsembleInputProtocol::parse_cmds(&mut p, &cmds);
1759
1760        assert_eq!(params, p);
1761    }
1762
1763    #[test]
1764    fn output_params_and_cmds() {
1765        let params = EnsembleOutputParameters {
1766            vol: 0x72,
1767            headphone_vols: [0x4f, 0x5a],
1768            levels: [
1769                OutputNominalLevel::Professional,
1770                OutputNominalLevel::Consumer,
1771                OutputNominalLevel::Professional,
1772                OutputNominalLevel::Consumer,
1773                OutputNominalLevel::Professional,
1774                OutputNominalLevel::Consumer,
1775                OutputNominalLevel::Professional,
1776                OutputNominalLevel::Consumer,
1777            ],
1778            opt_iface_mode: OptIfaceMode::Adat,
1779        };
1780        let cmds = EnsembleOutputProtocol::build_cmds(&params);
1781        let mut p = EnsembleOutputParameters::default();
1782        EnsembleOutputProtocol::parse_cmds(&mut p, &cmds);
1783
1784        assert_eq!(params, p);
1785    }
1786
1787    #[test]
1788    fn source_params_and_cmds() {
1789        let params = EnsembleSourceParameters {
1790            output_sources: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 17, 16, 15, 14, 13, 12, 11, 10],
1791            capture_sources: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 17, 16, 15, 14, 13, 12, 11, 10],
1792            headphone_sources: [7, 2],
1793        };
1794        let cmds = EnsembleSourceProtocol::build_cmds(&params);
1795        let mut p = EnsembleSourceParameters::default();
1796        EnsembleSourceProtocol::parse_cmds(&mut p, &cmds);
1797
1798        assert_eq!(params, p);
1799    }
1800
1801    #[test]
1802    fn mixer_params_and_cmds() {
1803        let params = EnsembleMixerParameters {
1804            src_gains: [
1805                [
1806                    39, -84, 33, 93, -55, 26, -14, -16, 36, 25, -76, 27, -90, -22, -92, 15, -98,
1807                    90, 55, 58, 0, -33, -3, -86, 62, -57, 45, 2, 51, -39, 53, 41, -58, -18, -88,
1808                    -38,
1809                ],
1810                [
1811                    -12, -78, -72, -43, -50, -73, 19, -9, 21, 28, -15, 36, 55, -58, 22, 56, 39, 43,
1812                    10, -1, 60, -6, -29, 15, -98, 46, 90, -67, 32, 83, -55, 66, 54, 48, 62, -49,
1813                ],
1814                [
1815                    10, -100, 90, 18, -3, -61, -2, -37, -29, -60, 99, -16, 54, 28, -17, 17, -69,
1816                    33, -81, -56, -39, 3, 22, 85, -35, -52, -21, 40, 21, -67, 45, 80, 0, 42, -88,
1817                    63,
1818                ],
1819                [
1820                    4, -72, 18, -56, 10, 68, -82, 82, 94, -8, -9, 6, -79, 64, 30, -50, -88, -23,
1821                    -34, 23, -33, 77, -28, -7, 21, -32, -42, -58, -1, 71, 84, 37, -80, -19, 88, 0,
1822                ],
1823            ],
1824        };
1825        let cmds = EnsembleMixerProtocol::build_cmds(&params);
1826        let mut p = EnsembleMixerParameters::default();
1827        EnsembleMixerProtocol::parse_cmds(&mut p, &cmds);
1828
1829        assert_eq!(params, p);
1830    }
1831
1832    #[test]
1833    fn stream_params() {
1834        let params = EnsembleStreamParameters {
1835            mode: StreamMode::Format10x10,
1836        };
1837        let cmds = EnsembleStreamProtocol::build_cmds(&params);
1838        let mut p = EnsembleStreamParameters::default();
1839        EnsembleStreamProtocol::parse_cmds(&mut p, &cmds);
1840
1841        assert_eq!(params, p);
1842    }
1843
1844    #[test]
1845    fn vendorcmd_from() {
1846        let cmd = EnsembleCmd::InputLimit(1, true);
1847        assert_eq!(
1848            cmd,
1849            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1850        );
1851
1852        let cmd = EnsembleCmd::MicPower(1, true);
1853        assert_eq!(
1854            cmd,
1855            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1856        );
1857
1858        let cmd = EnsembleCmd::InputNominalLevel(1, InputNominalLevel::Microphone);
1859        assert_eq!(
1860            cmd,
1861            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1862        );
1863
1864        let cmd = EnsembleCmd::OutputNominalLevel(1, OutputNominalLevel::Consumer);
1865        assert_eq!(
1866            cmd,
1867            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1868        );
1869
1870        let cmd = EnsembleCmd::IoRouting(1, 11);
1871        assert_eq!(
1872            cmd,
1873            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1874        );
1875
1876        let cmd = EnsembleCmd::Hw(HwCmd::StreamMode(StreamMode::Format10x10));
1877        assert_eq!(
1878            cmd,
1879            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1880        );
1881
1882        let cmd = EnsembleCmd::HpSrc(1, 31);
1883        assert_eq!(
1884            cmd,
1885            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1886        );
1887
1888        let cmd = EnsembleCmd::MixerSrc0(3, [3; MIXER_COEFFICIENT_COUNT]);
1889        assert_eq!(
1890            cmd,
1891            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1892        );
1893
1894        let cmd = EnsembleCmd::MixerSrc1(2, [11; MIXER_COEFFICIENT_COUNT]);
1895        assert_eq!(
1896            cmd,
1897            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1898        );
1899
1900        let cmd = EnsembleCmd::MixerSrc2(1, [17; MIXER_COEFFICIENT_COUNT]);
1901        assert_eq!(
1902            cmd,
1903            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1904        );
1905
1906        let cmd = EnsembleCmd::MixerSrc3(0, [21; MIXER_COEFFICIENT_COUNT]);
1907        assert_eq!(
1908            cmd,
1909            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1910        );
1911
1912        let cmd = EnsembleCmd::MicGain(195, 233);
1913        assert_eq!(
1914            cmd,
1915            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1916        );
1917
1918        let cmd = EnsembleCmd::OutputOptIface(OptIfaceMode::Adat);
1919        assert_eq!(
1920            cmd,
1921            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1922        );
1923
1924        let cmd = EnsembleCmd::InputOptIface(OptIfaceMode::Spdif);
1925        assert_eq!(
1926            cmd,
1927            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1928        );
1929
1930        let cmd = EnsembleCmd::FormatConvert(FormatConvertTarget::AnalogInputPair0);
1931        assert_eq!(
1932            cmd,
1933            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1934        );
1935
1936        let cmd = EnsembleCmd::RateConvert(
1937            RateConvertTarget::SpdifOpticalInputPair0,
1938            RateConvertRate::R88200,
1939        );
1940        assert_eq!(
1941            cmd,
1942            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1943        );
1944
1945        let cmd = EnsembleCmd::MicPolarity(0, true);
1946        assert_eq!(
1947            cmd,
1948            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1949        );
1950
1951        let cmd = EnsembleCmd::OutVol(0, 113);
1952        assert_eq!(
1953            cmd,
1954            EnsembleCmd::from(Into::<Vec<u8>>::into(&cmd).as_slice())
1955        );
1956    }
1957}