ot_tools_io/projects/settings/
control_menu.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Data structures for the Octatrack Project Settings 'Control Menu'.
7
8use crate::RBoxErr;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use crate::projects::{
13    options::ProjectMidiChannels, parse_hashmap_string_value, parse_hashmap_string_value_bool,
14    FromHashMap,
15};
16use crate::OptionEnumValueConvert;
17
18/// Convenience struct for all data related to the Octatrack Project Settings 'Control' Menu.
19
20#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
21pub struct ControlMenu {
22    /// 'Audio' page
23    pub audio: AudioControlPage,
24
25    /// 'Input' page
26    pub input: InputControlPage,
27
28    /// 'Sequencer' page
29    pub sequencer: SequencerControlPage,
30
31    /// 'MIDI Sequencer' page
32    // TODO?!?!?!??!
33    pub midi_sequencer: MidiSequencerControlPage,
34
35    /// 'Memory' page
36    pub memory: MemoryControlPage,
37
38    /// 'Metronome' page
39    pub metronome: MetronomeControlPage,
40
41    /// 'Midi' sub menu
42    pub midi: MidiSubMenu,
43}
44
45impl FromHashMap for ControlMenu {
46    type A = String;
47    type B = String;
48    type T = Self;
49
50    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
51        Ok(Self {
52            audio: AudioControlPage::from_hashmap(hmap)?,
53            input: InputControlPage::from_hashmap(hmap)?,
54            sequencer: SequencerControlPage::from_hashmap(hmap)?,
55            midi_sequencer: MidiSequencerControlPage {},
56            memory: MemoryControlPage::from_hashmap(hmap)?,
57            metronome: MetronomeControlPage::from_hashmap(hmap)?,
58            midi: MidiSubMenu::from_hashmap(hmap)?,
59        })
60    }
61}
62
63/// Convenience struct for all data related to the 'MIDI' sub-menu
64/// within the Octatrack Project Settings 'Control' Menu.
65
66#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
67pub struct MidiSubMenu {
68    pub control: MidiControlMidiPage,
69    pub sync: MidiSyncMidiPage,
70    pub channels: MidiChannelsMidiPage,
71    // TODO?!?!
72    // control_midi_turbo: todo!(),
73}
74
75impl FromHashMap for MidiSubMenu {
76    type A = String;
77    type B = String;
78    type T = Self;
79
80    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
81        Ok(Self {
82            control: MidiControlMidiPage::from_hashmap(hmap)?,
83            sync: MidiSyncMidiPage::from_hashmap(hmap)?,
84            channels: MidiChannelsMidiPage::from_hashmap(hmap)?,
85        })
86    }
87}
88
89/// `PROJECT` -> `CONTROL` -> `AUDIO` UI menu.
90
91#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
92pub struct AudioControlPage {
93    /// `TRACK 8` setting. Whether Track 8 is a master audio track or not:
94    /// - **NORMAL**: `false`
95    /// - **MASTER**: `true`
96    pub master_track: bool,
97
98    /// `CUE CFG` setting. Behaviour for audio routing to CUE outputs.
99    /// - **NORMAL** -> **CUE+TRACK** button combo sends audio to CUE out.
100    /// - **STUDIO** -> Individual track volume controls for CUE out (unable to **CUE+TRACK**).
101    pub cue_studio_mode: bool,
102}
103
104impl FromHashMap for AudioControlPage {
105    type A = String;
106    type B = String;
107    type T = Self;
108
109    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
110        Ok(Self {
111            master_track: parse_hashmap_string_value_bool(hmap, "master_track", None)?,
112            cue_studio_mode: parse_hashmap_string_value_bool(hmap, "cue_studio_mode", None)?,
113        })
114    }
115}
116
117/// `PROJECT` -> `CONTROL` -> `INPUT` UI menu.
118
119#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
120pub struct InputControlPage {
121    /// dB level of noise gate for the AB external audio inputs.
122    /// See Manual section 8.8 MIXER MENU
123    pub gate_ab: u8, // 127 is default so i assume this is u8? midpoint?
124
125    /// dB level of noise gate for the CD external audio inputs.
126    /// See Manual section 8.8 MIXER MENU
127    pub gate_cd: u8, // 127 is default so i assume this is u8? midpoint?
128
129    /// See Manual section 8.6.2. INPUT.
130    /// Adds a delay to incoming external audio signals. Controlled by the DIR setting on the MIXER page.
131    pub input_delay_compensation: bool,
132}
133
134impl FromHashMap for InputControlPage {
135    type A = String;
136    type B = String;
137    type T = Self;
138
139    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
140        Ok(Self {
141            gate_ab: parse_hashmap_string_value::<u8>(hmap, "gate_ab", None)?,
142            gate_cd: parse_hashmap_string_value::<u8>(hmap, "gate_cd", None)?,
143            input_delay_compensation: parse_hashmap_string_value_bool(
144                hmap,
145                "input_delay_compensation",
146                None,
147            )?,
148        })
149    }
150}
151
152/// `PROJECT` -> `CONTROL` -> `SEQUENCER` UI menu.
153
154#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
155pub struct SequencerControlPage {
156    /// `CHAIN AFTER` setting.
157    /// When chained patterns start playing once the pattern is chosen.
158    /// This is the global project level setting, but can be overidden for each pattern.
159    /// Default setting is "PATTERN LENGTH".
160    /// See Manual section 8.6.3. SEQUENCER.
161    pub pattern_change_chain_behaviour: u8, // bool?
162
163    /// `SILENCE TRACKS` setting
164    /// Silence tracks when switching to a new pattern.
165    /// See Manual section 8.6.3. SEQUENCER.
166    pub pattern_change_auto_silence_tracks: bool,
167
168    /// `LFO AUTO CHANGE` setting.
169    /// Whether to retrigger LFOs when swtiching to a new pattern
170    /// See Manual section 8.6.3. SEQUENCER.
171    pub pattern_change_auto_trig_lfos: bool,
172}
173
174impl FromHashMap for SequencerControlPage {
175    type A = String;
176    type B = String;
177    type T = Self;
178
179    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
180        Ok(Self {
181            pattern_change_chain_behaviour: parse_hashmap_string_value::<u8>(
182                hmap,
183                "pattern_change_chain_behavior",
184                None,
185            )?,
186            pattern_change_auto_silence_tracks: parse_hashmap_string_value_bool(
187                hmap,
188                "pattern_change_auto_trig_lfos",
189                None,
190            )?,
191            pattern_change_auto_trig_lfos: parse_hashmap_string_value_bool(
192                hmap,
193                "pattern_change_auto_trig_lfos",
194                None,
195            )?,
196        })
197    }
198}
199
200/// `PROJECT` -> `CONTROL` -> `MIDI SEQUENCER` UI menu.
201// TODO: ?!?!?!?! Where is the value for this??!?!?!
202#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
203pub struct MidiSequencerControlPage {}
204
205/// `PROJECT` -> `CONTROL` -> `MEMORY` UI menu.
206
207#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
208pub struct MemoryControlPage {
209    /// Whether samples can be loaded in 24-bit depth (16 bit depth samples are always oaded as 16 bit).
210    /// Setting this to false loads all samples as 16 bit depth.
211    /// See Manual section 8.6.5. MEMORY.
212    pub load_24bit_flex: bool,
213
214    /// Disabled forces all recorders to use track recorder memory (16 seconds per track).
215    /// When enabled, track recorders can use free Flex RAM memory.
216    /// See Manual section 8.6.5. MEMORY.
217    pub dynamic_recorders: bool,
218
219    /// Whether to record in 24 bit depth (`true`) or 16 bit depth (`false`).
220    /// See Manual section 8.6.5. MEMORY.
221    pub record_24bit: bool,
222
223    /// How many active track recorders are available in a project. Controls whether TR1 through to TR8 are enabled / disabled.
224    /// See Manual section 8.6.5. MEMORY.
225    pub reserved_recorder_count: u8,
226
227    /// How many 'sequencer steps' should be reserved for track recorders in RAM.
228    /// See Manual section 8.6.5. MEMORY.
229    pub reserved_recorder_length: u32,
230}
231
232impl FromHashMap for MemoryControlPage {
233    type A = String;
234    type B = String;
235    type T = Self;
236
237    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
238        Ok(Self {
239            load_24bit_flex: parse_hashmap_string_value_bool(hmap, "load_24bit_flex", None)?,
240            dynamic_recorders: parse_hashmap_string_value_bool(hmap, "dynamic_recorders", None)?,
241            record_24bit: parse_hashmap_string_value_bool(hmap, "record_24bit", None)?,
242            reserved_recorder_count: parse_hashmap_string_value::<u8>(
243                hmap,
244                "reserved_recorder_count",
245                None,
246            )?,
247            reserved_recorder_length: parse_hashmap_string_value::<u32>(
248                hmap,
249                "reserved_recorder_length",
250                None,
251            )?,
252        })
253    }
254}
255
256/// `PROJECT` -> `CONTROL` -> `METRONOME` UI menu.
257
258#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
259pub struct MetronomeControlPage {
260    /// `TIME SIG. NUMER` setting in `PROJECT` -> `CONTROL` -> `METRONOME` UI menu.
261    /// Controls the numerator for time signature (the 3 in 3/4).
262    /// See Manual section 8.6.6 METRONOME
263    pub metronome_time_signature: u8, // i'm guessing 3 is actually 4/4? 0-indexed
264
265    /// `TIME SIG. DENOM` setting in `PROJECT` -> `CONTROL` -> `METRONOME` UI menu.
266    /// Controls the numerator for time signature (the 3 in 3/4).
267    /// See Manual section 8.6.6 METRONOME
268    pub metronome_time_signature_denominator: u8, // i'm guessing 2 is actually 4/4? 0-indexed
269
270    /// `PREROLL` setting in `PROJECT` -> `CONTROL` -> `METRONOME` UI menu.
271    /// How many bars to prerolls with the metronome before playing a pattern.
272    /// See Manual section 8.6.6 METRONOME
273    pub metronome_preroll: u8, // what is the maximum for this?
274
275    /// How loud to play the metronome on CUE outputs. Default is 32.
276    /// See Manual section 8.6.6 METRONOME
277    pub metronome_cue_volume: u8,
278
279    /// How loud to play the metronome on MAIN outputs. Default is 0.
280    /// See Manual section 8.6.6 METRONOME
281    pub metronome_main_volume: u8,
282
283    /// Pitch of the metronome clicks. Default is 12.
284    /// See Manual section 8.6.6 METRONOME
285    pub metronome_pitch: u8,
286
287    /// Whether the metronome click has tonal characteristics or not. Default is `true` (enabled).
288    /// See Manual section 8.6.6 METRONOME
289    pub metronome_tonal: bool,
290
291    /// Whether the metronome is active. Default is `false`.
292    /// See Manual section 8.6.6 METRONOME
293    pub metronome_enabled: bool,
294}
295
296impl FromHashMap for MetronomeControlPage {
297    type A = String;
298    type B = String;
299    type T = Self;
300
301    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
302        Ok(Self {
303            metronome_time_signature: parse_hashmap_string_value::<u8>(
304                hmap,
305                "metronome_time_signature",
306                None,
307            )?,
308            metronome_time_signature_denominator: parse_hashmap_string_value::<u8>(
309                hmap,
310                "metronome_time_signature_denominator",
311                None,
312            )?,
313            metronome_preroll: parse_hashmap_string_value::<u8>(hmap, "metronome_preroll", None)?,
314            metronome_cue_volume: parse_hashmap_string_value::<u8>(
315                hmap,
316                "metronome_cue_volume",
317                None,
318            )?,
319            metronome_main_volume: parse_hashmap_string_value::<u8>(
320                hmap,
321                "metronome_main_volume",
322                None,
323            )?,
324            metronome_pitch: parse_hashmap_string_value::<u8>(hmap, "metronome_pitch", None)?,
325            metronome_tonal: parse_hashmap_string_value_bool(hmap, "metronome_tonal", None)?,
326            metronome_enabled: parse_hashmap_string_value_bool(hmap, "metronome_enabled", None)?,
327        })
328    }
329}
330
331/// `PROJECT` -> `CONTROL` -> `MIDI` -> `CONTROL` UI menu.
332
333#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
334pub struct MidiControlMidiPage {
335    /// Whether samples can be loaded in 24-bit depth (16 bit depth samples are always oaded as 16 bit).
336    /// `AUDIO CC IN` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CONTROL` UI menu.
337    /// Whether audio tracks respond to MIDI CC IN messages.
338    ///
339    /// See manual section 8.7.1 CONTROL.
340    pub midi_audio_track_cc_in: bool,
341
342    /// `AUDIO CC OUT` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CONTROL` UI menu.
343    /// Whether audio tracks send MIDI CC OUT messages.
344    /// Three options:
345    /// - `INT`: No messages sent, knobs only affect Octatrack settings.
346    /// - `EXT`: Sends CC OUT messages but they don't alter any Octatrack settings.
347    /// - `INT+EXT`: Simulataneously affects Octratack settings and sends CC OUT messages.
348    ///
349    /// See manual section 8.7.1 CONTROL.
350    pub midi_audio_track_cc_out: u8,
351
352    /// `AUDIO NOTE IN` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CONTROL` UI menu.
353    /// Whether to receive MIDI NOTE IN messages on Audio tracks and how the audio tracks
354    /// respond to those MIDI NOTE IN messages.
355    /// - **OFF**: midi note has no effet.
356    /// - **STANDARD**: standard note mapping (default).
357    /// - **FOLLOW TM**: Track's current trig mode affects audio tracks (track/chromatic/slots).
358    /// - **MAP/TRACK**: Uses MIDI MAP configuration on a per track basis (track/chromatic/slots
359    ///     disconnected from user trig mode of track).
360    pub midi_audio_track_note_in: u8,
361
362    /// `AUDIO NOTE OUT` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CONTROL` UI menu.
363    /// Whether audio tracks send MIDI NOTE OUT messages. Three options:
364    /// - `INT`: No messages sent, knobs only affect Octatrack settings.
365    /// - `EXT`: Sends NOTE OUT messages but they don't alter any Octatrack settings.
366    /// - `INT+EXT`: Simulataneously affects Octratack settings and sends NOTE OUT messages.
367    ///
368    /// See manual section 8.7.1 CONTROL.
369    pub midi_audio_track_note_out: u8,
370
371    /// Unknown. MIDI channel to MIDI Track CC In messages n (1 - 16) ?
372    pub midi_midi_track_cc_in: u8,
373}
374
375impl FromHashMap for MidiControlMidiPage {
376    type A = String;
377    type B = String;
378    type T = Self;
379
380    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
381        Ok(Self {
382            midi_audio_track_cc_in: parse_hashmap_string_value_bool(
383                hmap,
384                "midi_audio_trk_cc_in",
385                None,
386            )?,
387            midi_audio_track_cc_out: parse_hashmap_string_value::<u8>(
388                hmap,
389                "midi_audio_trk_cc_out",
390                None,
391            )?,
392            midi_audio_track_note_in: parse_hashmap_string_value::<u8>(
393                hmap,
394                "midi_audio_trk_note_in",
395                None,
396            )?,
397            midi_audio_track_note_out: parse_hashmap_string_value::<u8>(
398                hmap,
399                "midi_audio_trk_note_out",
400                None,
401            )?,
402            midi_midi_track_cc_in: parse_hashmap_string_value::<u8>(
403                hmap,
404                "midi_midi_trk_cc_in",
405                None,
406            )?,
407        })
408    }
409}
410
411/// `PROJECT` -> `CONTROL` -> `MIDI` -> `SYNC` UI menu.
412
413#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
414pub struct MidiSyncMidiPage {
415    /// `CLOCK SEND` setting.
416    /// Whether MIDI clock sending is enabled/disabled
417    /// See manual section 8.7.2 SYNC.
418    pub midi_clock_send: bool,
419
420    /// `CLOCK RECV` setting.
421    /// Whether MIDI clock receiving is enabled/disabled
422    /// See manual section 8.7.2 SYNC.
423    pub midi_clock_receive: bool,
424
425    /// `TRANS SEND` setting.
426    /// Whether MIDI transport sending is enabled/disabled
427    /// See manual section 8.7.2 SYNC.
428    pub midi_transport_send: bool,
429
430    /// `TRANS RECV` setting.
431    /// Whether MIDI transport receiving is enabled/disabled
432    /// See manual section 8.7.2 SYNC.
433    pub midi_transport_receive: bool,
434
435    /// `PROG CH SEND` setting.
436    /// Whether MIDI Program Change sending is enabled/disabled
437    /// See manual section 8.7.2 SYNC.
438    pub midi_progchange_send: bool,
439
440    /// `CHANNEL` setting.
441    /// Channel to send MIDI Program Change messages on. (-1, or between 1 - 16).
442    /// **NOTE**: should be set to `-1` when `midi_progchange_send` is disabled.
443    /// See manual section 8.7.2 SYNC.
444    pub midi_progchange_send_channel: ProjectMidiChannels,
445
446    /// `PROG CH RECEIVE` setting.
447    /// Whether MIDI Program Change receiveing is enabled/disabled
448    /// See manual section 8.7.2 SYNC.
449    pub midi_progchange_receive: bool,
450
451    /// `CHANNEL` setting.
452    /// Channel to receive MIDI Program Change messages on (-1 or between 1 - 16).
453    /// **NOTE**: should be set to `-1` when `midi_progchange_receive` is disabled.
454    /// See manual section 8.7.2 SYNC.
455    pub midi_progchange_receive_channel: ProjectMidiChannels,
456}
457
458impl FromHashMap for MidiSyncMidiPage {
459    type A = String;
460    type B = String;
461    type T = Self;
462
463    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
464        Ok(Self {
465            midi_clock_send: parse_hashmap_string_value_bool(hmap, "midi_clock_send", None)?,
466            midi_clock_receive: parse_hashmap_string_value_bool(hmap, "midi_clock_receive", None)?,
467            midi_transport_send: parse_hashmap_string_value_bool(
468                hmap,
469                "midi_transport_send",
470                None,
471            )?,
472            midi_transport_receive: parse_hashmap_string_value_bool(
473                hmap,
474                "midi_transport_receive",
475                None,
476            )?,
477            midi_progchange_send: parse_hashmap_string_value_bool(
478                hmap,
479                "midi_program_change_send",
480                None,
481            )?,
482            midi_progchange_send_channel: ProjectMidiChannels::from_value(
483                &parse_hashmap_string_value::<i8>(hmap, "midi_program_change_send_ch", None)?,
484            )?,
485            midi_progchange_receive: parse_hashmap_string_value_bool(
486                hmap,
487                "midi_program_change_receive",
488                None,
489            )?,
490            midi_progchange_receive_channel: ProjectMidiChannels::from_value(
491                &parse_hashmap_string_value::<i8>(hmap, "midi_program_change_receive_ch", None)?,
492            )?,
493        })
494    }
495}
496
497/// `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
498
499#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
500pub struct MidiChannelsMidiPage {
501    /// `TRIG CH 1` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
502    /// MIDI Channel to send MIDI Trig 1 messages to (1 - 16)
503    /// See manual section 8.7.3 CHANNELS.
504    pub midi_trig_ch1: u8,
505
506    /// `TRIG CH 2` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
507    /// MIDI Channel to send MIDI Trig 2 messages to (1 - 16)
508    /// See manual section 8.7.3 CHANNELS.
509    pub midi_trig_ch2: u8,
510
511    /// `TRIG CH 3` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
512    /// MIDI Channel to send MIDI Trig 3 messages to (1 - 16)
513    /// See manual section 8.7.3 CHANNELS.
514    pub midi_trig_ch3: u8,
515
516    /// `TRIG CH 4` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
517    /// MIDI Channel to send MIDI Trig 4 messages to (1 - 16)
518    /// See manual section 8.7.3 CHANNELS.
519    pub midi_trig_ch4: u8,
520
521    /// `TRIG CH 5` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
522    /// MIDI Channel to send MIDI Trig 5 messages to (1 - 16)
523    /// See manual section 8.7.3 CHANNELS.
524    pub midi_trig_ch5: u8,
525
526    /// `TRIG CH 6` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
527    /// MIDI Channel to send MIDI Trig 6 messages to (1 - 16)
528    /// See manual section 8.7.3 CHANNELS.
529    pub midi_trig_ch6: u8,
530
531    /// `TRIG CH 7` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
532    /// MIDI Channel to send MIDI Trig 7 messages to (1 - 16)
533    /// See manual section 8.7.3 CHANNELS.
534    pub midi_trig_ch7: u8,
535
536    /// `TRIG CH 8` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
537    /// MIDI Channel to send MIDI Trig 8 messages to (1 - 16)
538    /// See manual section 8.7.3 CHANNELS.
539    pub midi_trig_ch8: u8,
540
541    /// `AUTO CH` setting in `PROJECT` -> `CONTROL` -> `MIDI` -> `CHANNELS` UI menu.
542    /// Auto MIDI Channel (1 - 16)
543    /// See manual section 8.7.3 CHANNELS.
544    pub midi_auto_channel: u8,
545}
546
547impl FromHashMap for MidiChannelsMidiPage {
548    type A = String;
549    type B = String;
550    type T = Self;
551
552    fn from_hashmap(hmap: &HashMap<String, String>) -> RBoxErr<Self> {
553        Ok(Self {
554            midi_trig_ch1: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch1", None)?,
555            midi_trig_ch2: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch2", None)?,
556            midi_trig_ch3: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch3", None)?,
557            midi_trig_ch4: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch4", None)?,
558            midi_trig_ch5: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch5", None)?,
559            midi_trig_ch6: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch6", None)?,
560            midi_trig_ch7: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch7", None)?,
561            midi_trig_ch8: parse_hashmap_string_value::<u8>(hmap, "midi_trig_ch8", None)?,
562            midi_auto_channel: parse_hashmap_string_value::<u8>(hmap, "midi_auto_channel", None)?,
563        })
564    }
565}