m8_files/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 + 1] = [
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];
92
93#[rustfmt::skip] // Keep constants with important order vertical for maintenance
94const DESTINATIONS : [&'static str; 15] = [
95    dests::OFF,
96    dests::VOLUME,
97    dests::PITCH,
98
99    "TIMBRE",
100    "COLOR",
101    dests::DEGRADE,
102    "REDUX",
103    dests::CUTOFF,
104    dests::RES,
105    dests::AMP,
106    dests::PAN,
107    dests::MOD_AMT,
108    dests::MOD_RATE,
109    dests::MOD_BOTH,
110    dests::MOD_BINV,
111];
112
113#[derive(PartialEq, Debug, Clone)]
114pub struct MacroSynth {
115    pub number: u8,
116    pub name: String,              // 12
117    pub transpose: bool,           // 1
118    pub table_tick: u8,            // 1
119    pub synth_params: SynthParams, // 10
120
121    pub shape: MacroSynthOsc, // 1
122    pub timbre: u8,           // 1
123    pub color: u8,            // 1
124    pub degrade: u8,          // 1
125    pub redux: u8,            // 1
126}
127
128impl MacroSynth {
129    pub const MOD_OFFSET: usize = 30;
130
131    pub fn command_name(&self, _ver: Version) -> &'static [&'static str] {
132        &MACRO_SYNTH_COMMANDS
133    }
134
135    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
136        &DESTINATIONS
137    }
138
139    /// List of all the applyable filter types for the instrument
140    pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
141        &super::common::COMMON_FILTER_TYPES
142    }
143
144    pub fn human_readable_filter(&self) -> &'static str {
145        COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
146    }
147
148    pub fn write(&self, ver: Version, w: &mut Writer) {
149        w.write_string(&self.name, 12);
150        w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
151        w.write(self.table_tick);
152        w.write(self.synth_params.volume);
153        w.write(self.synth_params.pitch);
154        w.write(self.synth_params.fine_tune);
155
156        w.write(self.shape.into());
157        w.write(self.timbre);
158        w.write(self.color);
159        w.write(self.degrade);
160        w.write(self.redux);
161
162        self.synth_params.write(ver, w, MacroSynth::MOD_OFFSET);
163    }
164
165    pub fn from_reader(
166        ver: Version,
167        reader: &mut Reader,
168        number: u8,
169        version: Version,
170    ) -> M8Result<Self> {
171        let ms_pos = reader.pos();
172        let name = reader.read_string(12);
173
174        let transp_eq = TranspEq::from_version(ver, reader.read());
175        let table_tick = reader.read();
176        let volume = reader.read();
177        let pitch = reader.read();
178        let fine_tune = reader.read();
179
180        let ofs_shape = reader.pos();
181        let shape = reader.read();
182        let timbre = reader.read();
183        let color = reader.read();
184        let degrade = reader.read();
185        let redux = reader.read();
186
187        let synth_params = if version.at_least(3, 0) {
188            SynthParams::from_reader3(
189                ver,
190                reader,
191                volume,
192                pitch,
193                fine_tune,
194                transp_eq.eq,
195                MacroSynth::MOD_OFFSET,
196            )?
197        } else {
198            SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
199        };
200
201        let nc = name.clone();
202        Ok(MacroSynth {
203            number,
204            name,
205            transpose: transp_eq.transpose,
206            table_tick,
207            synth_params,
208
209            shape: shape.try_into().map_err(|_| {
210                ParseError(format!(
211                    "I{number:X} Wrong macrosynth@{ms_pos} ({nc}) shape {shape}@0x{ofs_shape}"
212                ))
213            })?,
214            timbre,
215            color,
216            degrade,
217            redux,
218        })
219    }
220}