1use crate::instruments::common::*;
2use crate::reader::*;
3use crate::version::*;
4use crate::writer::Writer;
5use num_enum::IntoPrimitive;
6use num_enum::TryFromPrimitive;
7
8use arr_macro::arr;
9
10use super::dests;
11use super::CommandPack;
12
13#[derive(Copy, Clone, PartialEq, Debug)]
14pub struct FmAlgo(pub u8);
15
16const FM_ALGO_STRINGS: [&str; 0x0C] = [
17 "A>B>C>D",
18 "[A+B]>C>D",
19 "[A>B+C]>D",
20 "[A>B+A>C]>D",
21 "[A+B+C]>D",
22 "[A>B>C]+D",
23 "[A>B>C]+[A>B>D]",
24 "[A>B]+[C>D]",
25 "[A>B]+[A>C]+[A>D]",
26 "[A>B]+[A>C]+D",
27 "[A>B]+C+D",
28 "A+B+C+D",
29];
30
31impl TryFrom<u8> for FmAlgo {
32 type Error = ParseError;
33
34 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
35 if (value as usize) < FM_ALGO_STRINGS.len() {
36 Ok(FmAlgo(value))
37 } else {
38 Err(ParseError(format!("Invalid fm algo {}", value)))
39 }
40 }
41}
42
43impl FmAlgo {
44 pub fn id(self) -> u8 {
45 let FmAlgo(v) = self;
46 v
47 }
48
49 pub fn str(self) -> &'static str {
50 FM_ALGO_STRINGS[self.id() as usize]
51 }
52}
53
54#[repr(u8)]
55#[allow(non_camel_case_types)]
56#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
57pub enum FMWave {
58 #[default]
59 SIN,
60 SW2,
61 SW3,
62 SW4,
63 SW5,
64 SW6,
65 TRI,
66 SAW,
67 SQR,
68 PUL,
69 IMP,
70 NOI,
71 NLP,
72 NHP,
73 NBP,
74 CLK,
75 W09,
77 W0A,
78 W0B,
79 W0C,
80 W0D,
81 W0E,
82 W0F,
83 W10,
84 W11,
85 W12,
86 W13,
87 W14,
88 W15,
89 W16,
90 W17,
91 W18,
92 W19,
93 W1A,
94 W1B,
95 W1C,
96 W1D,
97 W1E,
98 W1F,
99 W20,
100 W21,
101 W22,
102 W23,
103 W24,
104 W25,
105 W26,
106 W27,
107 W28,
108 W29,
109 W2A,
110 W2B,
111 W2C,
112 W2D,
113 W2E,
114 W2F,
115 W30,
116 W31,
117 W32,
118 W33,
119 W34,
120 W35,
121 W36,
122 W37,
123 W38,
124 W39,
125 W3A,
126 W3B,
127 W3C,
128 W3D,
129 W3E,
130 W3F,
131 W40,
132 W41,
133 W42,
134 W43,
135 W44,
136 W45,
137}
138
139#[rustfmt::skip] const FM_FX_COMMANDS : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 1] = [
141 "VOL",
142 "PIT",
143 "FIN",
144 "ALG",
145 "FM1",
146 "FM2",
147 "FM3",
148 "FM4",
149 "FLT",
150 "CUT",
151 "RES",
152 "AMP",
153 "LIM",
154 "PAN",
155 "DRY",
156
157 "SCH",
158 "SDL",
159 "SRV",
160
161 "FMP",
162];
163
164#[rustfmt::skip] const DESTINATIONS : [&'static str; 15] = [
166 dests::OFF,
167 dests::VOLUME,
168 dests::PITCH,
169
170 "MOD1",
171 "MOD2",
172 "MOD3",
173 "MOD4",
174 dests::CUTOFF,
175 dests::RES,
176 dests::AMP,
177 dests::PAN,
178 dests::MOD_AMT,
179 dests::MOD_RATE,
180 dests::MOD_BOTH,
181 dests::MOD_BINV,
182];
183
184#[derive(PartialEq, Debug, Default, Clone)]
185pub struct Operator {
186 pub shape: FMWave,
187 pub ratio: u8,
188 pub ratio_fine: u8,
189 pub level: u8,
190 pub feedback: u8,
191 pub retrigger: u8,
192 pub mod_a: u8,
193 pub mod_b: u8,
194}
195
196#[derive(PartialEq, Debug, Clone)]
197pub struct FMSynth {
198 pub number: u8,
199 pub name: String,
200 pub transpose: bool,
201 pub table_tick: u8,
202 pub synth_params: SynthParams,
203
204 pub algo: FmAlgo,
205 pub operators: [Operator; 4],
206 pub mod1: u8,
207 pub mod2: u8,
208 pub mod3: u8,
209 pub mod4: u8,
210}
211
212impl FMSynth {
213 const MOD_OFFSET: usize = 2;
214
215 pub fn command_name(&self, _ver: Version) -> &'static [&'static str] {
216 &FM_FX_COMMANDS
217 }
218
219 pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
220 &DESTINATIONS
221 }
222
223 pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
225 &COMMON_FILTER_TYPES
226 }
227
228 pub fn human_readable_filter(&self) -> &'static str {
229 COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
230 }
231
232 pub fn write(&self, ver: Version, w: &mut Writer) {
233 w.write_string(&self.name, 12);
234 w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
235 w.write(self.table_tick);
236 w.write(self.synth_params.volume);
237 w.write(self.synth_params.pitch);
238 w.write(self.synth_params.fine_tune);
239
240 w.write(self.algo.0);
241
242 for op in &self.operators {
243 w.write(op.shape.into());
244 }
245
246 for op in &self.operators {
247 w.write(op.ratio);
248 w.write(op.ratio_fine);
249 }
250
251 for op in &self.operators {
252 w.write(op.level);
253 w.write(op.feedback);
254 }
255
256 for op in &self.operators {
257 w.write(op.mod_a);
258 }
259
260 for op in &self.operators {
261 w.write(op.mod_b);
262 }
263
264 w.write(self.mod1);
265 w.write(self.mod2);
266 w.write(self.mod3);
267 w.write(self.mod4);
268
269 self.synth_params.write(ver, w, FMSynth::MOD_OFFSET);
270 }
271
272 pub fn from_reader(
273 ver: Version,
274 reader: &mut Reader,
275 number: u8,
276 version: Version,
277 ) -> M8Result<Self> {
278 let name = reader.read_string(12);
279 let transp_eq = TranspEq::from_version(ver, reader.read());
280 let table_tick = reader.read();
281 let volume = reader.read();
282 let pitch = reader.read();
283 let fine_tune = reader.read();
284
285 let algo = reader.read();
286 let mut operators: [Operator; 4] = arr![Operator::default(); 4];
287 if version.at_least(1, 4) {
288 for i in 0..4 {
289 let wav_code = reader.read();
290 operators[i].shape = FMWave::try_from(wav_code)
291 .map_err(|_| ParseError(format!("Invalid fm wave {}", wav_code)))?;
292 }
293 }
294 for i in 0..4 {
295 operators[i].ratio = reader.read();
296 operators[i].ratio_fine = reader.read();
297 }
298 for i in 0..4 {
299 operators[i].level = reader.read();
300 operators[i].feedback = reader.read();
301 }
302 for i in 0..4 {
303 operators[i].mod_a = reader.read();
304 }
305 for i in 0..4 {
306 operators[i].mod_b = reader.read();
307 }
308 let mod1 = reader.read();
309 let mod2 = reader.read();
310 let mod3 = reader.read();
311 let mod4 = reader.read();
312
313 let synth_params = if version.at_least(3, 0) {
314 SynthParams::from_reader3(
315 ver,
316 reader,
317 volume,
318 pitch,
319 fine_tune,
320 transp_eq.eq,
321 FMSynth::MOD_OFFSET,
322 )?
323 } else {
324 SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
325 };
326
327 Ok(FMSynth {
328 number,
329 name,
330 transpose: transp_eq.transpose,
331 table_tick,
332 synth_params,
333
334 algo: FmAlgo(algo),
335 operators,
336 mod1,
337 mod2,
338 mod3,
339 mod4,
340 })
341 }
342}