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}