firewire_dice_protocols/
lexicon.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol specific to Lexicon I-ONIX FW810s.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Lexicon for I-ONIX FW810s.
8//!
9//! ## Diagram of internal signal flow
10//!
11//! ```text
12//!
13//! analog-input-0/1        -> stream-output-0/1
14//! analog-input-2/3        -> stream-output-2/3
15//! analog-input-4/5        -> stream-output-4/5
16//! analog-input-6/8        -> stream-output-6/7
17//! coaxial-input-0/1       -> stream-output-8/9
18//!
19//! stream-input-0/1        -> mixer-input-0/1
20//! stream-input-2/3        -> mixer-input-2/3
21//! stream-input-4/5        -> mixer-input-4/5
22//! stream-input-6/7        -> mixer-input-6/7
23//! analog-input-0/1        -> mixer-input-8/9
24//! analog-input-2/3        -> mixer-input-10/11
25//! analog-input-4/5        -> mixer-input-12/13
26//! analog-input-6/8        -> mixer-input-14/15
27//! coaxial-input-0/1       -> mixer-input-16/17
28//!
29//! mixer-output-0/1        -> analog-output-0/1
30//! mixer-output-2/3        -> analog-output-2/3
31//! mixer-output-4/5        -> analog-output-4/5
32//! mixer-output-6/7        -> analog-output-6/7
33//! mixer-output-8/9        -> (unused)
34//! mixer-output-10/11      -> coaxial-output-0/1
35//! mixer-output-12/13      -> main-output-0/1 (headphone-output-0/1)
36//! ```
37
38use {
39    super::{
40        tcat::{
41            extension::{router_entry::*, *},
42            *,
43        },
44        *,
45    },
46    std::ops::Range,
47};
48
49const BASE_OFFSET: usize = 0x00200000;
50
51const MIXER_BUS_SRC_OFFSET: usize = 0x0000;
52const MIXER_MAIN_SRC_OFFSET: usize = 0x02d0;
53const MIXER_REVERB_SRC_OFFSET: usize = 0x0360;
54const METER_OFFSET: usize = 0x0500;
55const EFFECT_OFFSET: usize = 0x4000;
56
57const MIXER_BUS_SRC_SIZE: usize = 0x02d0;
58const MIXER_MAIN_SRC_SIZE: usize = 0x0090;
59const MIXER_REVERB_SRC_SIZE: usize = 0x090;
60const METER_SIZE: usize = 0x200;
61
62/// Protocol implementation specific to Lexicon I-ONIX FW810s.
63#[derive(Default, Debug)]
64pub struct IonixProtocol;
65
66impl TcatOperation for IonixProtocol {}
67
68impl TcatGlobalSectionSpecification for IonixProtocol {}
69
70impl LexiconOperation for IonixProtocol {}
71
72/// Serialize and deserialize parameters.
73pub trait LexiconParametersSerdes<T> {
74    /// Name of parameters.
75    const NAME: &'static str;
76
77    /// List of offset ranges for parameters.
78    const OFFSET_RANGES: &'static [Range<usize>];
79
80    /// Serialize parameters.
81    fn serialize_params(params: &T, raw: &mut [u8]) -> Result<(), String>;
82
83    /// Deserialize parameters.
84    fn deserialize_params(params: &mut T, raw: &[u8]) -> Result<(), String>;
85}
86
87/// Common operation for Lexicon I-ONIX F810s.
88pub trait LexiconOperation: TcatOperation {
89    /// Read parameters from specific address range.
90    fn read_parameters(
91        req: &FwReq,
92        node: &FwNode,
93        offset: usize,
94        raw: &mut [u8],
95        timeout_ms: u32,
96    ) -> Result<(), Error> {
97        Self::read(req, node, BASE_OFFSET + offset, raw, timeout_ms)
98    }
99
100    /// Write parameters to specific address range.
101    fn write_parameters(
102        req: &FwReq,
103        node: &FwNode,
104        offset: usize,
105        raw: &mut [u8],
106        timeout_ms: u32,
107    ) -> Result<(), Error> {
108        Self::write(req, node, BASE_OFFSET + offset, raw, timeout_ms)
109    }
110}
111
112fn compute_params_size(ranges: &[Range<usize>]) -> usize {
113    ranges
114        .iter()
115        .fold(0usize, |size, range| size + range.end - range.start)
116}
117
118fn generate_err(name: &str, cause: &str, raw: &[u8]) -> Error {
119    let msg = format!("params: {}, cause: {}, raw: {:02x?}", name, cause, raw);
120    Error::new(GeneralProtocolError::VendorDependent, &msg)
121}
122
123/// Operation to cache parameters.
124pub trait LexiconParametersOperation<T>: LexiconOperation + LexiconParametersSerdes<T> {
125    /// Cache parameters.
126    fn cache_whole_params(
127        req: &FwReq,
128        node: &FwNode,
129        params: &mut T,
130        timeout_ms: u32,
131    ) -> Result<(), Error> {
132        let size = compute_params_size(Self::OFFSET_RANGES);
133        let mut raw = vec![0u8; size];
134
135        let mut pos = 0;
136
137        Self::OFFSET_RANGES.iter().try_for_each(|range| {
138            let size = range.end - range.start;
139            Self::read_parameters(
140                req,
141                node,
142                range.start,
143                &mut raw[pos..(pos + size)],
144                timeout_ms,
145            )
146            .map(|_| pos += size)
147        })?;
148
149        Self::deserialize_params(params, &raw)
150            .map_err(|cause| generate_err(Self::NAME, &cause, &raw))
151    }
152}
153
154impl<O: LexiconOperation + LexiconParametersSerdes<T>, T> LexiconParametersOperation<T> for O {}
155
156/// Operation for parameters to update state of hardware.
157pub trait LexiconMutableParametersOperation<T>:
158    LexiconOperation + LexiconParametersSerdes<T>
159{
160    /// Update the hardware partially for any change of parameter.
161    fn update_partial_parameters(
162        req: &FwReq,
163        node: &FwNode,
164        params: &T,
165        prev: &mut T,
166        timeout_ms: u32,
167    ) -> Result<(), Error> {
168        let size = compute_params_size(Self::OFFSET_RANGES);
169
170        let mut new = vec![0u8; size];
171        let mut old = vec![0u8; size];
172        Self::serialize_params(params, &mut new)
173            .map_err(|cause| generate_err(Self::NAME, &cause, &new))?;
174        Self::serialize_params(prev, &mut old)
175            .map_err(|cause| generate_err(Self::NAME, &cause, &old))?;
176
177        let mut pos = 0;
178
179        Self::OFFSET_RANGES.iter().try_for_each(|range| {
180            let size = range.end - range.start;
181
182            if new[pos..(pos + size)] != old[pos..(pos + size)] {
183                (0..size).step_by(4).try_for_each(|offset| {
184                    let p = pos + offset;
185                    if new[p..(p + 4)] != old[p..(p + 4)] {
186                        Self::write_parameters(
187                            req,
188                            node,
189                            range.start + offset,
190                            &mut new[p..(p + 4)],
191                            timeout_ms,
192                        )
193                    } else {
194                        Ok(())
195                    }
196                })
197            } else {
198                Ok(())
199            }
200            .map(|_| pos += size)
201        })?;
202
203        Self::deserialize_params(prev, &new).map_err(|cause| generate_err(Self::NAME, &cause, &new))
204    }
205}
206
207impl IonixProtocol {
208    /// The number of analog inputs.
209    pub const ANALOG_INPUT_COUNT: usize = 8;
210
211    /// The number of S/PDIF inputs.
212    pub const SPDIF_INPUT_COUNT: usize = 2;
213
214    /// The number of stream inputs.
215    pub const STREAM_INPUT_COUNT: usize = 10;
216
217    /// The number of analog inputs for mixer.
218    pub const MIXER_ANALOG_INPUT_COUNT: usize = 8;
219
220    /// The number of S/PDIF inputs for mixer.
221    pub const MIXER_SPDIF_INPUT_COUNT: usize = 2;
222
223    /// The number of stream inputs for mixer.
224    pub const MIXER_STREAM_INPUT_COUNT: usize = 8;
225
226    /// The number of channels in bus mixer.
227    pub const MIXER_BUS_COUNT: usize = 8;
228
229    /// The number of channels in main mixer.
230    pub const MIXER_MAIN_COUNT: usize = 2;
231
232    /// The number of channels in reverb mixer.
233    pub const MIXER_REVERB_COUNT: usize = 2;
234}
235
236/// Hardware meter.
237#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
238pub struct IonixMeter {
239    /// Detected level of analog inputs.
240    pub analog_inputs: [u16; IonixProtocol::ANALOG_INPUT_COUNT],
241    /// Detected level of S/PDIF inputs.
242    pub spdif_inputs: [u16; IonixProtocol::SPDIF_INPUT_COUNT],
243    /// Detected level of stream inputs.
244    pub stream_inputs: [u16; IonixProtocol::STREAM_INPUT_COUNT],
245    /// Detected level of mixer bus outputs.
246    pub bus_outputs: [u16; IonixProtocol::MIXER_BUS_COUNT],
247    /// Detected level of main outputs.
248    pub main_outputs: [u16; IonixProtocol::MIXER_MAIN_COUNT],
249}
250
251// NOTE: Router entries in address region for hardware metering.
252//
253// analog-input-0/1                 -> ins0:0/1
254// analog-input-2/3                 -> ins0:2/3
255// analog-input-4/5                 -> ins0:8/9
256// analog-input-6/8                 -> ins0:10/11
257// coaxial-input-2/3                -> aes:2/3
258//
259// avs0:0/1    (stream-input-0/1)   -> mixer-tx0:0/1   (mixer-input-0/1)
260// avs0:2/3    (stream-input-2/3)   -> mixer-tx0:2/3   (mixer-input-2/3)
261// avs0:4/5    (stream-input-4/5)   -> mixer-tx0:4/5   (mixer-input-4/5)
262// avs0:6/7    (stream-input-6/7)   -> mixer-tx0:6/7   (mixer-input-6/7)
263// aes:2/3                          -> mixer-tx0:8/9   (mixer-input-8/9)
264// ins0:0/1                         -> mixer-tx0:10/11 (mixer-input-10/11)
265// ins0:2/3                         -> mixer-tx0:12/13 (mixer-input-12/13)
266// ins0:8/9                         -> mixer-tx0:14/15 (mixer-input-14/15)
267// ins0:10/11                       -> mixer-tx1:0/1   (mixer-input-16/17)
268//
269// ins0:0/1                         -> avs0:0/1        (stream-output-0/1)
270// ins0:2/3                         -> avs0:2/3        (stream-output-2/3)
271// ins0:8/9                         -> avs0:4/5        (stream-output-4/5)
272// ins0:10/11                       -> avs0:6/7        (stream-output-6/7)
273// aes:2/3                          -> avs0:8/9        (stream-output-8/9)
274//
275// mixer:0/1   (mixer-output-0/1)   -> ins0:4/5
276// mixer:2/3   (mixer-output-2/3)   -> ins0:6/7
277// mixer:4/5   (mixer-output-4/5)   -> ins0:12/13
278// mixer:6/7   (mixer-output-6/7)   -> ins0:14/15
279// mixer:8/9   (mixer-output-8/9)   -> (unused?)
280// mixer:10/11 (mixer-output-10/11) -> ins1:0/1
281// mixer:12/13 (mixer-output-12/13) -> ins1:2/3 (unused?)
282//
283// ins0:4/5                         -> analog-output-0/1
284// ins0:6/7                         -> analog-output-2/3
285// ins0:12/13                       -> analog-output-4/5
286// ins0:14/15                       -> analog-output-6/7
287// avs0:8/9                         -> coaxial-output-0/1
288// ins1:0/1                         -> main-output-0/1 (headphone-output-0/1)
289
290impl<O: LexiconOperation> LexiconParametersSerdes<IonixMeter> for O {
291    const NAME: &'static str = "meter";
292
293    const OFFSET_RANGES: &'static [Range<usize>] = &[Range {
294        start: METER_OFFSET,
295        end: METER_OFFSET + METER_SIZE,
296    }];
297
298    fn serialize_params(params: &IonixMeter, raw: &mut [u8]) -> Result<(), String> {
299        raw.fill_with(Default::default);
300
301        let mut entry = RouterEntry::default();
302        let mut pos = 0;
303
304        [
305            (
306                &params.stream_inputs[..8],
307                DstBlkId::MixerTx0,
308                0,
309                SrcBlkId::Avs0,
310                0,
311            ),
312            (
313                &params.stream_inputs[8..10],
314                DstBlkId::Aes,
315                2,
316                SrcBlkId::Avs0,
317                8,
318            ),
319            (
320                &params.spdif_inputs[..],
321                DstBlkId::MixerTx0,
322                8,
323                SrcBlkId::Aes,
324                2,
325            ),
326            (
327                &params.analog_inputs[..4],
328                DstBlkId::MixerTx0,
329                10,
330                SrcBlkId::Ins0,
331                0,
332            ),
333            (
334                &params.analog_inputs[4..6],
335                DstBlkId::MixerTx0,
336                14,
337                SrcBlkId::Ins0,
338                8,
339            ),
340            (
341                &params.analog_inputs[6..8],
342                DstBlkId::MixerTx1,
343                0,
344                SrcBlkId::Ins0,
345                10,
346            ),
347            (
348                &params.bus_outputs[..4],
349                DstBlkId::Ins0,
350                4,
351                SrcBlkId::Mixer,
352                0,
353            ),
354            (
355                &params.bus_outputs[4..],
356                DstBlkId::Ins0,
357                12,
358                SrcBlkId::Mixer,
359                4,
360            ),
361            (
362                &params.main_outputs[..],
363                DstBlkId::Ins1,
364                0,
365                SrcBlkId::Mixer,
366                10,
367            ),
368        ]
369        .iter()
370        .try_for_each(
371            |(levels, dst_blk_id, dst_blk_ch_offset, src_blk_id, src_blk_ch_offset)| {
372                levels.iter().enumerate().try_for_each(|(i, &level)| {
373                    entry.peak = level;
374                    entry.dst.id = *dst_blk_id;
375                    entry.dst.ch = (i + dst_blk_ch_offset) as u8;
376                    entry.src.id = *src_blk_id;
377                    entry.src.ch = (i + src_blk_ch_offset) as u8;
378                    serialize_router_entry(&entry, &mut raw[pos..(pos + 4)]).map(|_| pos += 4)
379                })
380            },
381        )
382    }
383
384    fn deserialize_params(params: &mut IonixMeter, raw: &[u8]) -> Result<(), String> {
385        let mut entry = RouterEntry::default();
386
387        (0..raw.len()).step_by(4).try_for_each(|pos| {
388            deserialize_router_entry(&mut entry, &raw[pos..(pos + 4)]).map(|_| {
389                match (entry.dst.id, entry.dst.ch, entry.src.id, entry.src.ch) {
390                    (DstBlkId::MixerTx0, 0..=7, SrcBlkId::Avs0, 0..=7) => {
391                        let pos = entry.src.ch as usize;
392                        params.stream_inputs[pos] = entry.peak;
393                    }
394                    (DstBlkId::Aes, 2..=3, SrcBlkId::Avs0, 8..=9) => {
395                        let pos = entry.src.ch as usize;
396                        params.stream_inputs[pos] = entry.peak;
397                    }
398                    (DstBlkId::MixerTx0, 8..=9, SrcBlkId::Aes, 2..=3) => {
399                        let pos = (entry.src.ch - 2) as usize;
400                        params.spdif_inputs[pos] = entry.peak;
401                    }
402                    (DstBlkId::MixerTx0, 10..=13, SrcBlkId::Ins0, 0..=3) => {
403                        let pos = entry.src.ch as usize;
404                        params.analog_inputs[pos] = entry.peak;
405                    }
406                    (DstBlkId::MixerTx0, 14..=15, SrcBlkId::Ins0, 8..=9) => {
407                        let pos = 4 + (entry.src.ch - 8) as usize;
408                        params.analog_inputs[pos] = entry.peak;
409                    }
410                    (DstBlkId::MixerTx1, 0..=2, SrcBlkId::Ins0, 10..=11) => {
411                        let pos = 6 + (entry.src.ch - 10) as usize;
412                        params.analog_inputs[pos] = entry.peak;
413                    }
414                    (DstBlkId::Ins0, 4..=7, SrcBlkId::Mixer, 0..=3) => {
415                        let pos = entry.src.ch as usize;
416                        params.bus_outputs[pos] = entry.peak;
417                    }
418                    (DstBlkId::Ins0, 12..=15, SrcBlkId::Mixer, 4..=7) => {
419                        let pos = 4 + (entry.src.ch - 4) as usize;
420                        params.bus_outputs[pos] = entry.peak;
421                    }
422                    (DstBlkId::Ins1, 0..=2, SrcBlkId::Mixer, 10..=11) => {
423                        let pos = (entry.src.ch - 10) as usize;
424                        params.main_outputs[pos] = entry.peak;
425                    }
426                    _ => (),
427                }
428            })
429        })
430    }
431}
432
433/// Gains of sources for mixer.
434#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
435pub struct IonixMixerSources {
436    /// Gains of 8 analog inputs.
437    pub stream_inputs: [i16; IonixProtocol::MIXER_STREAM_INPUT_COUNT],
438    /// Gains of 2 S/PDIF inputs.
439    pub spdif_inputs: [i16; IonixProtocol::MIXER_SPDIF_INPUT_COUNT],
440    /// Gains of 8 analog inputs.
441    pub analog_inputs: [i16; IonixProtocol::MIXER_ANALOG_INPUT_COUNT],
442}
443
444/// Parameters of mixer.
445#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
446pub struct IonixMixerParameters {
447    /// Sources for bus mixers.
448    pub bus_sources: [IonixMixerSources; IonixProtocol::MIXER_BUS_COUNT],
449    /// Sources for main mixers.
450    pub main_sources: [IonixMixerSources; IonixProtocol::MIXER_MAIN_COUNT],
451    /// Sources for reverb effects.
452    pub reverb_sources: [IonixMixerSources; IonixProtocol::MIXER_REVERB_COUNT],
453}
454
455impl<O: LexiconOperation> LexiconParametersSerdes<IonixMixerParameters> for O {
456    const NAME: &'static str = "meter";
457
458    const OFFSET_RANGES: &'static [Range<usize>] = &[
459        Range {
460            start: MIXER_BUS_SRC_OFFSET,
461            end: MIXER_BUS_SRC_OFFSET + MIXER_BUS_SRC_SIZE,
462        },
463        Range {
464            start: MIXER_MAIN_SRC_OFFSET,
465            end: MIXER_MAIN_SRC_OFFSET + MIXER_MAIN_SRC_SIZE,
466        },
467        Range {
468            start: MIXER_REVERB_SRC_OFFSET,
469            end: MIXER_REVERB_SRC_OFFSET + MIXER_REVERB_SRC_SIZE,
470        },
471    ];
472
473    fn serialize_params(params: &IonixMixerParameters, raw: &mut [u8]) -> Result<(), String> {
474        [
475            (&params.bus_sources[..], 0x0000),
476            (&params.main_sources[..], 0x02d0),
477            (&params.reverb_sources[..], 0x0360),
478        ]
479        .iter()
480        .for_each(|(srcs, offset)| {
481            srcs.iter().enumerate().for_each(|(i, src)| {
482                src.stream_inputs
483                    .iter()
484                    .chain(src.spdif_inputs.iter())
485                    .chain(src.analog_inputs.iter())
486                    .enumerate()
487                    .for_each(|(j, gain)| {
488                        let pos = *offset + i * 0x48 + j * 4;
489                        serialize_i16(gain, &mut raw[pos..(pos + 4)]);
490                    });
491            });
492        });
493
494        Ok(())
495    }
496
497    fn deserialize_params(params: &mut IonixMixerParameters, raw: &[u8]) -> Result<(), String> {
498        [
499            (&mut params.bus_sources[..], 0x0000),
500            (&mut params.main_sources[..], 0x02d0),
501            (&mut params.reverb_sources[..], 0x0360),
502        ]
503        .iter_mut()
504        .for_each(|(srcs, offset)| {
505            srcs.iter_mut().enumerate().for_each(|(i, src)| {
506                src.stream_inputs
507                    .iter_mut()
508                    .chain(src.spdif_inputs.iter_mut())
509                    .chain(src.analog_inputs.iter_mut())
510                    .enumerate()
511                    .for_each(|(j, gain)| {
512                        let pos = *offset + i * 0x48 + j * 4;
513                        deserialize_i16(gain, &raw[pos..(pos + 4)]);
514                    });
515            });
516        });
517
518        Ok(())
519    }
520}
521
522impl<O: LexiconOperation + LexiconParametersSerdes<IonixMixerParameters>>
523    LexiconMutableParametersOperation<IonixMixerParameters> for O
524{
525}
526
527/// Serialize and deserialize data of system exclusive message.
528pub trait IonixSysExDataSerdes<T> {
529    /// Name of system exclusive message.
530    const NAME: &'static str;
531
532    /// Serialize for data of system exclusive message.
533    fn serialize_sysex_data(params: &T) -> Result<Vec<Vec<u8>>, String>;
534
535    /// Deserialize for data of system exclusive message.
536    fn deserialize_sysex_data<U: AsRef<[u8]>>(params: &mut T, raw: &[U]) -> Result<(), String>;
537}
538
539fn serialize_effect_frame(data: &[u8]) -> Vec<u8> {
540    /// Prefix of data for system exclusive message.
541    const DATA_PREFIX: [u8; 5] = [0x06, 0x00, 0x1b, 0x01, 0x41];
542
543    /// Prefix of system exclusive message.
544    const SYSEX_MSG_PREFIX: u8 = 0xf0;
545
546    /// Suffix of system exclusive message.
547    const SYSEX_MSG_SUFFIX: u8 = 0xf7;
548
549    // NOTE: The data has prefix.
550    let mut sysex_data = DATA_PREFIX.to_vec();
551    sysex_data.extend_from_slice(data);
552
553    // NOTE: Append checksum calculated by XOR for all the data.
554    let checksum = sysex_data.iter().fold(0u8, |val, &msg| val | msg);
555    sysex_data.push(checksum);
556
557    // NOTE: Construct MIDI system exclusive message.
558    let mut sysex = vec![SYSEX_MSG_PREFIX];
559    sysex.append(&mut sysex_data);
560    sysex.push(SYSEX_MSG_SUFFIX);
561
562    // NOTE: One quadlet deliver one byte of message.
563    let mut raw = Vec::new();
564    sysex
565        .iter()
566        .for_each(|&msg| raw.extend_from_slice(&(msg as u32).to_be_bytes()));
567
568    raw
569}
570
571fn generate_effect_err(name: &str, cause: &str) -> Error {
572    let msg = format!("params: {}, cause: {}", name, cause);
573    Error::new(GeneralProtocolError::VendorDependent, &msg)
574}
575
576/// Operation for effect.
577pub trait LexiconEffectOperation<T>: LexiconOperation + IonixSysExDataSerdes<T> {
578    /// Update parameters for effect.
579    fn update_effect_params(
580        req: &FwReq,
581        node: &FwNode,
582        params: &T,
583        prev: &mut T,
584        timeout_ms: u32,
585    ) -> Result<(), Error> {
586        let new = Self::serialize_sysex_data(params)
587            .map_err(|cause| generate_effect_err(Self::NAME, &cause))?;
588        let old = Self::serialize_sysex_data(prev)
589            .map_err(|cause| generate_effect_err(Self::NAME, &cause))?;
590
591        new.iter()
592            .zip(old.iter())
593            .filter(|(n, o)| !n.eq(o))
594            .try_for_each(|(data, _)| {
595                let mut raw = serialize_effect_frame(data);
596                Self::write(req, node, EFFECT_OFFSET, &mut raw, timeout_ms)
597            })?;
598
599        Self::deserialize_sysex_data(prev, &new)
600            .map_err(|cause| generate_effect_err(Self::NAME, &cause))
601    }
602}
603
604impl<O: LexiconOperation + IonixSysExDataSerdes<T>, T> LexiconEffectOperation<T> for O {}
605
606#[cfg(test)]
607mod test {
608    use super::*;
609
610    #[test]
611    fn mixer_params_serdes() {
612        let mut params = IonixMixerParameters::default();
613        params
614            .bus_sources
615            .iter_mut()
616            .chain(params.main_sources.iter_mut())
617            .chain(params.reverb_sources.iter_mut())
618            .flat_map(|srcs| {
619                srcs.stream_inputs
620                    .iter_mut()
621                    .chain(srcs.spdif_inputs.iter_mut())
622                    .chain(srcs.analog_inputs.iter_mut())
623            })
624            .enumerate()
625            .for_each(|(i, gain)| *gain = i as i16);
626
627        let size = compute_params_size(
628            <IonixProtocol as LexiconParametersSerdes<IonixMixerParameters>>::OFFSET_RANGES,
629        );
630        let mut raw = vec![0u8; size];
631        IonixProtocol::serialize_params(&params, &mut raw).unwrap();
632
633        let mut p = IonixMixerParameters::default();
634        IonixProtocol::deserialize_params(&mut p, &raw).unwrap();
635
636        assert_eq!(params.bus_sources, p.bus_sources, "{:02x?}", raw);
637        assert_eq!(params.main_sources, p.main_sources);
638        assert_eq!(params.reverb_sources, p.reverb_sources);
639    }
640}