ot_tools_io/projects/
settings.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Type and parsing of a global project settings
7//! e.g. whether Track 8 is a master track or not.
8//! Used in the [`crate::projects::ProjectFile`] type.
9
10pub mod control_menu;
11pub mod mixer;
12pub mod tempo;
13pub mod trig_mode_midi_tracks;
14
15use control_menu::{
16    AudioControlPage, ControlMenu, InputControlPage, MemoryControlPage, MetronomeControlPage,
17    MidiChannelsMidiPage, MidiControlMidiPage, MidiSequencerControlPage, MidiSubMenu,
18    MidiSyncMidiPage, SequencerControlPage,
19};
20
21use mixer::MixerMenu;
22use tempo::TempoMenu;
23use trig_mode_midi_tracks::MidiTrackTrigModes;
24
25use serde::{Deserialize, Serialize};
26
27use crate::projects::{
28    parse_hashmap_string_value_bool, string_to_hashmap, FromHashMap, ProjectRawFileSection,
29};
30use crate::{OptionEnumValueConvert, OtToolsIoErrors, RBoxErr};
31
32/*
33Example data:
34[SETTINGS]\r\nWRITEPROTECTED=0\r\nTEMPOx24=2880\r\nPATTERN_TEMPO_ENABLED=0\r\nMIDI_CLOCK_SEND=0\r\nMIDI_CLOCK_RECEIVE=0\r\nMIDI_TRANSPORT_SEND=0\r\nMIDI_TRANSPORT_RECEIVE=0\r\nMIDI_PROGRAM_CHANGE_SEND=0\r\nMIDI_PROGRAM_CHANGE_SEND_CH=-1\r\nMIDI_PROGRAM_CHANGE_RECEIVE=0\r\nMIDI_PROGRAM_CHANGE_RECEIVE_CH=-1\r\nMIDI_TRIG_CH1=0\r\nMIDI_TRIG_CH2=1\r\nMIDI_TRIG_CH3=2\r\nMIDI_TRIG_CH4=3\r\nMIDI_TRIG_CH5=4\r\nMIDI_TRIG_CH6=5\r\nMIDI_TRIG_CH7=6\r\nMIDI_TRIG_CH8=7\r\nMIDI_AUTO_CHANNEL=10\r\nMIDI_SOFT_THRU=0\r\nMIDI_AUDIO_TRK_CC_IN=1\r\nMIDI_AUDIO_TRK_CC_OUT=3\r\nMIDI_AUDIO_TRK_NOTE_IN=1\r\nMIDI_AUDIO_TRK_NOTE_OUT=3\r\nMIDI_MIDI_TRK_CC_IN=1\r\nPATTERN_CHANGE_CHAIN_BEHAVIOR=0\r\nPATTERN_CHANGE_AUTO_SILENCE_TRACKS=0\r\nPATTERN_CHANGE_AUTO_TRIG_LFOS=0\r\nLOAD_24BIT_FLEX=0\r\nDYNAMIC_RECORDERS=0\r\nRECORD_24BIT=0\r\nRESERVED_RECORDER_COUNT=8\r\nRESERVED_RECORDER_LENGTH=16\r\nINPUT_DELAY_COMPENSATION=0\r\nGATE_AB=127\r\nGATE_CD=127\r\nGAIN_AB=64\r\nGAIN_CD=64\r\nDIR_AB=0\r\nDIR_CD=0\r\nPHONES_MIX=64\r\nMAIN_TO_CUE=0\r\nMASTER_TRACK=0\r\nCUE_STUDIO_MODE=0\r\nMAIN_LEVEL=64\r\nCUE_LEVEL=64\r\nMETRONOME_TIME_SIGNATURE=3\r\nMETRONOME_TIME_SIGNATURE_DENOMINATOR=2\r\nMETRONOME_PREROLL=0\r\nMETRONOME_CUE_VOLUME=32\r\nMETRONOME_MAIN_VOLUME=0\r\nMETRONOME_PITCH=12\r\nMETRONOME_TONAL=1\r\nMETRONOME_ENABLED=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\nTRIG_MODE_MIDI=0\r\n[/SETTINGS]
35----
36
37[SETTINGS]
38WRITEPROTECTED=0
39TEMPOx24=2880
40PATTERN_TEMPO_ENABLED=0
41MIDI_CLOCK_SEND=0
42MIDI_CLOCK_RECEIVE=0
43MIDI_TRANSPORT_SEND=0
44MIDI_TRANSPORT_RECEIVE=0
45MIDI_PROGRAM_CHANGE_SEND=0
46MIDI_PROGRAM_CHANGE_SEND_CH=-1
47MIDI_PROGRAM_CHANGE_RECEIVE=0
48MIDI_PROGRAM_CHANGE_RECEIVE_CH=-1
49MIDI_TRIG_CH1=0\r\nMIDI_TRIG_CH2=1
50MIDI_TRIG_CH3=2\r\nMIDI_TRIG_CH4=3
51MIDI_TRIG_CH5=4\r\nMIDI_TRIG_CH6=5
52MIDI_TRIG_CH7=6\r\nMIDI_TRIG_CH8=7
53MIDI_AUTO_CHANNEL=10
54MIDI_SOFT_THRU=0
55MIDI_AUDIO_TRK_CC_IN=1
56MIDI_AUDIO_TRK_CC_OUT=3
57MIDI_AUDIO_TRK_NOTE_IN=1
58MIDI_AUDIO_TRK_NOTE_OUT=3
59MIDI_MIDI_TRK_CC_IN=1
60PATTERN_CHANGE_CHAIN_BEHAVIOR=0
61PATTERN_CHANGE_AUTO_SILENCE_TRACKS=0
62PATTERN_CHANGE_AUTO_TRIG_LFOS=0
63LOAD_24BIT_FLEX=0
64DYNAMIC_RECORDERS=0
65RECORD_24BIT=0
66RESERVED_RECORDER_COUNT=8
67RESERVED_RECORDER_LENGTH=16
68INPUT_DELAY_COMPENSATION=0
69GATE_AB=127
70GATE_CD=127
71GAIN_AB=64
72GAIN_CD=64
73DIR_AB=0
74DIR_CD=0
75PHONES_MIX=64
76MAIN_TO_CUE=0
77MASTER_TRACK=0
78CUE_STUDIO_MODE=0
79MAIN_LEVEL=64
80CUE_LEVEL=64
81METRONOME_TIME_SIGNATURE=3
82METRONOME_TIME_SIGNATURE_DENOMINATOR=2
83METRONOME_PREROLL=0
84METRONOME_CUE_VOLUME=32
85METRONOME_MAIN_VOLUME=0
86METRONOME_PITCH=12
87METRONOME_TONAL=1
88METRONOME_ENABLED=0
89TRIG_MODE_MIDI=0
90TRIG_MODE_MIDI=0
91TRIG_MODE_MIDI=0
92TRIG_MODE_MIDI=0
93TRIG_MODE_MIDI=0
94TRIG_MODE_MIDI=0
95TRIG_MODE_MIDI=0
96TRIG_MODE_MIDI=0
97[/SETTINGS]
98*/
99
100/// MIDI Channel options in Project Settings Menu
101/// (should only be use where `Disabled` is an option in the menu!).
102#[derive(PartialEq, Debug, Clone, Default, Serialize, Deserialize, Copy)]
103#[repr(i8)]
104pub enum MidiChannel {
105    /// No MIDI Channel selected -- Project Menu -> Control -> Midi -> Sync
106    #[default]
107    Disabled = -1,
108    // 0 is skipped
109    /// MIDI CH 1
110    One = 1,
111    /// MIDI CH 2
112    Two = 2,
113    /// MIDI CH 3
114    Three = 3,
115    /// MIDI CH 4
116    Four = 4,
117    /// MIDI CH 5
118    Five = 5,
119    /// MIDI CH 6
120    Six = 6,
121    /// MIDI CH 7
122    Seven = 7,
123    /// MIDI CH 8
124    Eight = 8,
125    /// MIDI CH 9
126    Nine = 9,
127    /// MIDI CH 10
128    Ten = 10,
129    /// MIDI CH 11
130    Eleven = 11,
131    /// MIDI CH 12
132    Twelve = 12,
133    /// MIDI CH 13
134    Thirteen = 13,
135    /// MIDI CH 14
136    Fourteen = 14,
137    /// MIDI CH 15
138    Fifteen = 15,
139    /// MIDI CH 16
140    Sixteen = 16,
141}
142
143impl OptionEnumValueConvert<i8> for MidiChannel {
144    fn from_value(v: &i8) -> RBoxErr<Self> {
145        match v {
146            -1 => Ok(Self::Disabled),
147            1 => Ok(Self::One),
148            2 => Ok(Self::Two),
149            3 => Ok(Self::Three),
150            4 => Ok(Self::Four),
151            5 => Ok(Self::Five),
152            6 => Ok(Self::Six),
153            7 => Ok(Self::Seven),
154            8 => Ok(Self::Eight),
155            9 => Ok(Self::Nine),
156            10 => Ok(Self::Ten),
157            11 => Ok(Self::Eleven),
158            12 => Ok(Self::Twelve),
159            13 => Ok(Self::Thirteen),
160            14 => Ok(Self::Fourteen),
161            15 => Ok(Self::Fifteen),
162            16 => Ok(Self::Sixteen),
163            _ => Err(OtToolsIoErrors::NoMatchingOptionEnumValue.into()),
164        }
165    }
166
167    fn value(&self) -> RBoxErr<i8> {
168        Ok(*self as i8)
169    }
170}
171
172#[cfg(test)]
173#[allow(unused_imports)]
174mod test_spec {
175
176    mod value {
177        use crate::projects::settings::MidiChannel;
178        use crate::OptionEnumValueConvert;
179
180        #[test]
181        fn test_disabled() {
182            assert_eq!(MidiChannel::Disabled.value().unwrap(), -1);
183        }
184        #[test]
185        fn test_1() {
186            assert_eq!(MidiChannel::One.value().unwrap(), 1);
187        }
188        #[test]
189        fn test_2() {
190            assert_eq!(MidiChannel::Two.value().unwrap(), 2);
191        }
192        #[test]
193        fn test_3() {
194            assert_eq!(MidiChannel::Three.value().unwrap(), 3);
195        }
196        #[test]
197        fn test_4() {
198            assert_eq!(MidiChannel::Four.value().unwrap(), 4);
199        }
200        #[test]
201        fn test_5() {
202            assert_eq!(MidiChannel::Five.value().unwrap(), 5);
203        }
204        #[test]
205        fn test_6() {
206            assert_eq!(MidiChannel::Six.value().unwrap(), 6);
207        }
208        #[test]
209        fn test_7() {
210            assert_eq!(MidiChannel::Seven.value().unwrap(), 7);
211        }
212        #[test]
213        fn test_8() {
214            assert_eq!(MidiChannel::Eight.value().unwrap(), 8);
215        }
216        #[test]
217        fn test_9() {
218            assert_eq!(MidiChannel::Nine.value().unwrap(), 9);
219        }
220        #[test]
221        fn test_10() {
222            assert_eq!(MidiChannel::Ten.value().unwrap(), 10);
223        }
224        #[test]
225        fn test_11() {
226            assert_eq!(MidiChannel::Eleven.value().unwrap(), 11);
227        }
228        #[test]
229        fn test_12() {
230            assert_eq!(MidiChannel::Twelve.value().unwrap(), 12);
231        }
232        #[test]
233        fn test_13() {
234            assert_eq!(MidiChannel::Thirteen.value().unwrap(), 13);
235        }
236        #[test]
237        fn test_14() {
238            assert_eq!(MidiChannel::Fourteen.value().unwrap(), 14);
239        }
240        #[test]
241        fn test_15() {
242            assert_eq!(MidiChannel::Fifteen.value().unwrap(), 15);
243        }
244        #[test]
245        fn test_16() {
246            assert_eq!(MidiChannel::Sixteen.value().unwrap(), 16);
247        }
248    }
249
250    mod from_value {
251
252        use crate::projects::settings::MidiChannel;
253        use crate::OptionEnumValueConvert;
254
255        #[test]
256        fn test_error_1() {
257            assert!(MidiChannel::from_value(&100).is_err());
258        }
259        #[test]
260        fn test_error_2() {
261            assert!(MidiChannel::from_value(&0).is_err());
262        }
263        #[test]
264        fn test_disabled() {
265            assert_eq!(MidiChannel::Disabled, MidiChannel::from_value(&-1).unwrap());
266        }
267        #[test]
268        fn test_1() {
269            assert_eq!(MidiChannel::One, MidiChannel::from_value(&1).unwrap());
270        }
271        #[test]
272        fn test_2() {
273            assert_eq!(MidiChannel::Two, MidiChannel::from_value(&2).unwrap());
274        }
275        #[test]
276        fn test_3() {
277            assert_eq!(MidiChannel::Three, MidiChannel::from_value(&3).unwrap());
278        }
279        #[test]
280        fn test_4() {
281            assert_eq!(MidiChannel::Four, MidiChannel::from_value(&4).unwrap());
282        }
283        #[test]
284        fn test_5() {
285            assert_eq!(MidiChannel::Five, MidiChannel::from_value(&5).unwrap());
286        }
287        #[test]
288        fn test_6() {
289            assert_eq!(MidiChannel::Six, MidiChannel::from_value(&6).unwrap());
290        }
291        #[test]
292        fn test_7() {
293            assert_eq!(MidiChannel::Seven, MidiChannel::from_value(&7).unwrap());
294        }
295        #[test]
296        fn test_8() {
297            assert_eq!(MidiChannel::Eight, MidiChannel::from_value(&8).unwrap());
298        }
299        #[test]
300        fn test_9() {
301            assert_eq!(MidiChannel::Nine, MidiChannel::from_value(&9).unwrap());
302        }
303        #[test]
304        fn test_10() {
305            assert_eq!(MidiChannel::Ten, MidiChannel::from_value(&10).unwrap());
306        }
307        #[test]
308        fn test_11() {
309            assert_eq!(MidiChannel::Eleven, MidiChannel::from_value(&11).unwrap());
310        }
311        #[test]
312        fn test_12() {
313            assert_eq!(MidiChannel::Twelve, MidiChannel::from_value(&12).unwrap());
314        }
315        #[test]
316        fn test_13() {
317            assert_eq!(MidiChannel::Thirteen, MidiChannel::from_value(&13).unwrap());
318        }
319        #[test]
320        fn test_14() {
321            assert_eq!(MidiChannel::Fourteen, MidiChannel::from_value(&14).unwrap());
322        }
323        #[test]
324        fn test_15() {
325            assert_eq!(MidiChannel::Fifteen, MidiChannel::from_value(&15).unwrap());
326        }
327        #[test]
328        fn test_16() {
329            assert_eq!(MidiChannel::Sixteen, MidiChannel::from_value(&16).unwrap());
330        }
331    }
332}
333
334/// Project settings read from a parsed Octatrack Project file
335#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
336pub struct Settings {
337    /// Whether the project can be written to (is currently being read/written when `true`)
338    pub write_protected: bool,
339
340    /// Current settings in the `Project`'s control menu UI.
341    pub control: ControlMenu,
342
343    /// Unknown: Whether MIDI 'Thru' is enabled/disabled?
344    pub midi_soft_thru: bool,
345
346    /// Current state of the settings in the Mixer Menu overview
347    pub mixer: MixerMenu,
348
349    /// Current state of the settings in the Tempo menu
350    pub tempo: TempoMenu,
351
352    /// Current selections for MIDI Track Trig Mode
353    pub midi_tracks_trig_mode: MidiTrackTrigModes,
354}
355
356impl Default for Settings {
357    fn default() -> Self {
358        Self {
359            write_protected: false,
360            control: ControlMenu {
361                audio: AudioControlPage {
362                    master_track: false,
363                    cue_studio_mode: false,
364                },
365                input: InputControlPage {
366                    gate_ab: 127,
367                    gate_cd: 127,
368                    input_delay_compensation: false,
369                },
370                sequencer: SequencerControlPage {
371                    pattern_change_chain_behaviour: 0,
372                    pattern_change_auto_silence_tracks: false,
373                    pattern_change_auto_trig_lfos: false,
374                },
375                midi_sequencer: MidiSequencerControlPage {},
376                memory: MemoryControlPage {
377                    load_24bit_flex: false,
378                    dynamic_recorders: false,
379                    record_24bit: false,
380                    reserved_recorder_count: 8,
381                    reserved_recorder_length: 16,
382                },
383                metronome: MetronomeControlPage {
384                    metronome_time_signature: 3,
385                    metronome_time_signature_denominator: 2,
386                    metronome_preroll: 0,
387                    metronome_cue_volume: 32,
388                    metronome_main_volume: 0,
389                    metronome_pitch: 12,
390                    metronome_tonal: true,
391                    metronome_enabled: false,
392                },
393                midi: MidiSubMenu {
394                    control: MidiControlMidiPage {
395                        midi_audio_track_cc_in: true,
396                        midi_audio_track_cc_out: 3,
397                        midi_audio_track_note_in: 1,
398                        midi_audio_track_note_out: 3,
399                        midi_midi_track_cc_in: 1,
400                    },
401                    sync: MidiSyncMidiPage {
402                        midi_clock_send: false,
403                        midi_clock_receive: false,
404                        midi_transport_send: false,
405                        midi_transport_receive: false,
406                        midi_progchange_send: false,
407                        midi_progchange_send_channel: MidiChannel::Disabled,
408                        midi_progchange_receive: false,
409                        midi_progchange_receive_channel: MidiChannel::Disabled,
410                    },
411                    channels: MidiChannelsMidiPage {
412                        midi_trig_ch1: 0,
413                        midi_trig_ch2: 1,
414                        midi_trig_ch3: 2,
415                        midi_trig_ch4: 3,
416                        midi_trig_ch5: 4,
417                        midi_trig_ch6: 5,
418                        midi_trig_ch7: 6,
419                        midi_trig_ch8: 7,
420                        midi_auto_channel: 10,
421                    },
422                },
423            },
424            midi_soft_thru: false,
425            mixer: MixerMenu {
426                gain_ab: 64,
427                gain_cd: 64,
428                dir_ab: 0,
429                dir_cd: 0,
430                phones_mix: 64,
431                main_to_cue: 0,
432                main_level: 64,
433                cue_level: 64,
434            },
435            tempo: TempoMenu {
436                tempo: 120,
437                pattern_tempo_enabled: false,
438            },
439            midi_tracks_trig_mode: MidiTrackTrigModes {
440                trig_mode_midi_track_1: 0,
441                trig_mode_midi_track_2: 0,
442                trig_mode_midi_track_3: 0,
443                trig_mode_midi_track_4: 0,
444                trig_mode_midi_track_5: 0,
445                trig_mode_midi_track_6: 0,
446                trig_mode_midi_track_7: 0,
447                trig_mode_midi_track_8: 0,
448            },
449        }
450    }
451}
452
453impl std::str::FromStr for Settings {
454    type Err = Box<dyn std::error::Error>;
455
456    /// Load project 'state' data from the raw project ASCII file.
457    fn from_str(s: &str) -> Result<Self, Self::Err> {
458        let hmap = string_to_hashmap(s, &ProjectRawFileSection::Settings)?;
459
460        let write_protected = parse_hashmap_string_value_bool(&hmap, "writeprotected", None)?;
461        // Unknown: Whether MIDI 'Thru' is enabled/disabled?
462        let midi_soft_thru = parse_hashmap_string_value_bool(&hmap, "midi_soft_thru", None)?;
463        let control = ControlMenu::from_hashmap(&hmap)?;
464        let mixer = MixerMenu::from_hashmap(&hmap)?;
465        let tempo = TempoMenu::from_hashmap(&hmap)?;
466        let midi_tracks_trig_mode = MidiTrackTrigModes::from_hashmap(&hmap)?;
467
468        Ok(Self {
469            write_protected,
470            midi_soft_thru,
471            //
472            control,
473            mixer,
474            tempo,
475            midi_tracks_trig_mode,
476        })
477    }
478}
479
480impl std::fmt::Display for Settings {
481    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
482        let mut s = "".to_string();
483        s.push_str("[SETTINGS]");
484        s.push_str("\r\n");
485
486        s.push_str(format!("WRITEPROTECTED={}", self.write_protected as u8).as_str());
487        s.push_str("\r\n");
488        s.push_str(format!("TEMPOx24={}", self.tempo.tempo * 24).as_str());
489        s.push_str("\r\n");
490        s.push_str(
491            format!(
492                "PATTERN_TEMPO_ENABLED={}",
493                self.tempo.pattern_tempo_enabled as u8
494            )
495            .as_str(),
496        );
497        s.push_str("\r\n");
498
499        s.push_str(
500            format!(
501                "MIDI_CLOCK_SEND={}",
502                self.control.midi.sync.midi_clock_send as u8
503            )
504            .as_str(),
505        );
506        s.push_str("\r\n");
507        s.push_str(
508            format!(
509                "MIDI_CLOCK_RECEIVE={}",
510                self.control.midi.sync.midi_clock_receive as u8
511            )
512            .as_str(),
513        );
514        s.push_str("\r\n");
515        s.push_str(
516            format!(
517                "MIDI_TRANSPORT_SEND={}",
518                self.control.midi.sync.midi_transport_send as u8
519            )
520            .as_str(),
521        );
522        s.push_str("\r\n");
523        s.push_str(
524            format!(
525                "MIDI_TRANSPORT_RECEIVE={}",
526                self.control.midi.sync.midi_transport_receive as u8
527            )
528            .as_str(),
529        );
530        s.push_str("\r\n");
531        s.push_str(
532            format!(
533                "MIDI_PROGRAM_CHANGE_SEND={}",
534                self.control.midi.sync.midi_progchange_send as u8
535            )
536            .as_str(),
537        );
538        s.push_str("\r\n");
539        s.push_str(
540            format!(
541                "MIDI_PROGRAM_CHANGE_SEND_CH={}",
542                self.control
543                    .midi
544                    .sync
545                    .midi_progchange_send_channel
546                    .value()
547                    .unwrap()
548            )
549            .as_str(),
550        );
551        s.push_str("\r\n");
552        s.push_str(
553            format!(
554                "MIDI_PROGRAM_CHANGE_RECEIVE={}",
555                self.control.midi.sync.midi_progchange_receive as u8
556            )
557            .as_str(),
558        );
559        s.push_str("\r\n");
560        s.push_str(
561            format!(
562                "MIDI_PROGRAM_CHANGE_RECEIVE_CH={}",
563                self.control
564                    .midi
565                    .sync
566                    .midi_progchange_receive_channel
567                    .value()
568                    .unwrap()
569            )
570            .as_str(),
571        );
572        s.push_str("\r\n");
573
574        s.push_str(format!("MIDI_TRIG_CH1={}", self.control.midi.channels.midi_trig_ch1).as_str());
575        s.push_str("\r\n");
576        s.push_str(format!("MIDI_TRIG_CH2={}", self.control.midi.channels.midi_trig_ch2).as_str());
577        s.push_str("\r\n");
578        s.push_str(format!("MIDI_TRIG_CH3={}", self.control.midi.channels.midi_trig_ch3).as_str());
579        s.push_str("\r\n");
580        s.push_str(format!("MIDI_TRIG_CH4={}", self.control.midi.channels.midi_trig_ch4).as_str());
581        s.push_str("\r\n");
582        s.push_str(format!("MIDI_TRIG_CH5={}", self.control.midi.channels.midi_trig_ch5).as_str());
583        s.push_str("\r\n");
584        s.push_str(format!("MIDI_TRIG_CH6={}", self.control.midi.channels.midi_trig_ch6).as_str());
585        s.push_str("\r\n");
586        s.push_str(format!("MIDI_TRIG_CH7={}", self.control.midi.channels.midi_trig_ch7).as_str());
587        s.push_str("\r\n");
588        s.push_str(format!("MIDI_TRIG_CH8={}", self.control.midi.channels.midi_trig_ch8).as_str());
589        s.push_str("\r\n");
590        s.push_str(
591            format!(
592                "MIDI_AUTO_CHANNEL={}",
593                self.control.midi.channels.midi_auto_channel
594            )
595            .as_str(),
596        );
597        s.push_str("\r\n");
598
599        s.push_str(format!("MIDI_SOFT_THRU={}", self.midi_soft_thru as u8).as_str());
600        s.push_str("\r\n");
601
602        s.push_str(
603            format!(
604                "MIDI_AUDIO_TRK_CC_IN={}",
605                self.control.midi.control.midi_audio_track_cc_in as u8
606            )
607            .as_str(),
608        );
609        s.push_str("\r\n");
610        s.push_str(
611            format!(
612                "MIDI_AUDIO_TRK_CC_OUT={}",
613                self.control.midi.control.midi_audio_track_cc_out
614            )
615            .as_str(),
616        );
617        s.push_str("\r\n");
618        s.push_str(
619            format!("MIDI_AUDIO_TRK_NOTE_IN={}", {
620                self.control.midi.control.midi_audio_track_note_in
621            })
622            .as_str(),
623        );
624        s.push_str("\r\n");
625        s.push_str(
626            format!(
627                "MIDI_AUDIO_TRK_NOTE_OUT={}",
628                self.control.midi.control.midi_audio_track_note_out
629            )
630            .as_str(),
631        );
632        s.push_str("\r\n");
633        s.push_str(
634            format!(
635                "MIDI_MIDI_TRK_CC_IN={}",
636                self.control.midi.control.midi_midi_track_cc_in
637            )
638            .as_str(),
639        );
640        s.push_str("\r\n");
641
642        s.push_str(
643            format!(
644                "PATTERN_CHANGE_CHAIN_BEHAVIOR={}",
645                self.control.sequencer.pattern_change_chain_behaviour
646            )
647            .as_str(),
648        );
649        s.push_str("\r\n");
650        s.push_str(
651            format!(
652                "PATTERN_CHANGE_AUTO_SILENCE_TRACKS={}",
653                self.control.sequencer.pattern_change_auto_silence_tracks as u8
654            )
655            .as_str(),
656        );
657        s.push_str("\r\n");
658        s.push_str(
659            format!(
660                "PATTERN_CHANGE_AUTO_TRIG_LFOS={}",
661                self.control.sequencer.pattern_change_auto_trig_lfos as u8
662            )
663            .as_str(),
664        );
665        s.push_str("\r\n");
666
667        s.push_str(
668            format!(
669                "LOAD_24BIT_FLEX={}",
670                self.control.memory.load_24bit_flex as u8
671            )
672            .as_str(),
673        );
674        s.push_str("\r\n");
675        s.push_str(
676            format!(
677                "DYNAMIC_RECORDERS={}",
678                self.control.memory.dynamic_recorders as u8
679            )
680            .as_str(),
681        );
682        s.push_str("\r\n");
683        s.push_str(format!("RECORD_24BIT={}", self.control.memory.record_24bit as u8).as_str());
684        s.push_str("\r\n");
685        s.push_str(
686            format!(
687                "RESERVED_RECORDER_COUNT={}",
688                self.control.memory.reserved_recorder_count
689            )
690            .as_str(),
691        );
692        s.push_str("\r\n");
693        s.push_str(
694            format!(
695                "RESERVED_RECORDER_LENGTH={}",
696                self.control.memory.reserved_recorder_length
697            )
698            .as_str(),
699        );
700        s.push_str("\r\n");
701
702        s.push_str(
703            format!(
704                "INPUT_DELAY_COMPENSATION={}",
705                self.control.input.input_delay_compensation as u8
706            )
707            .as_str(),
708        );
709        s.push_str("\r\n");
710
711        s.push_str(format!("GATE_AB={}", self.control.input.gate_ab).as_str());
712        s.push_str("\r\n");
713        s.push_str(format!("GATE_CD={}", self.control.input.gate_cd).as_str());
714        s.push_str("\r\n");
715        s.push_str(format!("GAIN_AB={}", self.mixer.gain_ab).as_str());
716        s.push_str("\r\n");
717        s.push_str(format!("GAIN_CD={}", self.mixer.gain_cd).as_str());
718        s.push_str("\r\n");
719        s.push_str(format!("DIR_AB={}", self.mixer.dir_ab).as_str());
720        s.push_str("\r\n");
721        s.push_str(format!("DIR_CD={}", self.mixer.dir_cd).as_str());
722        s.push_str("\r\n");
723        s.push_str(format!("PHONES_MIX={}", self.mixer.phones_mix).as_str());
724        s.push_str("\r\n");
725        s.push_str(format!("MAIN_TO_CUE={}", self.mixer.main_to_cue).as_str());
726        s.push_str("\r\n");
727
728        s.push_str(format!("MASTER_TRACK={}", self.control.audio.master_track as u8).as_str());
729        s.push_str("\r\n");
730        s.push_str(
731            format!(
732                "CUE_STUDIO_MODE={}",
733                self.control.audio.cue_studio_mode as u8
734            )
735            .as_str(),
736        );
737        s.push_str("\r\n");
738
739        s.push_str(format!("MAIN_LEVEL={}", self.mixer.main_level).as_str());
740        s.push_str("\r\n");
741        s.push_str(format!("CUE_LEVEL={}", self.mixer.cue_level).as_str());
742        s.push_str("\r\n");
743
744        s.push_str(
745            format!(
746                "METRONOME_TIME_SIGNATURE={}",
747                self.control.metronome.metronome_time_signature
748            )
749            .as_str(),
750        );
751        s.push_str("\r\n");
752        s.push_str(
753            format!(
754                "METRONOME_TIME_SIGNATURE_DENOMINATOR={}",
755                self.control.metronome.metronome_time_signature_denominator
756            )
757            .as_str(),
758        );
759        s.push_str("\r\n");
760        s.push_str(
761            format!(
762                "METRONOME_PREROLL={}",
763                self.control.metronome.metronome_preroll
764            )
765            .as_str(),
766        );
767        s.push_str("\r\n");
768        s.push_str(
769            format!(
770                "METRONOME_CUE_VOLUME={}",
771                self.control.metronome.metronome_cue_volume
772            )
773            .as_str(),
774        );
775        s.push_str("\r\n");
776        s.push_str(
777            format!(
778                "METRONOME_MAIN_VOLUME={}",
779                self.control.metronome.metronome_main_volume
780            )
781            .as_str(),
782        );
783        s.push_str("\r\n");
784        s.push_str(format!("METRONOME_PITCH={}", self.control.metronome.metronome_pitch).as_str());
785        s.push_str("\r\n");
786        s.push_str(
787            format!(
788                "METRONOME_TONAL={}",
789                self.control.metronome.metronome_tonal as u8
790            )
791            .as_str(),
792        );
793        s.push_str("\r\n");
794        s.push_str(
795            format!(
796                "METRONOME_ENABLED={}",
797                self.control.metronome.metronome_enabled as u8
798            )
799            .as_str(),
800        );
801        s.push_str("\r\n");
802
803        s.push_str(
804            format!(
805                "TRIG_MODE_MIDI={}",
806                self.midi_tracks_trig_mode.trig_mode_midi_track_1
807            )
808            .as_str(),
809        );
810        s.push_str("\r\n");
811        s.push_str(
812            format!(
813                "TRIG_MODE_MIDI={}",
814                self.midi_tracks_trig_mode.trig_mode_midi_track_2
815            )
816            .as_str(),
817        );
818        s.push_str("\r\n");
819        s.push_str(
820            format!(
821                "TRIG_MODE_MIDI={}",
822                self.midi_tracks_trig_mode.trig_mode_midi_track_3
823            )
824            .as_str(),
825        );
826        s.push_str("\r\n");
827        s.push_str(
828            format!(
829                "TRIG_MODE_MIDI={}",
830                self.midi_tracks_trig_mode.trig_mode_midi_track_4
831            )
832            .as_str(),
833        );
834        s.push_str("\r\n");
835        s.push_str(
836            format!(
837                "TRIG_MODE_MIDI={}",
838                self.midi_tracks_trig_mode.trig_mode_midi_track_5
839            )
840            .as_str(),
841        );
842        s.push_str("\r\n");
843        s.push_str(
844            format!(
845                "TRIG_MODE_MIDI={}",
846                self.midi_tracks_trig_mode.trig_mode_midi_track_6
847            )
848            .as_str(),
849        );
850        s.push_str("\r\n");
851        s.push_str(
852            format!(
853                "TRIG_MODE_MIDI={}",
854                self.midi_tracks_trig_mode.trig_mode_midi_track_7
855            )
856            .as_str(),
857        );
858        s.push_str("\r\n");
859        s.push_str(
860            format!(
861                "TRIG_MODE_MIDI={}",
862                self.midi_tracks_trig_mode.trig_mode_midi_track_8
863            )
864            .as_str(),
865        );
866
867        s.push_str("\r\n[/SETTINGS]");
868
869        write!(f, "{s:#}")
870    }
871}