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 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}