1use crate::*;
2
3pub trait ParameterGatherer {
6 fn hex(self, name: &str, val: u8) -> Self;
8
9 fn bool(self, name: &str, val: bool) -> Self;
11
12 fn float(self, name: &str, val: f64) -> Self;
14
15 fn str(self, name: &str, val: &str) -> Self;
17
18 fn enumeration(self, name: &str, hex: u8, val: &str) -> Self;
21
22 fn nest_f<F>(self, name: &str, f: F) -> Self
25 where F : FnOnce (Self) -> Self, Self : Sized;
26}
27
28pub trait Describable {
31 fn describe<PG : ParameterGatherer>(&self, pg: PG, ver: Version) -> PG;
33}
34
35pub 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}