m8_file_parser/instruments/
macrosynth.rs

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