m8_file_parser/instruments/
hypersynth.rs

1use super::common::SynthParams;
2use super::common::TranspEq;
3use super::common::COMMON_FILTER_TYPES;
4use super::dests;
5use super::CommandPack;
6use super::Version;
7use crate::reader::*;
8use crate::writer::Writer;
9use crate::FIRMWARE_6_0_SONG_VERSION;
10use crate::FIRMWARE_6_2_SONG_VERSION;
11use crate::SEND_COMMAND_NAMES;
12use crate::SEND_COMMAND_NAMES_6_2;
13
14use arr_macro::arr;
15use array_concat::concat_arrays;
16
17#[derive(PartialEq, Debug, Clone, Default)]
18pub struct Chord {
19    pub mask: u8,
20    pub offsets : [u8; 6]
21}
22
23impl Chord {
24    pub fn read(reader: &mut Reader) -> Self{
25        let mask = reader.read();
26
27        Self {
28            mask,
29            offsets: arr![reader.read(); 6]
30        }
31    }
32
33    pub fn write(&self, w: &mut Writer) {
34        w.write(self.mask);
35        for k in &self.offsets {
36            w.write(*k);
37        }
38    }
39
40    pub fn is_osc_on(&self, osc: usize) -> bool{
41        (self.mask & (1 << osc)) != 0
42    }
43
44    pub fn offset_str(&self, osc: usize) -> String {
45        if self.is_osc_on(osc) {
46            format!("{:02X}", self.offsets[osc])
47        } else {
48            String::from("--")
49        }
50    }
51}
52
53#[derive(PartialEq, Debug, Clone)]
54pub struct HyperSynth {
55    pub number: u8,
56    pub name: String,
57    pub transpose: bool,
58    pub table_tick: u8,
59    pub synth_params: SynthParams,
60
61    pub scale: u8,
62    pub default_chord: [u8; 7],
63    pub shift: u8,
64    pub swarm: u8,
65    pub width: u8,
66    pub subosc: u8,
67
68    pub chords: [Chord; 0x10]
69}
70
71#[rustfmt::skip] // Keep constants with important order vertical for maintenance
72const HYPERSYNTH_COMMAND_NAMES_BASE : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
73    "VOL",
74    "PIT",
75    "FIN",
76    "CRD",
77    "SHF",
78    "SWM",
79    "WID",
80    "SUB",
81    "FLT",
82    "CUT",
83    "RES",
84    "AMP",
85    "LIM",
86    "PAN",
87    "DRY"
88];
89    
90
91#[rustfmt::skip] // Keep constants with important order vertical for maintenance
92const HYPERSYNTH_COMMAND_NAMES : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
93    concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE, SEND_COMMAND_NAMES, ["CVO", "SNC"]);
94
95#[rustfmt::skip] // Keep constants with important order vertical for maintenance
96const HYPERSYNTH_COMMAND_NAMES_BASE_6 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
97    "VOL",
98    "PIT",
99    "FIN",
100    "CRD",
101    "CVO",
102    "SWM",
103    "WID",
104    "SUB",
105    "FLT",
106    "CUT",
107    "RES",
108    "AMP",
109    "LIM",
110    "PAN",
111    "DRY"
112];
113    
114
115#[rustfmt::skip] // Keep constants with important order vertical for maintenance
116const HYPERSYNTH_COMMAND_NAMES_6 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
117    concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE_6, SEND_COMMAND_NAMES, ["SNC", "ERR"]);
118
119#[rustfmt::skip] // Keep constants with important order vertical for maintenance
120const HYPERSYNTH_COMMAND_NAMES_6_2 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
121    concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE_6, SEND_COMMAND_NAMES_6_2, ["SNC", "ERR"]);
122
123#[rustfmt::skip] // Keep constants with important order vertical for maintenance
124const DESTINATIONS : [&'static str; 15] = [
125    dests::OFF,
126    dests::VOLUME,
127    dests::PITCH,
128
129    "SHIFT",
130    "SWARM",
131    "WIDTH",
132    "SUBOSC",
133    dests::CUTOFF,
134    dests::RES,
135    dests::AMP,
136    dests::PAN,
137    dests::MOD_AMT,
138    dests::MOD_RATE,
139    dests::MOD_BOTH,
140    dests::MOD_BINV,
141];
142
143impl HyperSynth {
144    const MOD_OFFSET: usize = 23;
145
146    pub fn command_name(&self, ver: Version) -> &'static [&'static str] {
147        if ver.after(&FIRMWARE_6_2_SONG_VERSION) {
148            &HYPERSYNTH_COMMAND_NAMES_6_2
149        } else if ver.after(&FIRMWARE_6_0_SONG_VERSION) {
150            &HYPERSYNTH_COMMAND_NAMES_6
151        } else {
152            &HYPERSYNTH_COMMAND_NAMES
153        }
154    }
155
156    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
157        &DESTINATIONS
158    }
159
160    /// List of all the applyable filter types for the instrument
161    pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
162        &super::common::COMMON_FILTER_TYPES
163    }
164
165    pub fn human_readable_filter(&self) -> &'static str {
166        COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
167    }
168
169    pub fn write(&self, ver: Version, w: &mut Writer) {
170        w.write_string(&self.name, 12);
171        w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
172        w.write(self.table_tick);
173        w.write(self.synth_params.volume);
174        w.write(self.synth_params.pitch);
175        w.write(self.synth_params.fine_tune);
176
177        for c in self.default_chord {
178            w.write(c);
179        }
180
181        w.write(self.scale);
182        w.write(self.shift);
183        w.write(self.swarm);
184        w.write(self.width);
185        w.write(self.subosc);
186
187        self.synth_params.write(ver, w, HyperSynth::MOD_OFFSET);
188
189        for chd in &self.chords {
190            chd.write(w);
191        }
192    }
193
194    pub fn from_reader(ver: Version, reader: &mut Reader, number: u8) -> M8Result<Self> {
195        let name = reader.read_string(12);
196        let transp_eq = TranspEq::from_version(ver, reader.read());
197        let table_tick = reader.read();
198        let volume = reader.read();
199        let pitch = reader.read();
200        let fine_tune = reader.read();
201
202        let default_chord = arr![reader.read(); 7];
203        let scale = reader.read();
204        let shift = reader.read();
205        let swarm = reader.read();
206        let width = reader.read();
207        let subosc = reader.read();
208        let synth_params = SynthParams::from_reader3(
209            ver,
210            reader,
211            volume,
212            pitch,
213            fine_tune,
214            transp_eq.eq,
215            HyperSynth::MOD_OFFSET,
216        )?;
217
218        let chords = arr![Chord::read(reader); 0x10];
219
220        Ok(HyperSynth {
221            number,
222            name,
223            transpose: transp_eq.transpose,
224            table_tick,
225            synth_params,
226
227            scale,
228            default_chord,
229            shift,
230            swarm,
231            width,
232            subosc,
233            chords
234        })
235    }
236}