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