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.after(&FIRMWARE_6_2_SONG_VERSION) {
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 Chord {
355    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
356        pg.str("CHD",
357            &format!("{} {} {} {} {} {}",
358                self.offset_str(0),
359                self.offset_str(1),
360                self.offset_str(2),
361                self.offset_str(3),
362                self.offset_str(4),
363                self.offset_str(5)))
364    }
365}
366
367impl Describable for HyperSynth {
368    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
369        let dc = &self.default_chord;
370
371        let pg = pg
372          .str(params::NAME, &self.name)
373          .bool(params::TRANSPOSE, self.transpose)
374          .hex(params::EQ, self.synth_params.associated_eq)
375          .hex(params::SCALE, self.scale)
376          .str("CHORD", &format!("{:02X} | {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", dc[0], dc[1], dc[2], dc[3], dc[4], dc[5], dc[6]))
377          .hex(params::TBLTIC, self.table_tick)
378          .hex("SHIFT", self.shift)
379          .hex("SWARM", self.swarm)
380          .hex("WIDTH", self.width)
381          .hex("SUBOSC", self.subosc)
382          .nest_f("CHORDS", |ipg| {
383                let mut ipg = ipg;
384
385                for chd in self.chords.iter() {
386                    ipg = chd.describe(ipg, ver)
387                };
388
389                ipg
390          });
391
392        let pg =
393            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
394        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
395    }
396}
397
398impl Describable for MacroSynth {
399    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
400        let pg = pg
401          .str(params::NAME, &self.name)
402          .bool(params::TRANSPOSE, self.transpose)
403          .hex(params::EQ, self.synth_params.associated_eq)
404          .hex(params::TBLTIC, self.table_tick)
405          .enumeration("SHAPE", self.shape as u8, &format!("{:?}", self.shape))
406          .hex("TIMBRE", self.timbre)
407          .hex("COLOR", self.color)
408          .hex("DEGRADE", self.degrade)
409          .hex("REDUX", self.redux);
410
411        let pg =
412            self.synth_params.describe_with_dic(pg, self.filter_types(ver), ver);
413
414        describe_modulators(&self.synth_params, pg, self.destination_names(ver), ver)
415    }
416}
417
418impl Describable for ControlChange {
419    fn describe<PG : ParameterGatherer>(&self, pg: PG, _ver: Version) -> PG {
420        return pg
421          .hex("CC", self.number)
422          .hex("VAL", self.value);
423    }
424}
425
426impl Describable for MIDIOut {
427    fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG {
428        let port_str = self.human_readable_port();
429        let pg = pg
430          .str(params::NAME, &self.name)
431          .bool(params::TRANSPOSE, self.transpose)
432          .hex(params::TBLTIC, self.table_tick)
433
434          .enumeration("PORT", self.port, port_str)
435          .hex("CHANNEL", self.channel)
436          .hex("BANK", self.bank_select)
437          .hex("PROGRAM", self.program_change)
438          .nest_f("CCA", |ipg| self.custom_cc[0].describe(ipg, ver))
439          .nest_f("CCB", |ipg| self.custom_cc[1].describe(ipg, ver))
440          .nest_f("CCC", |ipg| self.custom_cc[2].describe(ipg, ver))
441          .nest_f("CCD", |ipg| self.custom_cc[3].describe(ipg, ver))
442          .nest_f("CCE", |ipg| self.custom_cc[4].describe(ipg, ver))
443          .nest_f("CCF", |ipg| self.custom_cc[5].describe(ipg, ver))
444          .nest_f("CCG", |ipg| self.custom_cc[6].describe(ipg, ver))
445          .nest_f("CCH", |ipg| self.custom_cc[7].describe(ipg, ver))
446          .nest_f("CCI", |ipg| self.custom_cc[8].describe(ipg, ver))
447          .nest_f("CCJ", |ipg| self.custom_cc[9].describe(ipg, ver));
448
449        describe_modulators(&self.mods, pg, self.destination_names(ver), ver)
450    }
451}
452
453impl DescribableWithDictionary for ADSREnv {
454    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
455        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
456        return pg
457          .enumeration(params::DEST, self.dest, dest_str)
458          .hex(params::AMOUNT, self.amount)
459          .hex(params::ATTACK, self.attack)
460          .hex(params::DECAY, self.decay)
461          .hex(params::SUSTAIN, self.sustain)
462          .hex(params::RELEASE, self.release);
463    }
464}
465
466impl DescribableWithDictionary for AHDEnv {
467    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG{
468        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
469        return pg
470          .enumeration(params::DEST, self.dest, dest_str)
471          .hex(params::AMOUNT, self.amount)
472          .hex(params::ATTACK, self.attack)
473          .hex(params::HOLD, self.hold)
474          .hex(params::DECAY, self.decay);
475    }
476}
477
478impl DescribableWithDictionary for DrumEnv {
479    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
480        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
481        return pg
482          .enumeration(params::DEST, self.dest, dest_str)
483          .hex(params::AMOUNT, self.amount)
484          .hex(params::PEAK, self.peak)
485          .hex(params::BODY, self.body)
486          .hex(params::DECAY, self.decay);
487    }
488}
489
490impl DescribableWithDictionary for LFO {
491    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
492        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
493        return pg
494          .enumeration(params::DEST, self.dest, dest_str)
495          .enumeration(params::LFOSHAPE, self.shape as u8, &format!("{:?}", self.shape))
496          .hex(params::AMOUNT, self.amount)
497          .hex(params::FREQ, self.freq)
498          .enumeration(params::TRIGGER, self.shape as u8, &format!("{:?}", self.trigger_mode));
499    }
500}
501
502impl DescribableWithDictionary for TrackingEnv {
503    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
504        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
505        return pg
506          .enumeration(params::DEST, self.dest, dest_str)
507          .hex(params::AMOUNT, self.amount)
508          .hex(params::SOURCE, self.src)
509          .hex("LVAL", self.lval)
510          .hex("HVAL", self.hval);
511    }
512}
513
514impl DescribableWithDictionary for TrigEnv {
515    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, dests: &[&'static str], _ver: Version) -> PG {
516        let dest_str = dests.get(self.dest as usize).unwrap_or(&"??");
517        return pg
518          .enumeration(params::DEST, self.dest, dest_str)
519          .hex(params::AMOUNT, self.amount)
520          .hex(params::ATTACK, self.attack)
521          .hex(params::HOLD, self.hold)
522          .str(params::SOURCE, &format!("{:?}", self.src));
523    }
524}
525
526fn describe_mod<PG : ParameterGatherer>(modulator: &Mod, pg: PG, ix: usize, dests:&[&'static str], ver: Version) -> PG {
527    let ix = ix + 1;
528    match modulator {
529        Mod::AHDEnv(ahd)  => {
530            let pg = pg.enumeration(&format!("MOD{ix}"), 0, "AHD ENV");
531            ahd.describe_with_dic(pg, dests, ver)
532        },
533        Mod::ADSREnv(adsr) => {
534            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "ADSR ENV");
535            adsr.describe_with_dic(pg, dests, ver)
536        },
537        Mod::DrumEnv(drum_env) =>{
538            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "DRUM ENV");
539            drum_env.describe_with_dic(pg, dests, ver)
540        }
541        Mod::LFO(lfo) => {
542            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "LFO");
543            lfo.describe_with_dic(pg, dests, ver)
544        }
545        Mod::TrigEnv(tenv) => {
546            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRIGENV");
547            tenv.describe_with_dic(pg, dests, ver)
548        }
549        Mod::TrackingEnv(tenv) => {
550            let pg = pg.enumeration(&format!("MOD{ix}"), 1, "TRACKENV");
551            tenv.describe_with_dic(pg, dests, ver)
552        },
553    }
554}
555
556pub fn describe_modulators<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, dests: &[&'static str], ver: Version) -> PG {
557    return pg
558        .nest_f("MOD1", |ipg| describe_mod(&sp.mods[0], ipg, 0, dests, ver))
559        .nest_f("MOD2", |ipg| describe_mod(&sp.mods[1], ipg, 1, dests, ver))
560        .nest_f("MOD3", |ipg| describe_mod(&sp.mods[2], ipg, 2, dests, ver))
561        .nest_f("MOD4", |ipg| describe_mod(&sp.mods[3], ipg, 3, dests, ver));
562}
563
564pub fn describe_succint_params<PG : ParameterGatherer>(sp: &SynthParams, pg: PG, ver: Version) -> PG{
565    return pg
566      .hex(params::EQ, sp.associated_eq)
567      .hex(dests::AMP, sp.amp)
568      .enumeration("LIM", sp.limit.0, sp.limit.str())
569      .hex(dests::PAN, sp.mixer_pan)
570      .hex("DRY", sp.mixer_dry)
571      .hex(if ver.after(&FIRMWARE_6_2_SONG_VERSION) { "MFX" } else { "CHORUS "}, 
572            sp.mixer_mfx)
573      .hex("DELAY", sp.mixer_delay)
574      .hex("REVERB", sp.mixer_reverb);
575}
576
577impl DescribableWithDictionary for SynthParams {
578    fn describe_with_dic<PG : ParameterGatherer>(&self, pg: PG, filters: &[&str], ver: Version) -> PG {
579        let pg = pg.hex("FINE", self.fine_tune);
580
581        let pg =
582            match filters.get(self.filter_type as usize) {
583                None =>
584                    pg.enumeration("FILTER", self.filter_type, &format!("{:02X}", self.filter_type)),
585                Some(str) => 
586                    pg.enumeration("FILTER", self.filter_type, str)
587            };
588
589        let pg = pg
590            .hex("CUT", self.filter_cutoff)
591            .hex("RES", self.filter_res);
592
593        describe_succint_params(self, pg, ver)
594    }
595}