firewire_dice_protocols/tcelectronic/
desktop.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! Protocol defined by TC Electronic for Desktop Konnekt 6.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by TC Electronic for Desktop Konnekt 6.
8//!
9//! ## Diagram of internal signal flow
10//!
11//! ```text
12//!                  +-------+
13//! XLR input -----> |       |
14//!                  | input | -> analog input-1/2 -----------> stream-output-1/2
15//! Phone input 1 -> | scene |          |          (unused) --> stream-output-3/4
16//! Phone input 2 -> |       |          |          (unused) --> stream-output-5/6
17//!                  +-------+          v
18//!                                ++=======++
19//!                                || 4 x 2 ||
20//!                         +----> ||       || -----+---------> analog-output-1/2 (main)
21//!                         |      || mixer ||      |
22//!                         |      ++=======++      |
23//! stream-input-1/2 -------+                       |
24//! stream-input-3/4 -------------------------------or--------> analog-output-3/4 (headphone)
25//! stream-input-5/6 --> (unused)
26//! ```
27//!
28//! Reverb effect is not implemented in hardware, while control items are in hardware surface.
29
30use super::tcelectronic::*;
31
32const DESKTOP_HW_STATE_NOTIFY_FLAG: u32 = 0x00010000;
33const DESKTOP_CONFIG_NOTIFY_FLAG: u32 = 0x00020000;
34const DESKTOP_MIXER_STATE_NOTIFY_FLAG: u32 = 0x00040000;
35const DESKTOP_PANEL_NOTIFY_FLAG: u32 = 0x00080000;
36
37/// Protocol implementation of Desktop Konnekt 6.
38#[derive(Default, Debug)]
39pub struct Desktopk6Protocol;
40
41impl TcatOperation for Desktopk6Protocol {}
42
43impl TcatGlobalSectionSpecification for Desktopk6Protocol {}
44
45/// Segment for panel. 0x0008..0x0097 (36 quads).
46pub type Desktopk6HwStateSegment = TcKonnektSegment<DesktopHwState>;
47
48/// Segment for configuration. 0x0098..0x00b7 (8 quads).
49pub type Desktopk6ConfigSegment = TcKonnektSegment<DesktopConfig>;
50
51/// Segment for state of mixer. 0x00b8..0x0367 (172 quads).
52pub type Desktopk6MixerStateSegment = TcKonnektSegment<DesktopMixerState>;
53
54/// Segment for panel. 0x2008..0x2047 (15 quads).
55pub type Desktopk6PanelSegment = TcKonnektSegment<DesktopPanel>;
56
57/// Segment for meter. 0x20e4..0x213f (23 quads).
58pub type Desktopk6MeterSegment = TcKonnektSegment<DesktopMeter>;
59
60macro_rules! segment_default {
61    ($p:ty, $t:ty) => {
62        impl Default for TcKonnektSegment<$t> {
63            fn default() -> Self {
64                Self {
65                    data: <$t>::default(),
66                    raw: vec![0; <$p as TcKonnektSegmentSerdes<$t>>::SIZE],
67                }
68            }
69        }
70    };
71}
72
73segment_default!(Desktopk6Protocol, DesktopHwState);
74segment_default!(Desktopk6Protocol, DesktopConfig);
75segment_default!(Desktopk6Protocol, DesktopMixerState);
76segment_default!(Desktopk6Protocol, DesktopPanel);
77segment_default!(Desktopk6Protocol, DesktopMeter);
78
79/// Target of meter.
80#[derive(Debug, Copy, Clone, PartialEq, Eq)]
81pub enum MeterTarget {
82    /// Analog input 1/2.
83    Input,
84    /// Mixer output 1/2 before volume adjustment.
85    Pre,
86    /// Mixer output 1/2 after volume adjustment.
87    Post,
88}
89
90impl Default for MeterTarget {
91    fn default() -> Self {
92        Self::Input
93    }
94}
95
96const METER_TARGETS: &[MeterTarget] = &[MeterTarget::Input, MeterTarget::Pre, MeterTarget::Post];
97
98const METER_TARGET_LABEL: &str = "meter-target";
99
100fn serialize_meter_target(target: &MeterTarget, raw: &mut [u8]) -> Result<(), String> {
101    serialize_position(METER_TARGETS, target, raw, METER_TARGET_LABEL)
102}
103
104fn deserialize_meter_target(target: &mut MeterTarget, raw: &[u8]) -> Result<(), String> {
105    deserialize_position(METER_TARGETS, target, raw, METER_TARGET_LABEL)
106}
107
108/// Current scene for analog input 1/2.
109#[derive(Debug, Copy, Clone, PartialEq, Eq)]
110pub enum InputScene {
111    /// Microphone and instrument.
112    MicInst,
113    /// Two instruments.
114    DualInst,
115    /// Two line inputs for stereo.
116    StereoIn,
117}
118
119impl Default for InputScene {
120    fn default() -> Self {
121        Self::MicInst
122    }
123}
124
125const INPUT_SCENES: &[InputScene] = &[
126    InputScene::MicInst,
127    InputScene::DualInst,
128    InputScene::StereoIn,
129];
130
131const INPUT_SCENE_LABEL: &str = "inputscene";
132
133fn serialize_input_scene(scene: &InputScene, raw: &mut [u8]) -> Result<(), String> {
134    serialize_position(INPUT_SCENES, scene, raw, INPUT_SCENE_LABEL)
135}
136
137fn deserialize_input_scene(scene: &mut InputScene, raw: &[u8]) -> Result<(), String> {
138    deserialize_position(INPUT_SCENES, scene, raw, INPUT_SCENE_LABEL)
139}
140
141/// General state of hardware.
142#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
143pub struct DesktopHwState {
144    /// The target of meter in surface.
145    pub meter_target: MeterTarget,
146    /// Use mixer output as monaural.
147    pub mixer_output_monaural: bool,
148    /// Whether to adjust volume of headphone output by main knob.
149    pub knob_assign_to_hp: bool,
150    /// Whether to dim main output.
151    pub mixer_output_dim_enabled: bool,
152    /// The volume of main output if dimmed between -1000..-60. (-94.0..-6.0 dB)
153    pub mixer_output_dim_volume: i32,
154    /// The use case of analog input 1/2.
155    pub input_scene: InputScene,
156    /// Multiplex reverb signal to stream input 1/2 in advance.
157    pub reverb_to_master: bool,
158    /// Multiplex reverb signal to stream input 3/4 in advance.
159    pub reverb_to_hp: bool,
160    /// Turn on backlight in master knob.
161    pub master_knob_backlight: bool,
162    /// Phantom powering in microphone 1.
163    pub mic_0_phantom: bool,
164    /// Signal boost in microphone 1 by 12 dB.
165    pub mic_0_boost: bool,
166}
167
168impl DesktopHwState {
169    const REVERB_TO_MAIN_MASK: u32 = 0x00000001;
170    const REVERB_TO_HP_MASK: u32 = 0x00000002;
171}
172
173impl TcKonnektSegmentSerdes<DesktopHwState> for Desktopk6Protocol {
174    const NAME: &'static str = "hardware-state";
175    const OFFSET: usize = 0x0008;
176    const SIZE: usize = 144;
177
178    fn serialize(params: &DesktopHwState, raw: &mut [u8]) -> Result<(), String> {
179        serialize_meter_target(&params.meter_target, &mut raw[..4])?;
180        serialize_bool(&params.mixer_output_monaural, &mut raw[4..8]);
181        serialize_bool(&params.knob_assign_to_hp, &mut raw[8..12]);
182        serialize_bool(&params.mixer_output_dim_enabled, &mut raw[12..16]);
183        serialize_i32(&params.mixer_output_dim_volume, &mut raw[16..20]);
184        serialize_input_scene(&params.input_scene, &mut raw[20..24])?;
185
186        let mut val = 0;
187        if params.reverb_to_master {
188            val |= DesktopHwState::REVERB_TO_MAIN_MASK;
189        }
190        if params.reverb_to_hp {
191            val |= DesktopHwState::REVERB_TO_HP_MASK;
192        }
193        serialize_u32(&val, &mut raw[28..32]);
194
195        serialize_bool(&params.master_knob_backlight, &mut raw[32..36]);
196        serialize_bool(&params.mic_0_phantom, &mut raw[52..56]);
197        serialize_bool(&params.mic_0_boost, &mut raw[56..60]);
198
199        Ok(())
200    }
201
202    fn deserialize(params: &mut DesktopHwState, raw: &[u8]) -> Result<(), String> {
203        deserialize_meter_target(&mut params.meter_target, &raw[..4])?;
204        deserialize_bool(&mut params.mixer_output_monaural, &raw[4..8]);
205        deserialize_bool(&mut params.knob_assign_to_hp, &raw[8..12]);
206        deserialize_bool(&mut params.mixer_output_dim_enabled, &raw[12..16]);
207        deserialize_i32(&mut params.mixer_output_dim_volume, &raw[16..20]);
208        deserialize_input_scene(&mut params.input_scene, &raw[20..24])?;
209
210        let mut val = 0;
211        deserialize_u32(&mut val, &raw[28..32]);
212        params.reverb_to_master = val & DesktopHwState::REVERB_TO_MAIN_MASK > 0;
213        params.reverb_to_hp = val & DesktopHwState::REVERB_TO_HP_MASK > 0;
214
215        deserialize_bool(&mut params.master_knob_backlight, &raw[32..36]);
216        deserialize_bool(&mut params.mic_0_phantom, &raw[52..56]);
217        deserialize_bool(&mut params.mic_0_boost, &raw[56..60]);
218
219        Ok(())
220    }
221}
222
223impl TcKonnektMutableSegmentOperation<DesktopHwState> for Desktopk6Protocol {}
224
225impl TcKonnektNotifiedSegmentOperation<DesktopHwState> for Desktopk6Protocol {
226    const NOTIFY_FLAG: u32 = DESKTOP_HW_STATE_NOTIFY_FLAG;
227}
228
229/// Configuration.
230#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
231pub struct DesktopConfig {
232    /// Sampling rate at standalone mode.
233    pub standalone_rate: TcKonnektStandaloneClockRate,
234}
235
236impl TcKonnektSegmentSerdes<DesktopConfig> for Desktopk6Protocol {
237    const NAME: &'static str = "configuration";
238    const OFFSET: usize = 0x0098;
239    const SIZE: usize = 32;
240
241    fn serialize(params: &DesktopConfig, raw: &mut [u8]) -> Result<(), String> {
242        serialize_standalone_clock_rate(&params.standalone_rate, &mut raw[4..8])
243    }
244
245    fn deserialize(params: &mut DesktopConfig, raw: &[u8]) -> Result<(), String> {
246        deserialize_standalone_clock_rate(&mut params.standalone_rate, &raw[4..8])
247    }
248}
249
250impl TcKonnektMutableSegmentOperation<DesktopConfig> for Desktopk6Protocol {}
251
252impl TcKonnektNotifiedSegmentOperation<DesktopConfig> for Desktopk6Protocol {
253    const NOTIFY_FLAG: u32 = DESKTOP_CONFIG_NOTIFY_FLAG;
254}
255
256impl AsRef<TcKonnektStandaloneClockRate> for DesktopConfig {
257    fn as_ref(&self) -> &TcKonnektStandaloneClockRate {
258        &self.standalone_rate
259    }
260}
261
262impl AsMut<TcKonnektStandaloneClockRate> for DesktopConfig {
263    fn as_mut(&mut self) -> &mut TcKonnektStandaloneClockRate {
264        &mut self.standalone_rate
265    }
266}
267
268/// Source of headphone.
269#[derive(Debug, Copy, Clone, PartialEq, Eq)]
270pub enum DesktopHpSrc {
271    /// Stream input 3/4.
272    Stream23,
273    /// Mixer output 1/2.
274    Mixer01,
275}
276
277impl Default for DesktopHpSrc {
278    fn default() -> Self {
279        Self::Stream23
280    }
281}
282
283fn serialize_hp_src(src: &DesktopHpSrc, raw: &mut [u8]) -> Result<(), String> {
284    assert!(raw.len() >= 4);
285
286    let val = match src {
287        DesktopHpSrc::Stream23 => 0x05,
288        DesktopHpSrc::Mixer01 => 0x0b,
289    };
290
291    serialize_u32(&val, raw);
292
293    Ok(())
294}
295
296fn deserialize_hp_src(src: &mut DesktopHpSrc, raw: &[u8]) -> Result<(), String> {
297    assert!(raw.len() >= 4);
298
299    let mut val = 0u32;
300    deserialize_u32(&mut val, raw);
301
302    *src = match val {
303        0x05 => DesktopHpSrc::Stream23,
304        _ => DesktopHpSrc::Mixer01,
305    };
306
307    Ok(())
308}
309
310/// State of mixer.
311#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
312pub struct DesktopMixerState {
313    /// The input level of microphone 1 and phone 1 for instrument, between -1000 and 0 (-94.0 and
314    /// 0.0 dB)
315    pub mic_inst_level: [i32; 2],
316    /// The LR balance of microphone 1 and phone 1 for instrument, between -50 and 50.
317    pub mic_inst_pan: [i32; 2],
318    /// The level to send from microphone 1 and phone 1 for instrument, between -1000 and 0 (-94.0
319    /// and 0.0 dB)
320    pub mic_inst_send: [i32; 2],
321    /// The input level of both phone 1 and 2 for instrument, between -1000 and 0 (-94.0 and
322    /// 0.0 dB)
323    pub dual_inst_level: [i32; 2],
324    /// The LR balance  of both phone 1 and 2 for instrument, between -50 and 50.
325    pub dual_inst_pan: [i32; 2],
326    /// The level to send from both phone 1 and 2 for instrument, between -1000 and 0 (-94.0 and
327    /// 0.0 dB)
328    pub dual_inst_send: [i32; 2],
329    /// The input level of both phone 1 and 2 for line, between -1000 and 0 (-94.0 and 0.0 dB)
330    pub stereo_in_level: i32,
331    /// The LR balance of both phone 1 and 2 for line, between -50 and 50.
332    pub stereo_in_pan: i32,
333    /// The level to send of both phone 1 and 2 for line, between -1000 and 0 (-94.0 and 0.0 dB)
334    pub stereo_in_send: i32,
335    /// The source of headphone output.
336    pub hp_src: DesktopHpSrc,
337}
338
339impl TcKonnektSegmentSerdes<DesktopMixerState> for Desktopk6Protocol {
340    const NAME: &'static str = "mixer-state";
341    const OFFSET: usize = 0x00b8;
342    const SIZE: usize = 688;
343
344    fn serialize(params: &DesktopMixerState, raw: &mut [u8]) -> Result<(), String> {
345        serialize_i32(&params.mic_inst_level[0], &mut raw[12..16]);
346        serialize_i32(&params.mic_inst_pan[0], &mut raw[16..20]);
347        serialize_i32(&params.mic_inst_send[0], &mut raw[20..24]);
348        serialize_i32(&params.mic_inst_level[1], &mut raw[28..32]);
349        serialize_i32(&params.mic_inst_pan[1], &mut raw[32..36]);
350        serialize_i32(&params.mic_inst_send[1], &mut raw[40..44]);
351
352        serialize_i32(&params.dual_inst_level[0], &mut raw[228..232]);
353        serialize_i32(&params.dual_inst_pan[0], &mut raw[232..236]);
354        serialize_i32(&params.dual_inst_send[0], &mut raw[240..244]);
355        serialize_i32(&params.dual_inst_level[1], &mut raw[248..252]);
356        serialize_i32(&params.dual_inst_pan[1], &mut raw[252..256]);
357        serialize_i32(&params.dual_inst_send[1], &mut raw[260..264]);
358
359        serialize_i32(&params.stereo_in_level, &mut raw[444..448]);
360        serialize_i32(&params.stereo_in_pan, &mut raw[448..452]);
361        serialize_i32(&params.stereo_in_send, &mut raw[452..456]);
362
363        serialize_hp_src(&params.hp_src, &mut raw[648..652])
364    }
365
366    fn deserialize(params: &mut DesktopMixerState, raw: &[u8]) -> Result<(), String> {
367        deserialize_i32(&mut params.mic_inst_level[0], &raw[12..16]);
368        deserialize_i32(&mut params.mic_inst_pan[0], &raw[16..20]);
369        deserialize_i32(&mut params.mic_inst_send[0], &raw[20..24]);
370        deserialize_i32(&mut params.mic_inst_level[1], &raw[28..32]);
371        deserialize_i32(&mut params.mic_inst_pan[1], &raw[32..36]);
372        deserialize_i32(&mut params.mic_inst_send[1], &raw[40..44]);
373
374        deserialize_i32(&mut params.dual_inst_level[0], &raw[228..232]);
375        deserialize_i32(&mut params.dual_inst_pan[0], &raw[232..236]);
376        deserialize_i32(&mut params.dual_inst_send[0], &raw[240..244]);
377        deserialize_i32(&mut params.dual_inst_level[1], &raw[248..252]);
378        deserialize_i32(&mut params.dual_inst_pan[1], &raw[252..256]);
379        deserialize_i32(&mut params.dual_inst_send[1], &raw[260..264]);
380
381        deserialize_i32(&mut params.stereo_in_level, &raw[444..448]);
382        deserialize_i32(&mut params.stereo_in_pan, &raw[448..452]);
383        deserialize_i32(&mut params.stereo_in_send, &raw[452..456]);
384
385        deserialize_hp_src(&mut params.hp_src, &raw[648..652])
386    }
387}
388
389impl TcKonnektMutableSegmentOperation<DesktopMixerState> for Desktopk6Protocol {}
390
391impl TcKonnektNotifiedSegmentOperation<DesktopMixerState> for Desktopk6Protocol {
392    const NOTIFY_FLAG: u32 = DESKTOP_MIXER_STATE_NOTIFY_FLAG;
393}
394
395/// Panel in hardware surface.
396#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
397pub struct DesktopPanel {
398    /// The count of panel button to push.
399    pub panel_button_count: u32,
400    /// The value of main knob, between -1000 and 0.
401    pub main_knob_value: i32,
402    /// The value of phone knob, between -1000 and 0.
403    pub phone_knob_value: i32,
404    /// The value of mix knob, between 0 and 1000.
405    pub mix_knob_value: u32,
406    /// The state of reverb LED.
407    pub reverb_led_on: bool,
408    /// The value of reverb knob, between -1000 and 0.
409    pub reverb_knob_value: i32,
410    /// The state of FireWire LED.
411    pub firewire_led: FireWireLedState,
412}
413
414impl TcKonnektSegmentSerdes<DesktopPanel> for Desktopk6Protocol {
415    const NAME: &'static str = "hardware-panel";
416    const OFFSET: usize = 0x2008;
417    const SIZE: usize = 64;
418
419    fn serialize(params: &DesktopPanel, raw: &mut [u8]) -> Result<(), String> {
420        serialize_u32(&params.panel_button_count, &mut raw[..4]);
421        serialize_i32(&params.main_knob_value, &mut raw[4..8]);
422        serialize_i32(&params.phone_knob_value, &mut raw[8..12]);
423        serialize_u32(&params.mix_knob_value, &mut raw[12..16]);
424        serialize_bool(&params.reverb_led_on, &mut raw[16..20]);
425        serialize_i32(&params.reverb_knob_value, &mut raw[24..28]);
426        serialize_fw_led_state(&params.firewire_led, &mut raw[36..40])?;
427        Ok(())
428    }
429
430    fn deserialize(params: &mut DesktopPanel, raw: &[u8]) -> Result<(), String> {
431        deserialize_u32(&mut params.panel_button_count, &raw[..4]);
432        deserialize_i32(&mut params.main_knob_value, &raw[4..8]);
433        deserialize_i32(&mut params.phone_knob_value, &raw[8..12]);
434        deserialize_u32(&mut params.mix_knob_value, &raw[12..16]);
435        deserialize_bool(&mut params.reverb_led_on, &raw[16..20]);
436        deserialize_i32(&mut params.reverb_knob_value, &raw[24..28]);
437        deserialize_fw_led_state(&mut params.firewire_led, &raw[36..40])?;
438        Ok(())
439    }
440}
441
442impl TcKonnektMutableSegmentOperation<DesktopPanel> for Desktopk6Protocol {}
443
444impl TcKonnektNotifiedSegmentOperation<DesktopPanel> for Desktopk6Protocol {
445    const NOTIFY_FLAG: u32 = DESKTOP_PANEL_NOTIFY_FLAG;
446}
447
448impl AsRef<FireWireLedState> for DesktopPanel {
449    fn as_ref(&self) -> &FireWireLedState {
450        &self.firewire_led
451    }
452}
453
454impl AsMut<FireWireLedState> for DesktopPanel {
455    fn as_mut(&mut self) -> &mut FireWireLedState {
456        &mut self.firewire_led
457    }
458}
459
460/// Hardware metering.
461#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
462pub struct DesktopMeter {
463    pub analog_inputs: [i32; 2],
464    pub mixer_outputs: [i32; 2],
465    pub stream_inputs: [i32; 2],
466}
467
468impl TcKonnektSegmentSerdes<DesktopMeter> for Desktopk6Protocol {
469    const NAME: &'static str = "hardware-meter";
470    const OFFSET: usize = 0x20e4;
471    const SIZE: usize = 92;
472
473    fn serialize(params: &DesktopMeter, raw: &mut [u8]) -> Result<(), String> {
474        serialize_i32(&params.analog_inputs[0], &mut raw[..4]);
475        serialize_i32(&params.analog_inputs[1], &mut raw[4..8]);
476        serialize_i32(&params.mixer_outputs[0], &mut raw[40..44]);
477        serialize_i32(&params.mixer_outputs[1], &mut raw[44..48]);
478        serialize_i32(&params.stream_inputs[0], &mut raw[48..52]);
479        serialize_i32(&params.stream_inputs[1], &mut raw[52..56]);
480        Ok(())
481    }
482
483    fn deserialize(params: &mut DesktopMeter, raw: &[u8]) -> Result<(), String> {
484        deserialize_i32(&mut params.analog_inputs[0], &raw[..4]);
485        deserialize_i32(&mut params.analog_inputs[1], &raw[4..8]);
486        deserialize_i32(&mut params.mixer_outputs[0], &raw[40..44]);
487        deserialize_i32(&mut params.mixer_outputs[1], &raw[44..48]);
488        deserialize_i32(&mut params.stream_inputs[0], &raw[48..52]);
489        deserialize_i32(&mut params.stream_inputs[1], &raw[52..56]);
490        Ok(())
491    }
492}