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
10mod control_menu;
11mod mixer;
12mod tempo;
13mod trig_mode_midi_tracks;
14
15pub use control_menu::{
16    AudioControlPage, ControlMenu, InputControlPage, MemoryControlPage, MetronomeControlPage,
17    MidiChannelsMidiPage, MidiControlMidiPage, MidiSequencerControlPage, MidiSubMenu,
18    MidiSyncMidiPage, SequencerControlPage,
19};
20pub use mixer::MixerMenu;
21pub use tempo::TempoMenu;
22pub use trig_mode_midi_tracks::MidiTrackTrigModes;
23
24use crate::projects::{
25    parse_hashmap_string_value_bool, string_to_hashmap, ProjectParseError, SectionHeader,
26};
27use crate::settings::MidiChannel;
28use serde::{Deserialize, Serialize};
29
30/*
31Example data:
32[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]
33----
34
35[SETTINGS]
36WRITEPROTECTED=0
37TEMPOx24=2880
38PATTERN_TEMPO_ENABLED=0
39MIDI_CLOCK_SEND=0
40MIDI_CLOCK_RECEIVE=0
41MIDI_TRANSPORT_SEND=0
42MIDI_TRANSPORT_RECEIVE=0
43MIDI_PROGRAM_CHANGE_SEND=0
44MIDI_PROGRAM_CHANGE_SEND_CH=-1
45MIDI_PROGRAM_CHANGE_RECEIVE=0
46MIDI_PROGRAM_CHANGE_RECEIVE_CH=-1
47MIDI_TRIG_CH1=0\r\nMIDI_TRIG_CH2=1
48MIDI_TRIG_CH3=2\r\nMIDI_TRIG_CH4=3
49MIDI_TRIG_CH5=4\r\nMIDI_TRIG_CH6=5
50MIDI_TRIG_CH7=6\r\nMIDI_TRIG_CH8=7
51MIDI_AUTO_CHANNEL=10
52MIDI_SOFT_THRU=0
53MIDI_AUDIO_TRK_CC_IN=1
54MIDI_AUDIO_TRK_CC_OUT=3
55MIDI_AUDIO_TRK_NOTE_IN=1
56MIDI_AUDIO_TRK_NOTE_OUT=3
57MIDI_MIDI_TRK_CC_IN=1
58PATTERN_CHANGE_CHAIN_BEHAVIOR=0
59PATTERN_CHANGE_AUTO_SILENCE_TRACKS=0
60PATTERN_CHANGE_AUTO_TRIG_LFOS=0
61LOAD_24BIT_FLEX=0
62DYNAMIC_RECORDERS=0
63RECORD_24BIT=0
64RESERVED_RECORDER_COUNT=8
65RESERVED_RECORDER_LENGTH=16
66INPUT_DELAY_COMPENSATION=0
67GATE_AB=127
68GATE_CD=127
69GAIN_AB=64
70GAIN_CD=64
71DIR_AB=0
72DIR_CD=0
73PHONES_MIX=64
74MAIN_TO_CUE=0
75MASTER_TRACK=0
76CUE_STUDIO_MODE=0
77MAIN_LEVEL=64
78CUE_LEVEL=64
79METRONOME_TIME_SIGNATURE=3
80METRONOME_TIME_SIGNATURE_DENOMINATOR=2
81METRONOME_PREROLL=0
82METRONOME_CUE_VOLUME=32
83METRONOME_MAIN_VOLUME=0
84METRONOME_PITCH=12
85METRONOME_TONAL=1
86METRONOME_ENABLED=0
87TRIG_MODE_MIDI=0
88TRIG_MODE_MIDI=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
95[/SETTINGS]
96*/
97
98/// Project settings read from a parsed Octatrack Project file
99#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
100pub struct Settings {
101    /// Whether the project can be written to (is currently being read/written when `true`)
102    pub write_protected: bool,
103
104    /// Current settings in the `Project`'s control menu UI.
105    pub control: ControlMenu,
106
107    /// Unknown: Whether MIDI 'Thru' is enabled/disabled?
108    pub midi_soft_thru: bool,
109
110    /// Current state of the settings in the Mixer Menu overview
111    pub mixer: MixerMenu,
112
113    /// Current state of the settings in the Tempo menu
114    pub tempo: TempoMenu,
115
116    /// Current selections for MIDI Track Trig Mode
117    pub midi_tracks_trig_mode: MidiTrackTrigModes,
118}
119
120impl Default for Settings {
121    fn default() -> Self {
122        Self {
123            write_protected: false,
124            control: ControlMenu {
125                audio: AudioControlPage {
126                    master_track: false,
127                    cue_studio_mode: false,
128                },
129                input: InputControlPage {
130                    gate_ab: 127,
131                    gate_cd: 127,
132                    input_delay_compensation: false,
133                },
134                sequencer: SequencerControlPage {
135                    pattern_change_chain_behaviour: 0,
136                    pattern_change_auto_silence_tracks: false,
137                    pattern_change_auto_trig_lfos: false,
138                },
139                midi_sequencer: MidiSequencerControlPage {},
140                memory: MemoryControlPage {
141                    load_24bit_flex: false,
142                    dynamic_recorders: false,
143                    record_24bit: false,
144                    reserved_recorder_count: 8,
145                    reserved_recorder_length: 16,
146                },
147                metronome: MetronomeControlPage {
148                    metronome_time_signature: 3,
149                    metronome_time_signature_denominator: 2,
150                    metronome_preroll: 0,
151                    metronome_cue_volume: 32,
152                    metronome_main_volume: 0,
153                    metronome_pitch: 12,
154                    metronome_tonal: true,
155                    metronome_enabled: false,
156                },
157                midi: MidiSubMenu {
158                    control: MidiControlMidiPage {
159                        midi_audio_track_cc_in: true,
160                        midi_audio_track_cc_out: 3,
161                        midi_audio_track_note_in: 1,
162                        midi_audio_track_note_out: 3,
163                        midi_midi_track_cc_in: 1,
164                    },
165                    sync: MidiSyncMidiPage {
166                        midi_clock_send: false,
167                        midi_clock_receive: false,
168                        midi_transport_send: false,
169                        midi_transport_receive: false,
170                        midi_progchange_send: false,
171                        midi_progchange_send_channel: MidiChannel::Disabled,
172                        midi_progchange_receive: false,
173                        midi_progchange_receive_channel: MidiChannel::Disabled,
174                    },
175                    channels: MidiChannelsMidiPage {
176                        midi_trig_ch1: 0,
177                        midi_trig_ch2: 1,
178                        midi_trig_ch3: 2,
179                        midi_trig_ch4: 3,
180                        midi_trig_ch5: 4,
181                        midi_trig_ch6: 5,
182                        midi_trig_ch7: 6,
183                        midi_trig_ch8: 7,
184                        midi_auto_channel: 10,
185                    },
186                },
187            },
188            midi_soft_thru: false,
189            mixer: MixerMenu {
190                gain_ab: 64,
191                gain_cd: 64,
192                dir_ab: 0,
193                dir_cd: 0,
194                phones_mix: 64,
195                main_to_cue: 0,
196                main_level: 64,
197                cue_level: 64,
198            },
199            tempo: TempoMenu {
200                tempo: 120,
201                pattern_tempo_enabled: false,
202            },
203            midi_tracks_trig_mode: MidiTrackTrigModes {
204                trig_mode_midi_track_1: 0,
205                trig_mode_midi_track_2: 0,
206                trig_mode_midi_track_3: 0,
207                trig_mode_midi_track_4: 0,
208                trig_mode_midi_track_5: 0,
209                trig_mode_midi_track_6: 0,
210                trig_mode_midi_track_7: 0,
211                trig_mode_midi_track_8: 0,
212            },
213        }
214    }
215}
216
217impl std::str::FromStr for Settings {
218    type Err = ProjectParseError;
219
220    /// Load project 'state' data from the raw project ASCII file.
221    fn from_str(s: &str) -> Result<Self, Self::Err> {
222        let hmap = string_to_hashmap(s, &SectionHeader::Settings)?;
223
224        let write_protected = parse_hashmap_string_value_bool(&hmap, "writeprotected", None)?;
225        // Unknown: Whether MIDI 'Thru' is enabled/disabled?
226        let midi_soft_thru = parse_hashmap_string_value_bool(&hmap, "midi_soft_thru", None)?;
227        let control = ControlMenu::try_from(&hmap)?;
228        let mixer = MixerMenu::try_from(&hmap)?;
229        let tempo = TempoMenu::try_from(&hmap)?;
230        let midi_tracks_trig_mode = MidiTrackTrigModes::try_from(&hmap)?;
231
232        Ok(Self {
233            write_protected,
234            midi_soft_thru,
235            //
236            control,
237            mixer,
238            tempo,
239            midi_tracks_trig_mode,
240        })
241    }
242}
243
244impl std::fmt::Display for Settings {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
246        let mut s = "".to_string();
247        s.push_str("[SETTINGS]");
248        s.push_str("\r\n");
249
250        s.push_str(format!("WRITEPROTECTED={}", self.write_protected as u8).as_str());
251        s.push_str("\r\n");
252        s.push_str(format!("TEMPOx24={}", self.tempo.tempo * 24).as_str());
253        s.push_str("\r\n");
254        s.push_str(
255            format!(
256                "PATTERN_TEMPO_ENABLED={}",
257                self.tempo.pattern_tempo_enabled as u8
258            )
259            .as_str(),
260        );
261        s.push_str("\r\n");
262
263        s.push_str(
264            format!(
265                "MIDI_CLOCK_SEND={}",
266                self.control.midi.sync.midi_clock_send as u8
267            )
268            .as_str(),
269        );
270        s.push_str("\r\n");
271        s.push_str(
272            format!(
273                "MIDI_CLOCK_RECEIVE={}",
274                self.control.midi.sync.midi_clock_receive as u8
275            )
276            .as_str(),
277        );
278        s.push_str("\r\n");
279        s.push_str(
280            format!(
281                "MIDI_TRANSPORT_SEND={}",
282                self.control.midi.sync.midi_transport_send as u8
283            )
284            .as_str(),
285        );
286        s.push_str("\r\n");
287        s.push_str(
288            format!(
289                "MIDI_TRANSPORT_RECEIVE={}",
290                self.control.midi.sync.midi_transport_receive as u8
291            )
292            .as_str(),
293        );
294        s.push_str("\r\n");
295        s.push_str(
296            format!(
297                "MIDI_PROGRAM_CHANGE_SEND={}",
298                self.control.midi.sync.midi_progchange_send as u8
299            )
300            .as_str(),
301        );
302        s.push_str("\r\n");
303        s.push_str(
304            format!(
305                "MIDI_PROGRAM_CHANGE_SEND_CH={}",
306                // REMINDER: THIS MUST BE SIGNED INT!
307                self.control.midi.sync.midi_progchange_send_channel as i8
308            )
309            .as_str(),
310        );
311        s.push_str("\r\n");
312        s.push_str(
313            format!(
314                "MIDI_PROGRAM_CHANGE_RECEIVE={}",
315                self.control.midi.sync.midi_progchange_receive as u8
316            )
317            .as_str(),
318        );
319        s.push_str("\r\n");
320        s.push_str(
321            format!(
322                "MIDI_PROGRAM_CHANGE_RECEIVE_CH={}",
323                // REMINDER: THIS MUST BE SIGNED INT!
324                self.control.midi.sync.midi_progchange_receive_channel as i8
325            )
326            .as_str(),
327        );
328        s.push_str("\r\n");
329
330        s.push_str(format!("MIDI_TRIG_CH1={}", self.control.midi.channels.midi_trig_ch1).as_str());
331        s.push_str("\r\n");
332        s.push_str(format!("MIDI_TRIG_CH2={}", self.control.midi.channels.midi_trig_ch2).as_str());
333        s.push_str("\r\n");
334        s.push_str(format!("MIDI_TRIG_CH3={}", self.control.midi.channels.midi_trig_ch3).as_str());
335        s.push_str("\r\n");
336        s.push_str(format!("MIDI_TRIG_CH4={}", self.control.midi.channels.midi_trig_ch4).as_str());
337        s.push_str("\r\n");
338        s.push_str(format!("MIDI_TRIG_CH5={}", self.control.midi.channels.midi_trig_ch5).as_str());
339        s.push_str("\r\n");
340        s.push_str(format!("MIDI_TRIG_CH6={}", self.control.midi.channels.midi_trig_ch6).as_str());
341        s.push_str("\r\n");
342        s.push_str(format!("MIDI_TRIG_CH7={}", self.control.midi.channels.midi_trig_ch7).as_str());
343        s.push_str("\r\n");
344        s.push_str(format!("MIDI_TRIG_CH8={}", self.control.midi.channels.midi_trig_ch8).as_str());
345        s.push_str("\r\n");
346        s.push_str(
347            format!(
348                "MIDI_AUTO_CHANNEL={}",
349                self.control.midi.channels.midi_auto_channel
350            )
351            .as_str(),
352        );
353        s.push_str("\r\n");
354
355        s.push_str(format!("MIDI_SOFT_THRU={}", self.midi_soft_thru as u8).as_str());
356        s.push_str("\r\n");
357
358        s.push_str(
359            format!(
360                "MIDI_AUDIO_TRK_CC_IN={}",
361                self.control.midi.control.midi_audio_track_cc_in as u8
362            )
363            .as_str(),
364        );
365        s.push_str("\r\n");
366        s.push_str(
367            format!(
368                "MIDI_AUDIO_TRK_CC_OUT={}",
369                self.control.midi.control.midi_audio_track_cc_out
370            )
371            .as_str(),
372        );
373        s.push_str("\r\n");
374        s.push_str(
375            format!("MIDI_AUDIO_TRK_NOTE_IN={}", {
376                self.control.midi.control.midi_audio_track_note_in
377            })
378            .as_str(),
379        );
380        s.push_str("\r\n");
381        s.push_str(
382            format!(
383                "MIDI_AUDIO_TRK_NOTE_OUT={}",
384                self.control.midi.control.midi_audio_track_note_out
385            )
386            .as_str(),
387        );
388        s.push_str("\r\n");
389        s.push_str(
390            format!(
391                "MIDI_MIDI_TRK_CC_IN={}",
392                self.control.midi.control.midi_midi_track_cc_in
393            )
394            .as_str(),
395        );
396        s.push_str("\r\n");
397
398        s.push_str(
399            format!(
400                "PATTERN_CHANGE_CHAIN_BEHAVIOR={}",
401                self.control.sequencer.pattern_change_chain_behaviour
402            )
403            .as_str(),
404        );
405        s.push_str("\r\n");
406        s.push_str(
407            format!(
408                "PATTERN_CHANGE_AUTO_SILENCE_TRACKS={}",
409                self.control.sequencer.pattern_change_auto_silence_tracks as u8
410            )
411            .as_str(),
412        );
413        s.push_str("\r\n");
414        s.push_str(
415            format!(
416                "PATTERN_CHANGE_AUTO_TRIG_LFOS={}",
417                self.control.sequencer.pattern_change_auto_trig_lfos as u8
418            )
419            .as_str(),
420        );
421        s.push_str("\r\n");
422
423        s.push_str(
424            format!(
425                "LOAD_24BIT_FLEX={}",
426                self.control.memory.load_24bit_flex as u8
427            )
428            .as_str(),
429        );
430        s.push_str("\r\n");
431        s.push_str(
432            format!(
433                "DYNAMIC_RECORDERS={}",
434                self.control.memory.dynamic_recorders as u8
435            )
436            .as_str(),
437        );
438        s.push_str("\r\n");
439        s.push_str(format!("RECORD_24BIT={}", self.control.memory.record_24bit as u8).as_str());
440        s.push_str("\r\n");
441        s.push_str(
442            format!(
443                "RESERVED_RECORDER_COUNT={}",
444                self.control.memory.reserved_recorder_count
445            )
446            .as_str(),
447        );
448        s.push_str("\r\n");
449        s.push_str(
450            format!(
451                "RESERVED_RECORDER_LENGTH={}",
452                self.control.memory.reserved_recorder_length
453            )
454            .as_str(),
455        );
456        s.push_str("\r\n");
457
458        s.push_str(
459            format!(
460                "INPUT_DELAY_COMPENSATION={}",
461                self.control.input.input_delay_compensation as u8
462            )
463            .as_str(),
464        );
465        s.push_str("\r\n");
466
467        s.push_str(format!("GATE_AB={}", self.control.input.gate_ab).as_str());
468        s.push_str("\r\n");
469        s.push_str(format!("GATE_CD={}", self.control.input.gate_cd).as_str());
470        s.push_str("\r\n");
471        s.push_str(format!("GAIN_AB={}", self.mixer.gain_ab).as_str());
472        s.push_str("\r\n");
473        s.push_str(format!("GAIN_CD={}", self.mixer.gain_cd).as_str());
474        s.push_str("\r\n");
475        s.push_str(format!("DIR_AB={}", self.mixer.dir_ab).as_str());
476        s.push_str("\r\n");
477        s.push_str(format!("DIR_CD={}", self.mixer.dir_cd).as_str());
478        s.push_str("\r\n");
479        s.push_str(format!("PHONES_MIX={}", self.mixer.phones_mix).as_str());
480        s.push_str("\r\n");
481        s.push_str(format!("MAIN_TO_CUE={}", self.mixer.main_to_cue).as_str());
482        s.push_str("\r\n");
483
484        s.push_str(format!("MASTER_TRACK={}", self.control.audio.master_track as u8).as_str());
485        s.push_str("\r\n");
486        s.push_str(
487            format!(
488                "CUE_STUDIO_MODE={}",
489                self.control.audio.cue_studio_mode as u8
490            )
491            .as_str(),
492        );
493        s.push_str("\r\n");
494
495        s.push_str(format!("MAIN_LEVEL={}", self.mixer.main_level).as_str());
496        s.push_str("\r\n");
497        s.push_str(format!("CUE_LEVEL={}", self.mixer.cue_level).as_str());
498        s.push_str("\r\n");
499
500        s.push_str(
501            format!(
502                "METRONOME_TIME_SIGNATURE={}",
503                self.control.metronome.metronome_time_signature
504            )
505            .as_str(),
506        );
507        s.push_str("\r\n");
508        s.push_str(
509            format!(
510                "METRONOME_TIME_SIGNATURE_DENOMINATOR={}",
511                self.control.metronome.metronome_time_signature_denominator
512            )
513            .as_str(),
514        );
515        s.push_str("\r\n");
516        s.push_str(
517            format!(
518                "METRONOME_PREROLL={}",
519                self.control.metronome.metronome_preroll
520            )
521            .as_str(),
522        );
523        s.push_str("\r\n");
524        s.push_str(
525            format!(
526                "METRONOME_CUE_VOLUME={}",
527                self.control.metronome.metronome_cue_volume
528            )
529            .as_str(),
530        );
531        s.push_str("\r\n");
532        s.push_str(
533            format!(
534                "METRONOME_MAIN_VOLUME={}",
535                self.control.metronome.metronome_main_volume
536            )
537            .as_str(),
538        );
539        s.push_str("\r\n");
540        s.push_str(format!("METRONOME_PITCH={}", self.control.metronome.metronome_pitch).as_str());
541        s.push_str("\r\n");
542        s.push_str(
543            format!(
544                "METRONOME_TONAL={}",
545                self.control.metronome.metronome_tonal as u8
546            )
547            .as_str(),
548        );
549        s.push_str("\r\n");
550        s.push_str(
551            format!(
552                "METRONOME_ENABLED={}",
553                self.control.metronome.metronome_enabled as u8
554            )
555            .as_str(),
556        );
557        s.push_str("\r\n");
558
559        s.push_str(
560            format!(
561                "TRIG_MODE_MIDI={}",
562                self.midi_tracks_trig_mode.trig_mode_midi_track_1
563            )
564            .as_str(),
565        );
566        s.push_str("\r\n");
567        s.push_str(
568            format!(
569                "TRIG_MODE_MIDI={}",
570                self.midi_tracks_trig_mode.trig_mode_midi_track_2
571            )
572            .as_str(),
573        );
574        s.push_str("\r\n");
575        s.push_str(
576            format!(
577                "TRIG_MODE_MIDI={}",
578                self.midi_tracks_trig_mode.trig_mode_midi_track_3
579            )
580            .as_str(),
581        );
582        s.push_str("\r\n");
583        s.push_str(
584            format!(
585                "TRIG_MODE_MIDI={}",
586                self.midi_tracks_trig_mode.trig_mode_midi_track_4
587            )
588            .as_str(),
589        );
590        s.push_str("\r\n");
591        s.push_str(
592            format!(
593                "TRIG_MODE_MIDI={}",
594                self.midi_tracks_trig_mode.trig_mode_midi_track_5
595            )
596            .as_str(),
597        );
598        s.push_str("\r\n");
599        s.push_str(
600            format!(
601                "TRIG_MODE_MIDI={}",
602                self.midi_tracks_trig_mode.trig_mode_midi_track_6
603            )
604            .as_str(),
605        );
606        s.push_str("\r\n");
607        s.push_str(
608            format!(
609                "TRIG_MODE_MIDI={}",
610                self.midi_tracks_trig_mode.trig_mode_midi_track_7
611            )
612            .as_str(),
613        );
614        s.push_str("\r\n");
615        s.push_str(
616            format!(
617                "TRIG_MODE_MIDI={}",
618                self.midi_tracks_trig_mode.trig_mode_midi_track_8
619            )
620            .as_str(),
621        );
622
623        s.push_str("\r\n[/SETTINGS]");
624
625        write!(f, "{s:#}")
626    }
627}