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 constats 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 constats 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    pub fn human_readable_filter(&self) -> &'static str {
140        COMMON_FILTER_TYPES[self.synth_params.filter_type as usize]
141    }
142
143    pub fn write(&self, ver: Version, w: &mut Writer) {
144        w.write_string(&self.name, 12);
145        w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
146        w.write(self.table_tick);
147        w.write(self.synth_params.volume);
148        w.write(self.synth_params.pitch);
149        w.write(self.synth_params.fine_tune);
150
151        w.write(self.shape.into());
152        w.write(self.timbre);
153        w.write(self.color);
154        w.write(self.degrade);
155        w.write(self.redux);
156
157        self.synth_params.write(ver, w, MacroSynth::MOD_OFFSET);
158    }
159
160    pub fn from_reader(
161        ver: Version,
162        reader: &mut Reader,
163        number: u8,
164        version: Version,
165    ) -> M8Result<Self> {
166        let ms_pos = reader.pos();
167        let name = reader.read_string(12);
168
169        let transp_eq = TranspEq::from_version(ver, reader.read());
170        let table_tick = reader.read();
171        let volume = reader.read();
172        let pitch = reader.read();
173        let fine_tune = reader.read();
174
175        let ofs_shape = reader.pos();
176        let shape = reader.read();
177        let timbre = reader.read();
178        let color = reader.read();
179        let degrade = reader.read();
180        let redux = reader.read();
181
182        let synth_params = if version.at_least(3, 0) {
183            SynthParams::from_reader3(
184                ver,
185                reader,
186                volume,
187                pitch,
188                fine_tune,
189                transp_eq.eq,
190                MacroSynth::MOD_OFFSET,
191            )?
192        } else {
193            SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
194        };
195
196        let nc = name.clone();
197        Ok(MacroSynth {
198            number,
199            name,
200            transpose: transp_eq.transpose,
201            table_tick,
202            synth_params,
203
204            shape: shape.try_into().map_err(|_| {
205                ParseError(format!(
206                    "I{number:X} Wrong macrosynth@{ms_pos} ({nc}) shape {shape}@0x{ofs_shape}"
207                ))
208            })?,
209            timbre,
210            color,
211            degrade,
212            redux,
213        })
214    }
215}