m8_file_parser/instruments/
macrosynth.rs1use crate::instruments::common::*;
2use crate::reader::*;
3use crate::version::*;
4use crate::writer::Writer;
5use crate::SEND_COMMAND_NAMES;
6use crate::SEND_COMMAND_NAMES_6_2;
7use array_concat::concat_arrays;
8use num_enum::IntoPrimitive;
9use num_enum::TryFromPrimitive;
10
11use super::dests;
12use super::CommandPack;
13
14#[repr(u8)]
16#[allow(non_camel_case_types)]
17#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
18pub enum MacroSynthOsc {
19 #[default]
20 CSAW,
21 MORPH,
22 SAW_SQUARE,
23 SINE_TRIANGLE,
24 BUZZ,
25 SQUARE_SUB,
26 SAW_SUB,
27 SQUARE_SYNC,
28 SAW_SYNC,
29 TRIPLE_SAW,
30 TRIPLE_SQUARE,
31 TRIPLE_TRIANGLE,
32 TRIPLE_SIN,
33 TRIPLE_RNG,
34 SAW_SWARM,
35 SAW_COMB,
36 TOY,
37 DIGITAL_FILTER_LP,
38 DIGITAL_FILTER_PK,
39 DIGITAL_FILTER_BP,
40 DIGITAL_FILTER_HP,
41 VOSIM,
42 VOWEL,
43 VOWEL_FOF,
44 HARMONICS,
45 FM,
46 FEEDBACK_FM,
47 CHAOTIC_FEEDBACK_FM,
48 PLUCKED,
49 BOWED,
50 BLOWN,
51 FLUTED,
52 STRUCK_BELL,
53 STRUCK_DRUM,
54 KICK,
55 CYMBAL,
56 SNARE,
57 WAVETABLES,
58 WAVE_MAP,
59 WAV_LINE,
60 WAV_PARAPHONIC,
61 FILTERED_NOISE,
62 TWIN_PEAKS_NOISE,
63 CLOCKED_NOISE,
64 GRANULAR_CLOUD,
65 PARTICLE_NOISE,
66 DIGITAL_MOD,
67 MORSE_NOISE,
68}
69
70#[rustfmt::skip] const MACRO_SYNTH_COMMANDS_BASE : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
72 "VOL",
73 "PIT",
74 "FIN",
75 "OSC",
76 "TBR",
77 "COL",
78 "DEG",
79 "RED",
80 "FIL",
81 "CUT",
82 "RES",
83 "AMP",
84 "LIM",
85 "PAN",
86 "DRY"
87];
88
89#[rustfmt::skip] const MACRO_SYNTH_COMMANDS_EXTRA : [&'static str; 2] = [
91 "TRG",
92 "ERR"
93];
94
95const MACRO_SYNTH_COMMANDS : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
96 concat_arrays!(MACRO_SYNTH_COMMANDS_BASE, SEND_COMMAND_NAMES, MACRO_SYNTH_COMMANDS_EXTRA);
97
98const MACRO_SYNTH_COMMANDS_6_2 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
99 concat_arrays!(MACRO_SYNTH_COMMANDS_BASE, SEND_COMMAND_NAMES_6_2, MACRO_SYNTH_COMMANDS_EXTRA);
100
101#[rustfmt::skip] const DESTINATIONS : [&'static str; 15] = [
103 dests::OFF,
104 dests::VOLUME,
105 dests::PITCH,
106
107 "TIMBRE",
108 "COLOR",
109 dests::DEGRADE,
110 "REDUX",
111 dests::CUTOFF,
112 dests::RES,
113 dests::AMP,
114 dests::PAN,
115 dests::MOD_AMT,
116 dests::MOD_RATE,
117 dests::MOD_BOTH,
118 dests::MOD_BINV,
119];
120
121#[derive(PartialEq, Debug, Clone)]
122pub struct MacroSynth {
123 pub number: u8,
124 pub name: String, pub transpose: bool, pub table_tick: u8, pub synth_params: SynthParams, pub shape: MacroSynthOsc, pub timbre: u8, pub color: u8, pub degrade: u8, pub redux: u8, }
135
136impl MacroSynth {
137 pub const MOD_OFFSET: usize = 30;
138
139 pub fn command_name(&self, ver: Version) -> &'static [&'static str] {
140 if ver.at_least(6, 1) {
141 &MACRO_SYNTH_COMMANDS_6_2
142 } else {
143 &MACRO_SYNTH_COMMANDS
144 }
145 }
146
147 pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
148 &DESTINATIONS
149 }
150
151 pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
153 &super::common::COMMON_FILTER_TYPES
154 }
155
156 pub fn human_readable_filter(&self) -> &'static str {
157 COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
158 }
159
160 pub fn write(&self, ver: Version, w: &mut Writer) {
161 w.write_string(&self.name, 12);
162 w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
163 w.write(self.table_tick);
164 w.write(self.synth_params.volume);
165 w.write(self.synth_params.pitch);
166 w.write(self.synth_params.fine_tune);
167
168 w.write(self.shape.into());
169 w.write(self.timbre);
170 w.write(self.color);
171 w.write(self.degrade);
172 w.write(self.redux);
173
174 self.synth_params.write(ver, w, MacroSynth::MOD_OFFSET);
175 }
176
177 pub fn from_reader(
178 ver: Version,
179 reader: &mut Reader,
180 number: u8,
181 version: Version,
182 ) -> M8Result<Self> {
183 let ms_pos = reader.pos();
184 let name = reader.read_string(12);
185
186 let transp_eq = TranspEq::from_version(ver, reader.read());
187 let table_tick = reader.read();
188 let volume = reader.read();
189 let pitch = reader.read();
190 let fine_tune = reader.read();
191
192 let ofs_shape = reader.pos();
193 let shape = reader.read();
194 let timbre = reader.read();
195 let color = reader.read();
196 let degrade = reader.read();
197 let redux = reader.read();
198
199 let synth_params = if version.at_least(3, 0) {
200 SynthParams::from_reader3(
201 ver,
202 reader,
203 volume,
204 pitch,
205 fine_tune,
206 transp_eq.eq,
207 MacroSynth::MOD_OFFSET,
208 )?
209 } else {
210 SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
211 };
212
213 let nc = name.clone();
214 Ok(MacroSynth {
215 number,
216 name,
217 transpose: transp_eq.transpose,
218 table_tick,
219 synth_params,
220
221 shape: shape.try_into().map_err(|_| {
222 ParseError(format!(
223 "I{number:X} Wrong macrosynth@{ms_pos} ({nc}) shape {shape}@0x{ofs_shape}"
224 ))
225 })?,
226 timbre,
227 color,
228 degrade,
229 redux,
230 })
231 }
232}