1#![warn(missing_docs)]
32
33use std::borrow::Cow;
34
35#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
37#[serde(rename = "multisample")]
38pub struct Multisample<'a> {
39 #[serde(
40 borrow,
41 default,
42 rename = "@name",
43 skip_serializing_if = "str::is_empty"
44 )]
45 name: Cow<'a, str>,
46 #[serde(borrow, default, skip_serializing_if = "str::is_empty")]
47 generator: Cow<'a, str>,
48 #[serde(borrow, default, skip_serializing_if = "str::is_empty")]
49 category: Cow<'a, str>,
50 #[serde(borrow, default, skip_serializing_if = "str::is_empty")]
51 creator: Cow<'a, str>,
52 #[serde(borrow, default, skip_serializing_if = "str::is_empty")]
53 description: Cow<'a, str>,
54 #[serde(borrow, default, skip_serializing_if = "Keywords::is_empty")]
55 keywords: Keywords<'a>,
56 #[serde(borrow, default, rename = "group")]
57 groups: Cow<'a, [Group<'a>]>,
58 #[serde(borrow, default, rename = "sample")]
59 samples: Cow<'a, [Sample<'a>]>,
60}
61
62impl<'a> Multisample<'a> {
63 pub fn to_owned(self) -> Multisample<'static> {
65 Multisample {
66 name: Cow::Owned(self.name.into_owned()),
67 generator: Cow::Owned(self.generator.into_owned()),
68 category: Cow::Owned(self.category.into_owned()),
69 creator: Cow::Owned(self.creator.into_owned()),
70 description: Cow::Owned(self.description.into_owned()),
71 keywords: Keywords {
72 list: self
73 .keywords
74 .list
75 .iter()
76 .map(|s| Cow::Owned(s.to_string()))
77 .collect(),
78 },
79 groups: self
80 .groups
81 .iter()
82 .map(|g| Group {
83 name: Cow::Owned(g.name.to_string()),
84 color: g.color,
85 })
86 .collect(),
87 samples: self
88 .samples
89 .iter()
90 .map(|s| Sample {
91 file: Cow::Owned(s.file.to_path_buf()),
92 ..s.clone()
93 })
94 .collect(),
95 }
96 }
97
98 pub fn with_name(self, name: impl Into<Cow<'a, str>>) -> Self {
100 Self {
101 name: name.into(),
102 ..self
103 }
104 }
105
106 pub fn with_generator(self, generator: impl Into<Cow<'a, str>>) -> Self {
108 Self {
109 generator: generator.into(),
110 ..self
111 }
112 }
113
114 pub fn with_category(self, category: impl Into<Cow<'a, str>>) -> Self {
116 Self {
117 category: category.into(),
118 ..self
119 }
120 }
121
122 pub fn with_creator(self, creator: impl Into<Cow<'a, str>>) -> Self {
124 Self {
125 creator: creator.into(),
126 ..self
127 }
128 }
129
130 pub fn with_description(self, description: impl Into<Cow<'a, str>>) -> Self {
132 Self {
133 description: description.into(),
134 ..self
135 }
136 }
137
138 pub fn with_keywords<S: Into<Cow<'a, str>>>(
140 self,
141 keywords: impl IntoIterator<Item = S>,
142 ) -> Self {
143 Self {
144 keywords: Keywords {
145 list: keywords.into_iter().map(Into::into).collect(),
146 },
147 ..self
148 }
149 }
150
151 pub fn with_groups(self, groups: impl IntoIterator<Item = Group<'a>>) -> Self {
153 Self {
154 groups: groups.into_iter().collect(),
155 ..self
156 }
157 }
158
159 pub fn with_samples(self, samples: impl IntoIterator<Item = Sample<'a>>) -> Self {
161 Self {
162 samples: samples.into_iter().collect(),
163 ..self
164 }
165 }
166
167 pub fn name(&self) -> &str {
169 &self.name
170 }
171
172 pub fn generator(&self) -> &str {
174 &self.generator
175 }
176
177 pub fn category(&self) -> &str {
179 &self.category
180 }
181
182 pub fn creator(&self) -> &str {
184 &self.creator
185 }
186
187 pub fn description(&self) -> &str {
189 &self.description
190 }
191
192 pub fn keywords(&self) -> &[Cow<'a, str>] {
194 &self.keywords.list
195 }
196
197 pub fn groups(&self) -> &[Group] {
199 &self.groups
200 }
201
202 pub fn samples(&self) -> &[Sample] {
204 &self.samples
205 }
206}
207
208#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
209struct Keywords<'a> {
210 #[serde(borrow, default, rename = "keyword")]
211 list: Cow<'a, [Cow<'a, str>]>,
212}
213
214impl Keywords<'_> {
215 fn is_empty(&self) -> bool {
216 self.list.is_empty()
217 }
218}
219
220#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
222pub struct Group<'a> {
223 #[serde(
224 borrow,
225 default,
226 rename = "@name",
227 skip_serializing_if = "str::is_empty"
228 )]
229 name: Cow<'a, str>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 color: Option<Color>,
232}
233
234impl<'a> Group<'a> {
235 pub fn with_name(self, name: impl Into<Cow<'a, str>>) -> Self {
237 Self {
238 name: name.into(),
239 ..self
240 }
241 }
242
243 pub fn with_color(self, color: impl Into<Option<Color>>) -> Self {
245 Self {
246 color: color.into(),
247 ..self
248 }
249 }
250
251 pub fn name(&self) -> &str {
253 &self.name
254 }
255
256 pub fn color(&self) -> Option<Color> {
258 self.color
259 }
260}
261
262pub type Color = [u8; 3];
264
265#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
267pub struct Sample<'a> {
268 #[serde(borrow, rename = "@file")]
269 file: Cow<'a, std::path::Path>,
270 #[serde(rename = "@sample-start", skip_serializing_if = "Option::is_none")]
271 sample_start: Option<f64>,
272 #[serde(rename = "@sample-stop", skip_serializing_if = "Option::is_none")]
273 sample_stop: Option<f64>,
274 #[serde(rename = "@gain", skip_serializing_if = "Option::is_none")]
275 gain: Option<f64>,
276 #[serde(rename = "@group", skip_serializing_if = "Option::is_none")]
277 group: Option<isize>,
278 #[serde(rename = "@parameter-1", skip_serializing_if = "Option::is_none")]
279 parameter_1: Option<f64>,
280 #[serde(rename = "@parameter-2", skip_serializing_if = "Option::is_none")]
281 parameter_2: Option<f64>,
282 #[serde(rename = "@parameter-3", skip_serializing_if = "Option::is_none")]
283 parameter_3: Option<f64>,
284 #[serde(rename = "@reverse", skip_serializing_if = "Option::is_none")]
285 reverse: Option<bool>,
286 #[serde(rename = "@zone-logic", skip_serializing_if = "Option::is_none")]
287 zone_logic: Option<ZoneLogic>,
288 #[serde(skip_serializing_if = "Option::is_none")]
289 key: Option<Key>,
290 #[serde(skip_serializing_if = "Option::is_none")]
291 velocity: Option<ZoneInfo>,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 select: Option<ZoneInfo>,
294 #[serde(skip_serializing_if = "Option::is_none")]
295 r#loop: Option<Loop>,
296}
297
298impl<'a> Sample<'a> {
299 pub fn with_file(self, file: impl Into<Cow<'a, std::path::Path>>) -> Self {
301 Self {
302 file: file.into(),
303 ..self
304 }
305 }
306
307 pub fn with_sample_start(self, sample_start: impl Into<Option<f64>>) -> Self {
309 Self {
310 sample_start: sample_start.into(),
311 ..self
312 }
313 }
314
315 pub fn with_sample_stop(self, sample_stop: impl Into<Option<f64>>) -> Self {
317 Self {
318 sample_stop: sample_stop.into(),
319 ..self
320 }
321 }
322
323 pub fn with_gain(self, gain: impl Into<Option<f64>>) -> Self {
325 Self {
326 gain: gain.into(),
327 ..self
328 }
329 }
330
331 pub fn with_group(self, group: impl Into<Option<isize>>) -> Self {
333 Self {
334 group: group.into(),
335 ..self
336 }
337 }
338
339 pub fn with_parameter_1(self, parameter_1: impl Into<Option<f64>>) -> Self {
341 Self {
342 parameter_1: parameter_1.into(),
343 ..self
344 }
345 }
346
347 pub fn with_parameter_2(self, parameter_2: impl Into<Option<f64>>) -> Self {
349 Self {
350 parameter_2: parameter_2.into(),
351 ..self
352 }
353 }
354
355 pub fn with_parameter_3(self, parameter_3: impl Into<Option<f64>>) -> Self {
357 Self {
358 parameter_3: parameter_3.into(),
359 ..self
360 }
361 }
362
363 pub fn with_reverse(self, reverse: impl Into<Option<bool>>) -> Self {
365 Self {
366 reverse: reverse.into(),
367 ..self
368 }
369 }
370
371 pub fn with_zone_logic(self, zone_logic: impl Into<Option<ZoneLogic>>) -> Self {
373 Self {
374 zone_logic: zone_logic.into(),
375 ..self
376 }
377 }
378
379 pub fn with_key(self, key: impl Into<Option<Key>>) -> Self {
381 Self {
382 key: key.into(),
383 ..self
384 }
385 }
386
387 pub fn with_velocity(self, velocity: impl Into<Option<ZoneInfo>>) -> Self {
389 Self {
390 velocity: velocity.into(),
391 ..self
392 }
393 }
394
395 pub fn with_select(self, select: impl Into<Option<ZoneInfo>>) -> Self {
397 Self {
398 select: select.into(),
399 ..self
400 }
401 }
402
403 pub fn with_loop(self, r#loop: impl Into<Option<Loop>>) -> Self {
405 Self {
406 r#loop: r#loop.into(),
407 ..self
408 }
409 }
410
411 pub fn file(&self) -> &std::path::Path {
413 &self.file
414 }
415
416 pub fn sample_start(&self) -> Option<f64> {
418 self.sample_start
419 }
420
421 pub fn sample_stop(&self) -> Option<f64> {
423 self.sample_stop
424 }
425
426 pub fn gain(&self) -> Option<f64> {
428 self.gain
429 }
430
431 pub fn group(&self) -> Option<isize> {
433 self.group
434 }
435
436 pub fn parameter_1(&self) -> Option<f64> {
438 self.parameter_1
439 }
440
441 pub fn parameter_2(&self) -> Option<f64> {
443 self.parameter_2
444 }
445
446 pub fn parameter_3(&self) -> Option<f64> {
448 self.parameter_3
449 }
450
451 pub fn reverse(&self) -> Option<bool> {
453 self.reverse
454 }
455
456 pub fn zone_logic(&self) -> Option<ZoneLogic> {
458 self.zone_logic
459 }
460
461 pub fn key(&self) -> &Option<Key> {
463 &self.key
464 }
465
466 pub fn velocity(&self) -> &Option<ZoneInfo> {
468 &self.velocity
469 }
470
471 pub fn select(&self) -> &Option<ZoneInfo> {
473 &self.select
474 }
475
476 pub fn r#loop(&self) -> &Option<Loop> {
478 &self.r#loop
479 }
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
484#[serde(rename_all = "kebab-case")]
485pub enum ZoneLogic {
486 AlwaysPlay,
488 RoundRobin,
490}
491
492#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
494pub struct Key {
495 #[serde(rename = "@root", default, skip_serializing_if = "Option::is_none")]
496 root: Option<u8>,
497 #[serde(rename = "@track", default, skip_serializing_if = "Option::is_none")]
498 track: Option<f64>,
499 #[serde(rename = "@tune", default, skip_serializing_if = "Option::is_none")]
500 tune: Option<f64>,
501 #[serde(rename = "@low", default, skip_serializing_if = "Option::is_none")]
502 low: Option<u8>,
503 #[serde(rename = "@high", default, skip_serializing_if = "Option::is_none")]
504 high: Option<u8>,
505 #[serde(rename = "@low-fade", default, skip_serializing_if = "Option::is_none")]
506 low_fade: Option<u8>,
507 #[serde(
508 rename = "@high-fade",
509 default,
510 skip_serializing_if = "Option::is_none"
511 )]
512 high_fade: Option<u8>,
513}
514
515impl Key {
516 pub fn with_root(self, root: impl Into<Option<u8>>) -> Self {
518 Self {
519 root: root.into(),
520 ..self
521 }
522 }
523
524 pub fn with_track(self, track: impl Into<Option<f64>>) -> Self {
526 Self {
527 track: track.into(),
528 ..self
529 }
530 }
531
532 pub fn with_tune(self, tune: impl Into<Option<f64>>) -> Self {
534 Self {
535 tune: tune.into(),
536 ..self
537 }
538 }
539
540 pub fn with_low(self, low: impl Into<Option<u8>>) -> Self {
542 Self {
543 low: low.into(),
544 ..self
545 }
546 }
547
548 pub fn with_high(self, high: impl Into<Option<u8>>) -> Self {
550 Self {
551 high: high.into(),
552 ..self
553 }
554 }
555
556 pub fn with_low_fade(self, low_fade: impl Into<Option<u8>>) -> Self {
558 Self {
559 low_fade: low_fade.into(),
560 ..self
561 }
562 }
563
564 pub fn with_high_fade(self, high_fade: impl Into<Option<u8>>) -> Self {
566 Self {
567 high_fade: high_fade.into(),
568 ..self
569 }
570 }
571
572 pub fn root(&self) -> Option<u8> {
574 self.root
575 }
576
577 pub fn track(&self) -> Option<f64> {
579 self.track
580 }
581
582 pub fn tune(&self) -> Option<f64> {
584 self.tune
585 }
586
587 pub fn low(&self) -> Option<u8> {
589 self.low
590 }
591
592 pub fn high(&self) -> Option<u8> {
594 self.high
595 }
596
597 pub fn low_fade(&self) -> Option<u8> {
599 self.low_fade
600 }
601
602 pub fn high_fade(&self) -> Option<u8> {
604 self.high_fade
605 }
606}
607
608#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
610pub struct ZoneInfo {
611 #[serde(rename = "@low", default, skip_serializing_if = "Option::is_none")]
612 low: Option<u8>,
613 #[serde(rename = "@high", default, skip_serializing_if = "Option::is_none")]
614 high: Option<u8>,
615 #[serde(rename = "@low-fade", default, skip_serializing_if = "Option::is_none")]
616 low_fade: Option<u8>,
617 #[serde(
618 rename = "@high-fade",
619 default,
620 skip_serializing_if = "Option::is_none"
621 )]
622 high_fade: Option<u8>,
623}
624
625impl ZoneInfo {
626 pub fn with_low(self, low: impl Into<Option<u8>>) -> Self {
628 Self {
629 low: low.into(),
630 ..self
631 }
632 }
633
634 pub fn with_high(self, high: impl Into<Option<u8>>) -> Self {
636 Self {
637 high: high.into(),
638 ..self
639 }
640 }
641
642 pub fn with_low_fade(self, low_fade: impl Into<Option<u8>>) -> Self {
644 Self {
645 low_fade: low_fade.into(),
646 ..self
647 }
648 }
649
650 pub fn with_high_fade(self, high_fade: impl Into<Option<u8>>) -> Self {
652 Self {
653 high_fade: high_fade.into(),
654 ..self
655 }
656 }
657
658 pub fn low(&self) -> Option<u8> {
660 self.low
661 }
662
663 pub fn high(&self) -> Option<u8> {
665 self.high
666 }
667
668 pub fn low_fade(&self) -> Option<u8> {
670 self.low_fade
671 }
672
673 pub fn high_fade(&self) -> Option<u8> {
675 self.high_fade
676 }
677}
678
679#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
681pub struct Loop {
682 #[serde(rename = "@mode", skip_serializing_if = "Option::is_none")]
683 mode: Option<LoopMode>,
684 #[serde(rename = "@start", skip_serializing_if = "Option::is_none")]
685 start: Option<f64>,
686 #[serde(rename = "@stop", skip_serializing_if = "Option::is_none")]
687 stop: Option<f64>,
688 #[serde(rename = "@fade", skip_serializing_if = "Option::is_none")]
689 fade: Option<f64>,
690}
691
692impl Loop {
693 pub fn with_mode(self, mode: impl Into<Option<LoopMode>>) -> Self {
695 Self {
696 mode: mode.into(),
697 ..self
698 }
699 }
700
701 pub fn with_start(self, start: impl Into<Option<f64>>) -> Self {
703 Self {
704 start: start.into(),
705 ..self
706 }
707 }
708
709 pub fn with_stop(self, stop: impl Into<Option<f64>>) -> Self {
711 Self {
712 stop: stop.into(),
713 ..self
714 }
715 }
716
717 pub fn with_fade(self, fade: impl Into<Option<f64>>) -> Self {
719 Self {
720 fade: fade.into(),
721 ..self
722 }
723 }
724
725 pub fn mode(&self) -> Option<LoopMode> {
727 self.mode
728 }
729
730 pub fn start(&self) -> Option<f64> {
732 self.start
733 }
734
735 pub fn stop(&self) -> Option<f64> {
737 self.stop
738 }
739
740 pub fn fade(&self) -> Option<f64> {
742 self.fade
743 }
744}
745
746#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
748#[serde(rename_all = "kebab-case")]
749pub enum LoopMode {
750 #[default]
752 Off,
753 Loop,
755 PingPong,
757}