m8_file_parser/instruments/
hypersynth.rs1use 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] const 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] const 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] const 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] const 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] const 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] const 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 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}