m8_file_parser/instruments/
hypersynth.rs1use num_enum::{IntoPrimitive, TryFromPrimitive};
2use super::common::SynthParams;
3use super::common::TranspEq;
4use super::common::COMMON_FILTER_TYPES;
5use super::dests;
6use super::CommandPack;
7use super::Version;
8use crate::reader::*;
9use crate::writer::Writer;
10use crate::FIRMWARE_6_0_SONG_VERSION;
11use crate::FIRMWARE_6_2_SONG_VERSION;
12use crate::SEND_COMMAND_NAMES;
13use crate::SEND_COMMAND_NAMES_6_2;
14
15use arr_macro::arr;
16use array_concat::concat_arrays;
17
18#[derive(PartialEq, Debug, Clone, Default)]
19pub struct Chord {
20 pub mask: u8,
21 pub offsets : [u8; 6]
22}
23
24impl Chord {
25 pub fn read(reader: &mut Reader) -> Self{
26 let mask = reader.read();
27
28 Self {
29 mask,
30 offsets: arr![reader.read(); 6]
31 }
32 }
33
34 pub fn write(&self, w: &mut Writer) {
35 w.write(self.mask);
36 for k in &self.offsets {
37 w.write(*k);
38 }
39 }
40
41 pub fn is_osc_on(&self, osc: usize) -> bool{
42 (self.mask & (1 << osc)) != 0
43 }
44
45 pub fn offset_str(&self, osc: usize) -> String {
46 if self.is_osc_on(osc) {
47 format!("{:02X}", self.offsets[osc])
48 } else {
49 String::from("--")
50 }
51 }
52}
53
54#[repr(u8)]
55#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
56pub enum HyperSynthShape {
57 #[default]
58 Saw = 0x0,
59 SoftSaw = 0x1,
60 DarkSaw = 0x2,
61 SquareSoft = 0x3,
62 Triangle = 0x4,
63 Sine = 0x5,
64 Sine3x = 0x6,
65 SineFb = 0x7,
66 SineFold = 0x8,
67 SineRing = 0x9,
68 SineHalf = 0xA,
69 SineOrgan = 0xB
70}
71
72
73#[derive(PartialEq, Debug, Clone)]
74pub struct HyperSynth {
75 pub number: u8,
76 pub name: String,
77 pub transpose: bool,
78 pub table_tick: u8,
79 pub synth_params: SynthParams,
80
81 pub scale: u8,
82 pub default_chord: [u8; 7],
83 pub shift: u8,
84 pub swarm: u8,
85 pub width: u8,
86 pub subosc: u8,
87
88 pub chords: [Chord; 0x10],
89 pub shape: Option<HyperSynthShape>
90}
91
92#[rustfmt::skip] const HYPERSYNTH_COMMAND_NAMES_BASE : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
94 "VOL",
95 "PIT",
96 "FIN",
97 "CRD",
98 "SHF",
99 "SWM",
100 "WID",
101 "SUB",
102 "FLT",
103 "CUT",
104 "RES",
105 "AMP",
106 "LIM",
107 "PAN",
108 "DRY"
109];
110
111
112#[rustfmt::skip] const HYPERSYNTH_COMMAND_NAMES : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
114 concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE, SEND_COMMAND_NAMES, ["CVO", "SNC"]);
115
116#[rustfmt::skip] const HYPERSYNTH_COMMAND_NAMES_BASE_6 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
118 "VOL",
119 "PIT",
120 "FIN",
121 "CRD",
122 "CVO",
123 "SWM",
124 "WID",
125 "SUB",
126 "FLT",
127 "CUT",
128 "RES",
129 "AMP",
130 "LIM",
131 "PAN",
132 "DRY"
133];
134
135
136#[rustfmt::skip] const HYPERSYNTH_COMMAND_NAMES_6 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
138 concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE_6, SEND_COMMAND_NAMES, ["SNC", "ERR"]);
139
140#[rustfmt::skip] const HYPERSYNTH_COMMAND_NAMES_6_2 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
142 concat_arrays!(HYPERSYNTH_COMMAND_NAMES_BASE_6, SEND_COMMAND_NAMES_6_2, ["SNC", "ERR"]);
143
144#[rustfmt::skip] const DESTINATIONS : [&'static str; 15] = [
146 dests::OFF,
147 dests::VOLUME,
148 dests::PITCH,
149
150 "SHIFT",
151 "SWARM",
152 "WIDTH",
153 "SUBOSC",
154 dests::CUTOFF,
155 dests::RES,
156 dests::AMP,
157 dests::PAN,
158 dests::MOD_AMT,
159 dests::MOD_RATE,
160 dests::MOD_BOTH,
161 dests::MOD_BINV,
162];
163
164impl HyperSynth {
165 const MOD_OFFSET: usize = 23;
166
167 pub fn command_name(&self, ver: Version) -> &'static [&'static str] {
168 if ver.after(&FIRMWARE_6_2_SONG_VERSION) {
169 &HYPERSYNTH_COMMAND_NAMES_6_2
170 } else if ver.after(&FIRMWARE_6_0_SONG_VERSION) {
171 &HYPERSYNTH_COMMAND_NAMES_6
172 } else {
173 &HYPERSYNTH_COMMAND_NAMES
174 }
175 }
176
177 pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
178 &DESTINATIONS
179 }
180
181 pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
183 &super::common::COMMON_FILTER_TYPES
184 }
185
186 pub fn human_readable_filter(&self) -> &'static str {
187 COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
188 }
189
190 pub fn write(&self, ver: Version, w: &mut Writer) {
191 w.write_string(&self.name, 12);
192 w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
193 w.write(self.table_tick);
194 w.write(self.synth_params.volume);
195 w.write(self.synth_params.pitch);
196 w.write(self.synth_params.fine_tune);
197
198 for c in self.default_chord {
199 w.write(c);
200 }
201
202 w.write(self.scale);
203 w.write(self.shift);
204 w.write(self.swarm);
205 w.write(self.width);
206 w.write(self.subosc);
207
208 self.synth_params.write(ver, w, HyperSynth::MOD_OFFSET);
209
210 for chd in &self.chords {
211 chd.write(w);
212 }
213 }
214
215 pub fn from_reader(ver: Version, reader: &mut Reader, number: u8) -> M8Result<Self> {
216 let name = reader.read_string(12);
217 let transp_eq = TranspEq::from_version(ver, reader.read());
218 let table_tick = reader.read();
219 let volume = reader.read();
220 let pitch = reader.read();
221 let fine_tune = reader.read();
222
223 let default_chord = arr![reader.read(); 7];
224 let scale = reader.read();
225 let shift = reader.read();
226 let swarm = reader.read();
227 let width = reader.read();
228 let subosc = reader.read();
229 let synth_params = SynthParams::from_reader3(
230 ver,
231 reader,
232 volume,
233 pitch,
234 fine_tune,
235 transp_eq.eq,
236 HyperSynth::MOD_OFFSET,
237 )?;
238
239 let chords = arr![Chord::read(reader); 0x10];
240
241 let shape =
242 if ver.at_least(6, 6) {
243 let shape_byte = synth_params.shape;
244 let hsshape =
245 HyperSynthShape::try_from(shape_byte)
246 .map_err(|_| ParseError(String::from("Invalid hypersynth shape parameter")))?;
247
248 Some(hsshape)
249 } else {
250 None
251 };
252
253 Ok(HyperSynth {
254 number,
255 name,
256 transpose: transp_eq.transpose,
257 table_tick,
258 synth_params,
259
260 scale,
261 default_chord,
262 shift,
263 swarm,
264 width,
265 subosc,
266 chords,
267 shape
268 })
269 }
270}