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 InputMixerSettings {
41    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
42        pg
43          .hex("VOL", self.volume)
44          .hex("Chorus", self.mfx)
45          .hex("Delay", self.delay)
46          .hex("Reverb", self.reverb)
47    }
48}
49
50impl Describable for MixerSettings {
51    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
52        let pg = pg
53            .hex("MASTER_VOL", self.master_volume)
54            .nest_f("TRACK volume", |ipg| {
55                self.track_volume.iter()
56                    .enumerate()
57                    .fold(ipg, |iipg, (i, v)|
58                        iipg.hex(&format!("VOL_{}", i), *v))
59            })
60            .hex("Chorus Vol", self.chorus_volume)
61            .hex("Delay Vol", self.delay_volume)
62            .hex("Reverb Vol", self.reverb_volume);
63
64        let pg = match &self.analog_input {
65            AnalogInputSettings::Stereo(inp) =>
66                pg.nest_f("INPUT", |ipg| inp.describe(ipg, ver)),
67            AnalogInputSettings::DualMono((l, r)) =>
68                pg.nest_f("INPUT Left", |ipg| l.describe(ipg, ver))
69                  .nest_f("INPUT right", |ipg| r.describe(ipg, ver))
70        };
71
72        let pg =
73            pg.nest_f("USB input", |ipg| self.usb_input.describe(ipg, ver))
74            .nest_f("LIMITER", |ipg| {
75                let ipg = ipg.hex("LIM", self.limiter.level);
76                match &self.limiter.attack_release {
77                    None => ipg,
78                    Some ((at, rl, soft)) =>
79                        ipg.hex("ATTACK", *at)
80                        .hex("RELEASE", *rl)
81                        .bool("Soft clip", *soft)
82                }
83            })
84            .hex("DJF", self.dj_filter)
85            .hex("PEAK", self.dj_peak)
86            .hex("FLTTY", self.dj_filter_type);
87
88        match self.ott_level {
89            None => pg,
90            Some(ott) => pg.hex("OTT", ott)
91        }
92    }
93}
94
95impl Describable for EffectsSettings {
96    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
97        let mfx_name = if ver.at_least(6, 1) {
98            "MFX"
99        } else {
100            "Chorus"
101        };
102
103        let pg = pg
104          .nest_f(mfx_name, |ipg| {
105              let ipg =
106                  match self.mfx_kind {
107                      None => ipg,
108                      Some(mfx) => 
109                          ipg.enumeration("MFX KIND", mfx.into(), &format!("{:?}", mfx))
110                  };
111            
112              ipg.hex("MOD_DEPTH", self.chorus_mod_depth)
113                 .hex("MOD_FREQ", self.chorus_mod_freq)
114                 .hex("WIDTH", self.chorus_width)
115                 .hex("REVERB_SEND", self.chorus_reverb_send)
116          })
117          .nest_f("Delay", |ipg| {
118              let ipg = match &self.delay_filter {
119                  None => ipg,
120                  Some(df) =>
121                      ipg.hex("LP", df.low_pass)
122                         .hex("HP", df.high_pass)
123              };
124
125              ipg.hex("DELAY_L", self.delay_time_l)
126                 .hex("DELAY_R", self.delay_time_r)
127                 .hex("FEEDBACK", self.delay_feedback)
128                 .hex("WIDTH", self.delay_width)
129                 .hex("REVERB_SEND", self.delay_reverb_send)
130          })
131          .nest_f("Reverb", |ipg| {
132              let ipg = match &self.delay_filter {
133                  None => ipg,
134                  Some(df) =>
135                      ipg.hex("LP", df.low_pass)
136                         .hex("HP", df.high_pass)
137              };
138
139              let ipg = ipg
140                 .hex("SIZE", self.reverb_size)
141                 .hex("WIDTH", self.reverb_width)
142                 .hex("DECAY", self.reverb_damping)
143                 .hex("MOD_DEPTH", self.reverb_mod_depth)
144                 .hex("MOD_FREQ", self.reverb_mod_freq);
145
146              match self.reverb_shimmer {
147                None => ipg,
148                Some(shimmer) => ipg.hex("SHIMMER", shimmer)
149              }
150          });
151
152        match &self.ott_configuration {
153            None => pg,
154            Some(ott) => {
155                pg.nest_f("OTT", |ipg| {
156                    ipg.hex("TIME", ott.time)
157                       .hex("COLOR", ott.color)
158                })
159            }
160        }
161    }
162}
163
164impl Describable for EqBand {
165    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
166        return pg.float("GAIN", self.gain())
167          .float("FREQ", self.frequency() as f64)
168          .hex("Q", self.q)
169          .enumeration("TYPE", self.mode.eq_type_hex(), self.mode.type_str())
170          .enumeration("MODE", self.mode.eq_mode_hex(), self.mode.mode_str())
171    }
172}
173
174impl Describable for Equ {
175    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
176        return pg
177            .nest_f("LOW", |ii| self.low.describe(ii, ver))
178            .nest_f("MID", |ii| self.mid.describe(ii, ver))
179            .nest_f("HIGH", |ii| self.high.describe(ii, ver));
180    }
181}
182
183impl Describable for Operator {
184    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG{
185        return pg
186          .str("SHAPE", &format!("{:?}", self.shape))
187          .float("RATIO", (self.ratio as f64) + (self.ratio_fine as f64) / 100.0)
188          .hex("LEVEL", self.level)
189          .hex("FBK", self.feedback)
190          .hex("MOD_A", self.mod_a)
191          .hex("MOD_B", self.mod_b);
192    }
193}
194
195impl Describable for FMSynth {
196   fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
197        let pg = pg
198            .str(params::NAME, &self.name)
199            .bool(params::TRANSPOSE, self.transpose)
200            .hex(params::EQ, self.synth_params.associated_eq)
201            .hex(params::TBLTIC, self.table_tick)
202            .enumeration("ALG", self.algo.0, self.algo.str())
203            .nest_f("A", |ipg| self.operators[0].describe(ipg, ver))
204            .nest_f("B", |ipg| self.operators[1].describe(ipg, ver))
205            .nest_f("C", |ipg| self.operators[2].describe(ipg, ver))
206            .nest_f("D", |ipg| self.operators[3].describe(ipg,ver));
207
208        let pg = self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
209        return describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver);
210   }
211}
212
213impl Describable for Sampler {
214    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
215        let pg = pg
216            .str(params::NAME, &self.name)
217            .bool(params::TRANSPOSE, self.transpose)
218            .hex(params::TBLTIC, self.table_tick)
219            .hex(params::EQ, self.synth_params.associated_eq)
220            .str("SAMPLE", &self.sample_path)
221            .enumeration("PLAY", self.play_mode as u8, &format!("{:?}", self.play_mode))
222            .hex("SLICE", self.slice);
223
224        let pg = match self.play_mode {
225            SamplePlayMode::FWD |
226            SamplePlayMode::REV |
227            SamplePlayMode::FWDLOOP |
228            SamplePlayMode::REVLOOP |
229            SamplePlayMode::FWD_PP |
230            SamplePlayMode::REV_PP |
231            SamplePlayMode::OSC |
232            SamplePlayMode::OSC_REV |
233            SamplePlayMode::OSC_PP => {
234                pg.hex("START", self.start)
235                  .hex("LOOP ST", self.loop_start)
236                  .hex("LENGTH", self.length)
237                  .hex("DETUNE", self.synth_params.pitch)
238            },
239
240            SamplePlayMode::REPITCH |
241            SamplePlayMode::REP_REV |
242            SamplePlayMode::REP_PP => {
243                pg.hex("STEPS", self.synth_params.pitch)
244                  .hex("START", self.start)
245                  .hex("LOOP ST", self.loop_start)
246                  .hex("LENGTH", self.length)
247            },
248
249            SamplePlayMode::REP_BPM |
250            SamplePlayMode::BPM_REV |
251            SamplePlayMode::BPM_PP => {
252                pg.hex("BPM", self.synth_params.pitch)
253                  .hex("START", self.start)
254                  .hex("LOOP ST", self.loop_start)
255                  .hex("LENGTH", self.length)
256            }
257        };
258
259        let pg =
260            pg.hex("DEGRADE", self.degrade);
261
262        let pg = self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
263        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
264    }
265}
266
267impl Describable for WavSynth {
268   fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
269        let pg = pg
270          .str(params::NAME, &self.name)
271          .bool(params::TRANSPOSE, self.transpose)
272          .hex(params::TBLTIC, self.table_tick)
273          .hex(params::EQ, self.synth_params.associated_eq)
274          .enumeration("SHAPE", self.shape as u8, &format!("{:?}", self.shape))
275          .hex("SIZE", self.size)
276          .hex("MULT", self.mult)
277          .hex("WARP", self.warp)
278          .hex("SCAN", self.scan);
279
280        let pg =
281            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
282
283        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
284   }
285}
286
287impl Describable for Instrument {
288    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
289        match self {
290            Instrument::WavSynth(ws)     => 
291                pg.nest_f("WAVSYNTH", |ipg| ws.describe(ipg, ver)),
292            Instrument::MacroSynth(ms) =>
293                pg.nest_f("MACROSYN", |ipg| ms.describe(ipg, ver)),
294            Instrument::Sampler(s)        =>
295                pg.nest_f("SAMPLE", |ipg| s.describe(ipg, ver)),
296            Instrument::MIDIOut(mo)       =>
297                pg.nest_f("MIDIOUT", |ipg| mo.describe(ipg, ver)),
298            Instrument::FMSynth(fs)       =>
299                pg.nest_f("FMSYNTH", |ipg| fs.describe(ipg, ver)),
300            Instrument::HyperSynth(hs) =>
301                pg.nest_f("HYPERSYNTH", |ipg| hs.describe(ipg, ver)),
302            Instrument::External(ex) =>
303                pg.nest_f("EXTERNALINST", |ipg| ex.describe(ipg, ver)),
304            Instrument::None => pg
305        }
306    }
307}
308
309pub fn describe_succint<PG : ParameterGatherer>(instr: &Instrument, pg: PG, ver: Version) -> PG {
310    let (k, common) =
311        match instr {
312            Instrument::WavSynth(ws)     => ("WAVSYNTH", Some(&ws.synth_params)),
313            Instrument::MacroSynth(ms) => ("MACROSYN", Some(&ms.synth_params)),
314            Instrument::Sampler(s)        => ("SAMPLE", Some(&s.synth_params)),
315            Instrument::MIDIOut(_mo)       => ("MIDIOUT", None),
316            Instrument::FMSynth(fs)       => ("FMSYNTH", Some(&fs.synth_params)),
317            Instrument::HyperSynth(hs) => ("HYPERSYNTH", Some(&hs.synth_params)),
318            Instrument::External(ex) => ("EXTERNALINST", Some(&ex.synth_params)),
319            Instrument::None => ("NONE", None)
320        };
321
322    let pg = pg.str("KIND", k);
323    match common {
324        None => pg,
325        Some(c) => describe_succint_params(&c, pg, ver)
326    }
327}
328
329impl Describable for ExternalInst {
330    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
331        let port_str = self.human_readable_port();
332        let pg = pg
333          .str(params::NAME, &self.name)
334          .bool(params::TRANSPOSE, self.transpose)
335          .hex(params::EQ, self.synth_params.associated_eq)
336          .hex(params::TBLTIC, self.table_tick)
337
338          .enumeration("PORT", self.port, port_str)
339          .hex("CHANNEL", self.channel)
340          .hex("BANK", self.bank)
341          .hex("PROGRAM", self.program)
342          .nest_f(params::CCA, |ipg| self.cca.describe(ipg, ver))
343          .nest_f(params::CCB, |ipg| self.ccb.describe(ipg, ver))
344          .nest_f(params::CCC, |ipg| self.ccc.describe(ipg, ver))
345          .nest_f(params::CCD, |ipg| self.ccd.describe(ipg, ver));
346
347        let pg =
348            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
349
350        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
351    }
352}
353
354impl Describable for HyperSynth {
355    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
356        let dc = &self.default_chord;
357
358        let pg = pg
359          .str(params::NAME, &self.name)
360          .bool(params::TRANSPOSE, self.transpose)
361          .hex(params::EQ, self.synth_params.associated_eq)
362          .hex(params::SCALE, self.scale)
363          .str("CHORD", &format!("{:02X} | {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", dc[0], dc[1], dc[2], dc[3], dc[4], dc[5], dc[6]))
364          .hex(params::TBLTIC, self.table_tick)
365          .hex("SHIFT", self.shift)
366          .hex("SWARM", self.swarm)
367          .hex("WIDTH", self.width)
368          .hex("SUBOSC", self.subosc);
369
370        let pg =
371            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
372        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
373    }
374}
375
376impl Describable for MacroSynth {
377    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
378        let pg = pg
379          .str(params::NAME, &self.name)
380          .bool(params::TRANSPOSE, self.transpose)
381          .hex(params::EQ, self.synth_params.associated_eq)
382          .hex(params::TBLTIC, self.table_tick)
383          .enumeration("SHAPE", self.shape as u8, &format!("{:?}", self.shape))
384          .hex("TIMBRE", self.timbre)
385          .hex("COLOR", self.color)
386          .hex("DEGRADE", self.degrade)
387          .hex("REDUX", self.redux);
388
389        let pg =
390            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
391
392        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
393    }
394}
395
396impl Describable for ControlChange {
397    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
398        return pg
399          .hex("CC", self.number)
400          .hex("VAL", self.value);
401    }
402}
403
404impl Describable for MIDIOut {
405    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
406        let port_str = self.human_readable_port();
407        let pg = pg
408          .str(params::NAME, &self.name)
409          .bool(params::TRANSPOSE, self.transpose)
410          .hex(params::TBLTIC, self.table_tick)
411
412          .enumeration("PORT", self.port, port_str)
413          .hex("CHANNEL", self.channel)
414          .hex("BANK", self.bank_select)
415          .hex("PROGRAM", self.program_change)
416          .nest_f("CCA", |ipg| self.custom_cc[0].describe(ipg, ver))
417          .nest_f("CCB", |ipg| self.custom_cc[1].describe(ipg, ver))
418          .nest_f("CCC", |ipg| self.custom_cc[2].describe(ipg, ver))
419          .nest_f("CCD", |ipg| self.custom_cc[3].describe(ipg, ver))
420          .nest_f("CCE", |ipg| self.custom_cc[4].describe(ipg, ver))
421          .nest_f("CCF", |ipg| self.custom_cc[5].describe(ipg, ver))
422          .nest_f("CCG", |ipg| self.custom_cc[6].describe(ipg, ver))
423          .nest_f("CCH", |ipg| self.custom_cc[7].describe(ipg, ver))
424          .nest_f("CCI", |ipg| self.custom_cc[8].describe(ipg, ver))
425          .nest_f("CCJ", |ipg| self.custom_cc[9].describe(ipg, ver));
426
427        describe_modulators(&self.mods, pg, self.destination_names(ver), ver)
428    }
429}
430
431impl DescribableWithDictionary for ADSREnv {
432    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
433        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
434        return pg
435          .enumeration(params::DEST, self.dest, dest_str)
436          .hex(params::AMOUNT, self.amount)
437          .hex(params::ATTACK, self.attack)
438          .hex(params::DECAY, self.decay)
439          .hex(params::SUSTAIN, self.sustain)
440          .hex(params::RELEASE, self.release);
441    }
442}
443
444impl DescribableWithDictionary for AHDEnv {
445    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG{
446        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
447        return pg
448          .enumeration(params::DEST, self.dest, dest_str)
449          .hex(params::AMOUNT, self.amount)
450          .hex(params::ATTACK, self.attack)
451          .hex(params::HOLD, self.hold)
452          .hex(params::DECAY, self.decay);
453    }
454}
455
456impl DescribableWithDictionary for DrumEnv {
457    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
458        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
459        return pg
460          .enumeration(params::DEST, self.dest, dest_str)
461          .hex(params::AMOUNT, self.amount)
462          .hex(params::PEAK, self.peak)
463          .hex(params::BODY, self.body)
464          .hex(params::DECAY, self.decay);
465    }
466}
467
468impl DescribableWithDictionary for LFO {
469    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
470        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
471        return pg
472          .enumeration(params::DEST, self.dest, dest_str)
473          .enumeration(params::LFOSHAPE, self.shape as u8, &format!("{:?}", self.shape))
474          .hex(params::AMOUNT, self.amount)
475          .hex(params::FREQ, self.freq)
476          .enumeration(params::TRIGGER, self.shape as u8, &format!("{:?}", self.trigger_mode));
477    }
478}
479
480impl DescribableWithDictionary for TrackingEnv {
481    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
482        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
483        return pg
484          .enumeration(params::DEST, self.dest, dest_str)
485          .hex(params::AMOUNT, self.amount)
486          .hex(params::SOURCE, self.src)
487          .hex("LVAL", self.lval)
488          .hex("HVAL", self.hval);
489    }
490}
491
492impl DescribableWithDictionary for TrigEnv {
493    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
494        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
495        return pg
496          .enumeration(params::DEST, self.dest, dest_str)
497          .hex(params::AMOUNT, self.amount)
498          .hex(params::ATTACK, self.attack)
499          .hex(params::HOLD, self.hold)
500          .str(params::SOURCE, &format!("{:?}", self.src));
501    }
502}
503
504fn describe_mod<PG : ParameterGatherer>(modulator: &Mod, pg: PG, ix: usize, dests:&[&'static str], ver: Version) -> PG {
505    let ix = ix + 1;
506    match modulator {
507        Mod::AHDEnv(ahd)  => {
508            let pg = pg.enumeration(&format!("MOD{ix}"), 0, "AHD ENV");
509            ahd.describe_with_dic(pg, dests, ver)
510        },
511        Mod::ADSREnv(adsr) => {
512            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "ADSR ENV");
513            adsr.describe_with_dic(pg, dests, ver)
514        },
515        Mod::DrumEnv(drum_env) =>{
516            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "DRUM ENV");
517            drum_env.describe_with_dic(pg, dests, ver)
518        }
519        Mod::LFO(lfo) => {
520            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "LFO");
521            lfo.describe_with_dic(pg, dests, ver)
522        }
523        Mod::TrigEnv(tenv) => {
524            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRIGENV");
525            tenv.describe_with_dic(pg, dests, ver)
526        }
527        Mod::TrackingEnv(tenv) => {
528            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRACKENV");
529            tenv.describe_with_dic(pg, dests, ver)
530        },
531    }
532}
533
534pub fn describe_modulators<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, dests: &[&'static str], ver: Version) -> PG {
535    return pg
536        .nest_f("MOD1", |ipg| describe_mod(&sp.mods[0], ipg, 0, dests, ver))
537        .nest_f("MOD2", |ipg| describe_mod(&sp.mods[1], ipg, 1, dests, ver))
538        .nest_f("MOD3", |ipg| describe_mod(&sp.mods[2], ipg, 2, dests, ver))
539        .nest_f("MOD4", |ipg| describe_mod(&sp.mods[3], ipg, 3, dests, ver));
540}
541
542pub fn describe_succint_params<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, ver: Version) -> PG{
543    return pg
544      .hex(params::EQ, sp.associated_eq)
545      .hex(dests::AMP, sp.amp)
546      .enumeration("LIM", sp.limit.0, sp.limit.str())
547      .hex(dests::PAN, sp.mixer_pan)
548      .hex("DRY", sp.mixer_dry)
549      .hex(if ver.at_least(6, 1) { "MFX" } else { "CHORUS "}, 
550            sp.mixer_mfx)
551      .hex("DELAY", sp.mixer_delay)
552      .hex("REVERB", sp.mixer_reverb);
553}
554
555impl DescribableWithDictionary for SynthParams {
556    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, filters: &[&str], ver: Version) -> PG {
557        let pg = pg.hex("FINE", self.fine_tune);
558
559        let pg =
560            match filters.get(self.filter_type as usize) {
561                None =>
562                    pg.enumeration("FILTER", self.filter_type, &format!("{:02X}", self.filter_type)),
563                Some(str) => 
564                    pg.enumeration("FILTER", self.filter_type, str)
565            };
566
567        let pg = pg
568            .hex("CUT", self.filter_cutoff)
569            .hex("RES", self.filter_res);
570
571        describe_succint_params(self, pg, ver)
572    }
573}