m8_file_parser/
param_gatherer.rs

1use crate::*;
2
3/// Interface to gather and display parameters in a semi
4/// automated manner
5pub trait ParameterGatherer {
6    /// Display a hex value for the instrument
7    fn hex(self, name: &str, val: u8) -> Self;
8
9    /// Display a boolean value for the described element
10    fn bool(self, name: &str, val: bool) -> Self;
11
12    /// Display a floating point value.
13    fn float(self, name: &str, val: f64) -> Self;
14
15    /// Display a string
16    fn str(self, name: &str, val: &str) -> Self;
17
18    /// Write an enumeration, with an hex code and a string representation
19    /// alongside it.
20    fn enumeration(self, name: &str, hex: u8, val: &str) -> Self;
21
22    /// Enter a sub scope, the callback should use the nested gatherer
23    /// to write the arguments.
24    fn nest_f<F>(self, name: &str, f: F) -> Self
25        where F : FnOnce (Self) -> Self, Self : Sized;
26}
27
28/// Interface implementing generic description of M8 structures
29/// for human consumption.
30pub trait Describable {
31    /// Method called to describte the content of the structure in any gatherer.
32    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG;
33}
34
35/// Some pretty printing require a dictionary found elsewhere. Encode that
36pub trait DescribableWithDictionary {
37    fn describe_with_dic< PG : ParameterGatherer>(&self, pg: PG, dic: &[&'static str], ver: Version) -> PG;
38}
39
40impl Describable for EqBand {
41    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
42        return pg.float("GAIN", self.gain())
43          .float("FREQ", self.frequency() as f64)
44          .hex("Q", self.q)
45          .enumeration("TYPE", self.mode.eq_type_hex(), self.mode.type_str())
46          .enumeration("MODE", self.mode.eq_mode_hex(), self.mode.mode_str())
47    }
48}
49
50impl Describable for Equ {
51    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
52        return pg
53            .nest_f("LOW", |ii| self.low.describe(ii, ver))
54            .nest_f("MID", |ii| self.mid.describe(ii, ver))
55            .nest_f("HIGH", |ii| self.high.describe(ii, ver));
56    }
57}
58
59impl Describable for Operator {
60    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG{
61        return pg
62          .str("SHAPE", &format!("{:?}", self.shape))
63          .float("RATIO", (self.ratio as f64) + (self.ratio_fine as f64) / 100.0)
64          .hex("LEVEL", self.level)
65          .hex("FBK", self.feedback)
66          .hex("MOD_A", self.mod_a)
67          .hex("MOD_B", self.mod_b);
68    }
69}
70
71impl Describable for FMSynth {
72   fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
73        let pg = pg
74            .str(params::NAME, &self.name)
75            .bool(params::TRANSPOSE, self.transpose)
76            .hex(params::EQ, self.synth_params.associated_eq)
77            .hex(params::TBLTIC, self.table_tick)
78            .enumeration("ALG", self.algo.0, self.algo.str())
79            .nest_f("A", |ipg| self.operators[0].describe(ipg, ver))
80            .nest_f("B", |ipg| self.operators[1].describe(ipg, ver))
81            .nest_f("C", |ipg| self.operators[2].describe(ipg, ver))
82            .nest_f("D", |ipg| self.operators[3].describe(ipg,ver));
83
84        let pg = self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
85        return describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver);
86   }
87}
88
89impl Describable for Sampler {
90    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
91        let pg = pg
92            .str(params::NAME, &self.name)
93            .bool(params::TRANSPOSE, self.transpose)
94            .hex(params::TBLTIC, self.table_tick)
95            .hex(params::EQ, self.synth_params.associated_eq)
96            .str("SAMPLE", &self.sample_path)
97            .enumeration("PLAY", self.play_mode as u8, &format!("{:?}", self.play_mode))
98            .hex("SLICE", self.slice);
99
100        let pg = match self.play_mode {
101            SamplePlayMode::FWD |
102            SamplePlayMode::REV |
103            SamplePlayMode::FWDLOOP |
104            SamplePlayMode::REVLOOP |
105            SamplePlayMode::FWD_PP |
106            SamplePlayMode::REV_PP |
107            SamplePlayMode::OSC |
108            SamplePlayMode::OSC_REV |
109            SamplePlayMode::OSC_PP => {
110                pg.hex("START", self.start)
111                  .hex("LOOP ST", self.loop_start)
112                  .hex("LENGTH", self.length)
113                  .hex("DETUNE", self.synth_params.pitch)
114            },
115
116            SamplePlayMode::REPITCH |
117            SamplePlayMode::REP_REV |
118            SamplePlayMode::REP_PP => {
119                pg.hex("STEPS", self.synth_params.pitch)
120                  .hex("START", self.start)
121                  .hex("LOOP ST", self.loop_start)
122                  .hex("LENGTH", self.length)
123            },
124
125            SamplePlayMode::REP_BPM |
126            SamplePlayMode::BPM_REV |
127            SamplePlayMode::BPM_PP => {
128                pg.hex("BPM", self.synth_params.pitch)
129                  .hex("START", self.start)
130                  .hex("LOOP ST", self.loop_start)
131                  .hex("LENGTH", self.length)
132            }
133        };
134
135        let pg =
136            pg.hex("DEGRADE", self.degrade);
137
138        let pg = self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
139        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
140    }
141}
142
143impl Describable for WavSynth {
144   fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
145        let pg = pg
146          .str(params::NAME, &self.name)
147          .bool(params::TRANSPOSE, self.transpose)
148          .hex(params::TBLTIC, self.table_tick)
149          .hex(params::EQ, self.synth_params.associated_eq)
150          .enumeration("SHAPE", self.shape as u8, &format!("{:?}", self.shape))
151          .hex("SIZE", self.size)
152          .hex("MULT", self.mult)
153          .hex("WARP", self.warp)
154          .hex("SCAN", self.scan);
155
156        let pg =
157            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
158
159        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
160   }
161}
162
163impl Describable for Instrument {
164    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
165        match self {
166            Instrument::WavSynth(ws)     => 
167                pg.nest_f("WAVSYNTH", |ipg| ws.describe(ipg, ver)),
168            Instrument::MacroSynth(ms) =>
169                pg.nest_f("MACROSYN", |ipg| ms.describe(ipg, ver)),
170            Instrument::Sampler(s)        =>
171                pg.nest_f("SAMPLE", |ipg| s.describe(ipg, ver)),
172            Instrument::MIDIOut(mo)       =>
173                pg.nest_f("MIDIOUT", |ipg| mo.describe(ipg, ver)),
174            Instrument::FMSynth(fs)       =>
175                pg.nest_f("FMSYNTH", |ipg| fs.describe(ipg, ver)),
176            Instrument::HyperSynth(hs) =>
177                pg.nest_f("HYPERSYNTH", |ipg| hs.describe(ipg, ver)),
178            Instrument::External(ex) =>
179                pg.nest_f("EXTERNALINST", |ipg| ex.describe(ipg, ver)),
180            Instrument::None => pg
181        }
182    }
183}
184
185pub fn describe_succint<PG : ParameterGatherer>(instr: &Instrument, pg: PG, ver: Version) -> PG {
186    let (k, common) =
187        match instr {
188            Instrument::WavSynth(ws)     => ("WAVSYNTH", Some(&ws.synth_params)),
189            Instrument::MacroSynth(ms) => ("MACROSYN", Some(&ms.synth_params)),
190            Instrument::Sampler(s)        => ("SAMPLE", Some(&s.synth_params)),
191            Instrument::MIDIOut(_mo)       => ("MIDIOUT", None),
192            Instrument::FMSynth(fs)       => ("FMSYNTH", Some(&fs.synth_params)),
193            Instrument::HyperSynth(hs) => ("HYPERSYNTH", Some(&hs.synth_params)),
194            Instrument::External(ex) => ("EXTERNALINST", Some(&ex.synth_params)),
195            Instrument::None => ("NONE", None)
196        };
197
198    let pg = pg.str("KIND", k);
199    match common {
200        None => pg,
201        Some(c) => describe_succint_params(&c, pg, ver)
202    }
203}
204
205impl Describable for ExternalInst {
206    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
207        let port_str = self.human_readable_port();
208        let pg = pg
209          .str(params::NAME, &self.name)
210          .bool(params::TRANSPOSE, self.transpose)
211          .hex(params::EQ, self.synth_params.associated_eq)
212          .hex(params::TBLTIC, self.table_tick)
213
214          .enumeration("PORT", self.port, port_str)
215          .hex("CHANNEL", self.channel)
216          .hex("BANK", self.bank)
217          .hex("PROGRAM", self.program)
218          .nest_f(params::CCA, |ipg| self.cca.describe(ipg, ver))
219          .nest_f(params::CCB, |ipg| self.ccb.describe(ipg, ver))
220          .nest_f(params::CCC, |ipg| self.ccc.describe(ipg, ver))
221          .nest_f(params::CCD, |ipg| self.ccd.describe(ipg, ver));
222
223        let pg =
224            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
225
226        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
227    }
228}
229
230impl Describable for HyperSynth {
231    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
232        let dc = &self.default_chord;
233
234        let pg = pg
235          .str(params::NAME, &self.name)
236          .bool(params::TRANSPOSE, self.transpose)
237          .hex(params::EQ, self.synth_params.associated_eq)
238          .hex(params::SCALE, self.scale)
239          .str("CHORD", &format!("{:02X} | {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", dc[0], dc[1], dc[2], dc[3], dc[4], dc[5], dc[6]))
240          .hex(params::TBLTIC, self.table_tick)
241          .hex("SHIFT", self.shift)
242          .hex("SWARM", self.swarm)
243          .hex("WIDTH", self.width)
244          .hex("SUBOSC", self.subosc);
245
246        let pg =
247            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
248        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
249    }
250}
251
252impl Describable for MacroSynth {
253    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
254        let pg = pg
255          .str(params::NAME, &self.name)
256          .bool(params::TRANSPOSE, self.transpose)
257          .hex(params::EQ, self.synth_params.associated_eq)
258          .hex(params::TBLTIC, self.table_tick)
259          .enumeration("SHAPE", self.shape as u8, &format!("{:?}", self.shape))
260          .hex("TIMBRE", self.timbre)
261          .hex("COLOR", self.color)
262          .hex("DEGRADE", self.degrade)
263          .hex("REDUX", self.redux);
264
265        let pg =
266            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
267
268        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
269    }
270}
271
272impl Describable for ControlChange {
273    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
274        return pg
275          .hex("CC", self.number)
276          .hex("VAL", self.value);
277    }
278}
279
280impl Describable for MIDIOut {
281    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
282        let port_str = self.human_readable_port();
283        let pg = pg
284          .str(params::NAME, &self.name)
285          .bool(params::TRANSPOSE, self.transpose)
286          .hex(params::TBLTIC, self.table_tick)
287
288          .enumeration("PORT", self.port, port_str)
289          .hex("CHANNEL", self.channel)
290          .hex("BANK", self.bank_select)
291          .hex("PROGRAM", self.program_change)
292          .nest_f("CCA", |ipg| self.custom_cc[0].describe(ipg, ver))
293          .nest_f("CCB", |ipg| self.custom_cc[1].describe(ipg, ver))
294          .nest_f("CCC", |ipg| self.custom_cc[2].describe(ipg, ver))
295          .nest_f("CCD", |ipg| self.custom_cc[3].describe(ipg, ver))
296          .nest_f("CCE", |ipg| self.custom_cc[4].describe(ipg, ver))
297          .nest_f("CCF", |ipg| self.custom_cc[5].describe(ipg, ver))
298          .nest_f("CCG", |ipg| self.custom_cc[6].describe(ipg, ver))
299          .nest_f("CCH", |ipg| self.custom_cc[7].describe(ipg, ver))
300          .nest_f("CCI", |ipg| self.custom_cc[8].describe(ipg, ver))
301          .nest_f("CCJ", |ipg| self.custom_cc[9].describe(ipg, ver));
302
303        describe_modulators(&self.mods, pg, self.destination_names(ver), ver)
304    }
305}
306
307impl DescribableWithDictionary for ADSREnv {
308    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
309        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
310        return pg
311          .enumeration(params::DEST, self.dest, dest_str)
312          .hex(params::AMOUNT, self.amount)
313          .hex(params::ATTACK, self.attack)
314          .hex(params::DECAY, self.decay)
315          .hex(params::SUSTAIN, self.sustain)
316          .hex(params::RELEASE, self.release);
317    }
318}
319
320impl DescribableWithDictionary for AHDEnv {
321    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG{
322        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
323        return pg
324          .enumeration(params::DEST, self.dest, dest_str)
325          .hex(params::AMOUNT, self.amount)
326          .hex(params::ATTACK, self.attack)
327          .hex(params::HOLD, self.hold)
328          .hex(params::DECAY, self.decay);
329    }
330}
331
332impl DescribableWithDictionary for DrumEnv {
333    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
334        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
335        return pg
336          .enumeration(params::DEST, self.dest, dest_str)
337          .hex(params::AMOUNT, self.amount)
338          .hex(params::PEAK, self.peak)
339          .hex(params::BODY, self.body)
340          .hex(params::DECAY, self.decay);
341    }
342}
343
344impl DescribableWithDictionary for LFO {
345    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
346        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
347        return pg
348          .enumeration(params::DEST, self.dest, dest_str)
349          .enumeration(params::LFOSHAPE, self.shape as u8, &format!("{:?}", self.shape))
350          .hex(params::AMOUNT, self.amount)
351          .hex(params::FREQ, self.freq)
352          .enumeration(params::TRIGGER, self.shape as u8, &format!("{:?}", self.trigger_mode));
353    }
354}
355
356impl DescribableWithDictionary for TrackingEnv {
357    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
358        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
359        return pg
360          .enumeration(params::DEST, self.dest, dest_str)
361          .hex(params::AMOUNT, self.amount)
362          .hex(params::SOURCE, self.src)
363          .hex("LVAL", self.lval)
364          .hex("HVAL", self.hval);
365    }
366}
367
368impl DescribableWithDictionary for TrigEnv {
369    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
370        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
371        return pg
372          .enumeration(params::DEST, self.dest, dest_str)
373          .hex(params::AMOUNT, self.amount)
374          .hex(params::ATTACK, self.attack)
375          .hex(params::HOLD, self.hold)
376          .str(params::SOURCE, &format!("{:?}", self.src));
377    }
378}
379
380fn describe_mod<PG : ParameterGatherer>(modulator: &Mod, pg: PG, ix: usize, dests:&[&'static str], ver: Version) -> PG {
381    let ix = ix + 1;
382    match modulator {
383        Mod::AHDEnv(ahd)  => {
384            let pg = pg.enumeration(&format!("MOD{ix}"), 0, "AHD ENV");
385            ahd.describe_with_dic(pg, dests, ver)
386        },
387        Mod::ADSREnv(adsr) => {
388            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "ADSR ENV");
389            adsr.describe_with_dic(pg, dests, ver)
390        },
391        Mod::DrumEnv(drum_env) =>{
392            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "DRUM ENV");
393            drum_env.describe_with_dic(pg, dests, ver)
394        }
395        Mod::LFO(lfo) => {
396            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "LFO");
397            lfo.describe_with_dic(pg, dests, ver)
398        }
399        Mod::TrigEnv(tenv) => {
400            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRIGENV");
401            tenv.describe_with_dic(pg, dests, ver)
402        }
403        Mod::TrackingEnv(tenv) => {
404            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRACKENV");
405            tenv.describe_with_dic(pg, dests, ver)
406        },
407    }
408}
409
410pub fn describe_modulators<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, dests: &[&'static str], ver: Version) -> PG {
411    return pg
412        .nest_f("MOD1", |ipg| describe_mod(&sp.mods[0], ipg, 0, dests, ver))
413        .nest_f("MOD2", |ipg| describe_mod(&sp.mods[1], ipg, 1, dests, ver))
414        .nest_f("MOD3", |ipg| describe_mod(&sp.mods[2], ipg, 2, dests, ver))
415        .nest_f("MOD4", |ipg| describe_mod(&sp.mods[3], ipg, 3, dests, ver));
416}
417
418pub fn describe_succint_params<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, _ver: Version) -> PG{
419    return pg
420      .hex(params::EQ, sp.associated_eq)
421      .hex(dests::AMP, sp.amp)
422      .enumeration("LIM", sp.limit.0, sp.limit.str())
423      .hex(dests::PAN, sp.mixer_pan)
424      .hex("DRY", sp.mixer_dry)
425      .hex("CHORUS", sp.mixer_chorus)
426      .hex("DELAY", sp.mixer_delay)
427      .hex("REVERB", sp.mixer_reverb);
428}
429
430impl DescribableWithDictionary for SynthParams {
431    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, filters: &[&str], ver: Version) -> PG {
432        let pg = pg.hex("FINE", self.fine_tune);
433
434        let pg =
435            match filters.get(self.filter_type as usize) {
436                None =>
437                    pg.enumeration("FILTER", self.filter_type, &format!("{:02X}", self.filter_type)),
438                Some(str) => 
439                    pg.enumeration("FILTER", self.filter_type, str)
440            };
441
442        let pg = pg
443            .hex("CUT", self.filter_cutoff)
444            .hex("RES", self.filter_res);
445
446        describe_succint_params(self, pg, ver)
447    }
448}