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