ot_tools_io/projects/
states.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Type and parsing of a project state.
7//! e.g. the currently selected pattern/bank; whether a scene is muted; current muted tracks etc.
8//! Used in the [`crate::projects::ProjectFile`] type.
9
10use serde::{Deserialize, Serialize};
11
12use crate::projects::{
13    parse_hashmap_string_value, parse_hashmap_string_value_bool, string_to_hashmap,
14    ProjectParseError, SectionHeader,
15};
16
17/*
18[STATES]\r\nBANK=0\r\nPATTERN=0\r\nARRANGEMENT=0\r\nARRANGEMENT_MODE=0\r\nPART=0\r\nTRACK=0\r\nTRACK_OTHERMODE=0\r\nSCENE_A_MUTE=0\r\nSCENE_B_MUTE=0\r\nTRACK_CUE_MASK=0\r\nTRACK_MUTE_MASK=0\r\nTRACK_SOLO_MASK=0\r\nMIDI_TRACK_MUTE_MASK=0\r\nMIDI_TRACK_SOLO_MASK=0\r\nMIDI_MODE=0\r\n[/STATES]
19
20----
21
22[STATES]
23BANK=0
24PATTERN=0
25ARRANGEMENT=0
26ARRANGEMENT_MODE=0
27PART=0
28TRACK=0
29TRACK_OTHERMODE=0
30SCENE_A_MUTE=0
31SCENE_B_MUTE=0
32TRACK_CUE_MASK=0
33TRACK_MUTE_MASK=0
34TRACK_SOLO_MASK=0
35MIDI_TRACK_MUTE_MASK=0
36MIDI_TRACK_SOLO_MASK=0
37MIDI_MODE=0
38[/STATES]
39*/
40
41/// Project state from a parsed Octatrack `project.*` file.
42/// This is the current 'UX focus' state, i.e. what parts, patterns, tracks are currently selected etc.
43#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Default)]
44pub struct State {
45    /// Current active bank
46    pub bank: u8,
47
48    /// Current active pattern within a bank
49    pub pattern: u8,
50
51    /// Current active arrangement.
52    pub arrangement: u8,
53
54    /// Whether in arrangement mode or not.
55    pub arrangement_mode: u8, // dunno if this is a toggle or an enum
56
57    /// Current active part for the active pattern within a bank
58    pub part: u8,
59
60    /// Current active track
61    pub track: u8,
62
63    /// Unknown
64    pub track_othermode: u8, // WTFF is this?
65
66    /// Whether Scene A is disabled/enabled
67    pub scene_a_mute: bool,
68
69    /// Whether Scene B is disabled/enabled
70    pub scene_b_mute: bool,
71
72    /// Mask detailingg which CUE outputs are muted on Audio Tracks.  
73    pub track_cue_mask: u8,
74
75    /// Mask detailingg which MAIN outputs are muted on Audio Tracks.  
76    pub track_mute_mask: u8,
77
78    /// Mask detailingg which MAIN outputs are soloed on Audio Tracks.  
79    pub track_solo_mask: u8,
80
81    /// Mask detailingg which MIDI outputs are muted on MIDI  Tracks.  
82    pub midi_track_mute_mask: u8,
83
84    /// Mask detailingg which MIDI outputs are soloed on MIDI  Tracks.  
85    pub midi_track_solo_mask: u8,
86
87    // Unknown
88    pub midi_mode: u8,
89}
90
91impl std::str::FromStr for State {
92    type Err = ProjectParseError;
93
94    /// Load project 'state' data from the raw project ASCII file.
95    fn from_str(s: &str) -> Result<Self, Self::Err> {
96        let hmap = string_to_hashmap(s, &SectionHeader::States)?;
97
98        Ok(Self {
99            bank: parse_hashmap_string_value::<u8>(&hmap, "bank", None)?,
100            pattern: parse_hashmap_string_value::<u8>(&hmap, "pattern", None)?,
101            arrangement: parse_hashmap_string_value::<u8>(&hmap, "arrangement", None)?,
102            arrangement_mode: parse_hashmap_string_value::<u8>(&hmap, "arrangement_mode", None)?,
103            part: parse_hashmap_string_value::<u8>(&hmap, "part", None)?,
104            track: parse_hashmap_string_value::<u8>(&hmap, "track", None)?,
105            track_othermode: parse_hashmap_string_value::<u8>(&hmap, "track_othermode", None)?,
106            scene_a_mute: parse_hashmap_string_value_bool(&hmap, "scene_a_mute", None)?,
107            scene_b_mute: parse_hashmap_string_value_bool(&hmap, "scene_b_mute", None)?,
108            track_cue_mask: parse_hashmap_string_value::<u8>(&hmap, "track_cue_mask", None)?,
109            track_mute_mask: parse_hashmap_string_value::<u8>(&hmap, "track_mute_mask", None)?,
110            track_solo_mask: parse_hashmap_string_value::<u8>(&hmap, "track_solo_mask", None)?,
111            midi_track_mute_mask: parse_hashmap_string_value::<u8>(
112                &hmap,
113                "midi_track_mute_mask",
114                None,
115            )?,
116            midi_track_solo_mask: parse_hashmap_string_value::<u8>(
117                &hmap,
118                "midi_track_solo_mask",
119                None,
120            )?,
121            midi_mode: parse_hashmap_string_value::<u8>(&hmap, "midi_mode", None)?,
122        })
123    }
124}
125
126impl std::fmt::Display for State {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
128        let mut s = "".to_string();
129        s.push_str("[STATES]\r\n");
130        s.push_str(format!("BANK={}", self.bank).as_str());
131        s.push_str("\r\n");
132        s.push_str(format!("PATTERN={}", self.pattern).as_str());
133        s.push_str("\r\n");
134        s.push_str(format!("ARRANGEMENT={}", self.arrangement).as_str());
135        s.push_str("\r\n");
136        s.push_str(format!("ARRANGEMENT_MODE={}", self.arrangement_mode).as_str());
137        s.push_str("\r\n");
138        s.push_str(format!("PART={}", self.part).as_str());
139        s.push_str("\r\n");
140        s.push_str(format!("TRACK={}", self.track).as_str());
141        s.push_str("\r\n");
142        s.push_str(format!("TRACK_OTHERMODE={}", self.track_othermode).as_str());
143        s.push_str("\r\n");
144        s.push_str(format!("SCENE_A_MUTE={}", self.scene_a_mute as u8).as_str());
145        s.push_str("\r\n");
146        s.push_str(format!("SCENE_B_MUTE={}", self.scene_b_mute as u8).as_str());
147        s.push_str("\r\n");
148        s.push_str(format!("TRACK_CUE_MASK={}", self.track_cue_mask).as_str());
149        s.push_str("\r\n");
150        s.push_str(format!("TRACK_MUTE_MASK={}", self.track_mute_mask).as_str());
151        s.push_str("\r\n");
152        s.push_str(format!("TRACK_SOLO_MASK={}", self.track_solo_mask).as_str());
153        s.push_str("\r\n");
154        s.push_str(format!("MIDI_TRACK_MUTE_MASK={}", self.midi_track_mute_mask).as_str());
155        s.push_str("\r\n");
156        s.push_str(format!("MIDI_TRACK_SOLO_MASK={}", self.midi_track_solo_mask).as_str());
157        s.push_str("\r\n");
158        s.push_str(format!("MIDI_MODE={}", self.midi_mode).as_str());
159        s.push_str("\r\n[/STATES]");
160        write!(f, "{s:#}")
161    }
162}