m8_file_parser/instruments/
wavsynth.rs

1use 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/// Wavsynth wave shape
15#[repr(u8)]
16#[allow(non_camel_case_types)]
17#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
18pub enum WavShape {
19    #[default]
20    PULSE12,
21    PULSE25,
22    PULSE50,
23    PULSE75,
24    SAW,
25    TRIANGLE,
26    SINE,
27    NOISE_PITCHED,
28    NOISE,
29    WT_CRUSH,
30    WT_FOLDING,
31    WT_FREQ,
32    WT_FUZZY,
33    WT_GHOST,
34    WT_GRAPHIC,
35    WT_LFOPLAY,
36    WT_LIQUID,
37    WT_MORPHING,
38    WT_MYSTIC,
39    WT_STICKY,
40    WT_TIDAL,
41    WT_TIDY,
42    WT_TUBE,
43    WT_UMBRELLA,
44    WT_UNWIND,
45    WT_VIRAL,
46    WT_WAVES,
47    WT_DRIP,
48    WT_FROGGY,
49    WT_INSONIC,
50    WT_RADIUS,
51    WT_SCRATCH,
52    WT_SMOOTH,
53    WT_WOBBLE,
54    WT_ASIMMTRY,
55    WT_BLEEN,
56    WT_FRACTAL,
57    WT_GENTLE,
58    WT_HARMONIC,
59    WT_HYPNOTIC,
60    WT_ITERATIV,
61    WT_MICROWAV,
62    WT_PLAITS01,
63    WT_PLAITS02,
64    WT_RISEFALL,
65    WT_TONAL,
66    WT_TWINE,
67    WT_ALIEN,
68    WT_CYBERNET,
69    WT_DISORDR,
70    WT_FORMANT,
71    WT_HYPER,
72    WT_JAGGED,
73    WT_MIXED,
74    WT_MULTIPLY,
75    WT_NOWHERE,
76    WT_PINBALL,
77    WT_RINGS,
78    WT_SHIMMER,
79    WT_SPECTRAL,
80    WT_SPOOKY,
81    WT_TRANSFRM,
82    WT_TWISTED,
83    WT_VOCAL,
84    WT_WASHED,
85    WT_WONDER,
86    WT_WOWEE,
87    WT_ZAP,
88    WT_BRAIDS,
89    WT_VOXSYNTH,
90}
91
92#[derive(PartialEq, Debug, Clone)]
93pub struct WavSynth {
94    pub number: u8,
95    pub name: String,
96    pub transpose: bool,
97    pub table_tick: u8,
98    pub synth_params: SynthParams,
99
100    pub shape: WavShape,
101    pub size: u8,
102    pub mult: u8,
103    pub warp: u8,
104    pub scan: u8,
105}
106
107#[rustfmt::skip] // Keep constants with important order vertical for maintenance
108const WAVSYNTH_COMMAND_NAMES_BASE : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 3] = [
109    "VOL",
110    "PIT",
111    "FIN",
112    "OSC",
113    "SIZ",
114    "MUL",
115    "WRP",
116    "MIR",
117    "FIL",
118    "CUT",
119    "RES",
120    "AMP",
121    "LIM",
122    "PAN",
123    "DRY"
124];
125
126#[rustfmt::skip] // Keep constants with important order vertical for maintenance
127const WAVSYNTH_COMMAND_NAMES_EXTRA : [&'static str; 2] = [
128    "SNC",
129    "ERR"
130];
131
132const WAVSYNTH_COMMAND_NAMES : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
133    concat_arrays!(WAVSYNTH_COMMAND_NAMES_BASE, SEND_COMMAND_NAMES, WAVSYNTH_COMMAND_NAMES_EXTRA);
134
135const WAVSYNTH_COMMAND_NAMES_6_2 : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT + 2] =
136    concat_arrays!(WAVSYNTH_COMMAND_NAMES_BASE, SEND_COMMAND_NAMES_6_2, WAVSYNTH_COMMAND_NAMES_EXTRA);
137
138#[rustfmt::skip] // Keep constants with important order vertical for maintenance
139const DESTINATIONS : [&'static str; 15] = [
140    dests::OFF,
141    dests::VOLUME,
142    dests::PITCH,
143
144    "SIZE",
145    "MULT",
146    "WARP",
147    "SCAN",
148    dests::CUTOFF,
149    dests::RES,
150    dests::AMP,
151    dests::PAN,
152    dests::MOD_AMT,
153    dests::MOD_RATE,
154    dests::MOD_BOTH,
155    dests::MOD_BINV,
156];
157
158#[rustfmt::skip] // Keep constants with important order vertical for maintenance
159const WAVSYNTH_FILTER_TYPES : [&'static str; 12] = [
160    "OFF",
161    "LOWPASS",
162    "HIGHPAS",
163    "BANDPAS",
164    "BANDSTP",
165    "LP > HP",
166    "ZDF LP",
167    "ZDF HP",
168    "WAV LP",
169    "WAV HP",
170    "WAV BP",
171    "WAV BS"
172];
173
174impl WavSynth {
175    pub const MOD_OFFSET: usize = 30;
176
177    pub fn command_name(&self, ver: Version) -> &'static [&'static str] {
178        if ver.at_least(6, 1) {
179            &WAVSYNTH_COMMAND_NAMES_6_2
180        } else {
181            &WAVSYNTH_COMMAND_NAMES
182        }
183    }
184
185    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
186        &DESTINATIONS
187    }
188
189    /// List of all the applyable filter types for the instrument
190    pub fn filter_types(&self, _ver: Version) -> &'static [&'static str] {
191        &WAVSYNTH_FILTER_TYPES
192    }
193
194    pub fn human_readable_filter(&self) -> &'static str {
195        WAVSYNTH_FILTER_TYPES[self.synth_params.filter_type as usize]
196    }
197
198    pub fn write(&self, ver: Version, w: &mut Writer) {
199        w.write_string(&self.name[..], 12);
200        w.write(TranspEq::from(ver, self.transpose, self.synth_params.associated_eq).into());
201        w.write(self.table_tick);
202        w.write(self.synth_params.volume);
203        w.write(self.synth_params.pitch);
204        w.write(self.synth_params.fine_tune);
205
206        w.write(self.shape.into());
207        w.write(self.size);
208        w.write(self.mult);
209        w.write(self.warp);
210        w.write(self.scan);
211        self.synth_params.write(ver, w, WavSynth::MOD_OFFSET);
212    }
213
214    pub fn from_reader(
215        ver: Version,
216        reader: &mut Reader,
217        number: u8,
218        version: Version,
219    ) -> M8Result<Self> {
220        let name = reader.read_string(12);
221        let transp_eq = TranspEq::from_version(ver, reader.read());
222        let table_tick = reader.read();
223        let volume = reader.read();
224        let pitch = reader.read();
225        let fine_tune = reader.read();
226
227        let shape = reader.read();
228        let size = reader.read();
229        let mult = reader.read();
230        let warp = reader.read();
231        let scan = reader.read();
232        let synth_params = if version.at_least(3, 0) {
233            SynthParams::from_reader3(
234                ver,
235                reader,
236                volume,
237                pitch,
238                fine_tune,
239                transp_eq.eq,
240                WavSynth::MOD_OFFSET,
241            )?
242        } else {
243            SynthParams::from_reader2(reader, volume, pitch, fine_tune)?
244        };
245
246        Ok(WavSynth {
247            number,
248            name,
249            transpose: transp_eq.transpose,
250            table_tick,
251            synth_params,
252
253            shape: shape
254                .try_into()
255                .map_err(|_| ParseError(format!("Invalid wavsynth shape")))?,
256            size,
257            mult,
258            warp,
259            scan,
260        })
261    }
262}