ot_tools_io/generics.rs
1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6use crate::arrangements::ArrangeRow;
7use crate::identifiers::{
8 ArrId, BankId, PartId, PatternId, PlaybackSlotId, RecBufId, SavedState, SceneId, SliceId,
9 TrackId, TrigId,
10};
11use crate::macros::*;
12use crate::markers::SlotMarkers;
13use crate::parts::Part;
14use crate::patterns::Pattern;
15use crate::projects::SlotAttributes;
16use crate::{
17 ArrangementFile, BankFile, Defaults, HasHeaderField, OctatrackFileIO, OtToolsIoError,
18 SampleSettingsFile,
19};
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21use serde_big_array::BigArray;
22use std::array::from_fn;
23use std::ops::{Deref, DerefMut, Index, IndexMut};
24use std::path::PathBuf;
25
26/// Generic collection for [`ArrangementFile`]s.
27///
28/// # [`Serialize`]/[`Deserialize`]
29///
30/// Neither [`Serialize`] nor [`Deserialize`] are implemented for this type.
31///
32/// # `Box` (no `Copy`)
33///
34/// The inner array of this type is wrapped in a `Box`.
35/// -- we cannot fit all [`ArrangementFile`]s on the stack.
36/// Unfortunately this means the type does not implement `Copy`.
37///
38/// # Example: Extending in your own code with custom inner types
39/// ```
40/// use std::path::PathBuf;
41/// use ot_tools_io::types::Arrangements;
42///
43/// use ot_tools_io::Defaults;
44/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
45/// use std::array::from_fn;
46///
47/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
48/// pub struct BackupPath(PathBuf);
49///
50/// # fn main() -> Result<(), ()> {
51/// let mut my_backups = Arrangements::<BackupPath>::default();
52/// let my_new_backup = BackupPath(PathBuf::from("some_path"));
53/// let first = my_backups.first_mut().ok_or(())?;
54/// *first = my_new_backup;
55///
56/// // first one changed
57/// assert_eq!(my_backups[0], BackupPath(PathBuf::from("some_path")));
58/// // rest are default
59/// assert_eq!(my_backups[1], BackupPath::default());
60///
61/// // correct length
62/// assert_eq!(my_backups.len(), 8);
63///
64/// # Ok(()) }
65/// ```
66#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
67pub struct Arrangements<T>(Box<[T; 8]>);
68
69impl<T> Arrangements<T> {
70 pub fn new(value: [T; 8]) -> Self {
71 Self(Box::new(value))
72 }
73
74 pub fn new_boxed(value: Box<[T; 8]>) -> Self {
75 Self(value)
76 }
77}
78
79generic_newtype_id_lookups!(Arrangements, ArrId);
80generic_newtype_unbox!(Arrangements, 8);
81generic_newtype_asref!(Arrangements);
82generic_newtype_asmut!(Arrangements);
83generic_newtype_deref!(Arrangements, 8);
84generic_newtype_deref_mut!(Arrangements);
85
86impl<T: Defaults<Box<[T; 8]>> + Default> Default for Arrangements<T> {
87 fn default() -> Self {
88 Self(T::defaults())
89 }
90}
91
92impl HasHeaderField for Arrangements<ArrangementFile> {
93 fn check_header(&self) -> Result<bool, OtToolsIoError> {
94 Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
95 }
96}
97
98/// Concrete implementation for [`ArrangementFile`] adds two methods for reading and writing a
99/// batch of files.
100impl Arrangements<ArrangementFile> {
101 /// Reads a collection of [`ArrangementFile`]s from a directory.
102 pub fn from_data_files<P>(dirpath: P, state: &SavedState) -> Result<Self, OtToolsIoError>
103 where
104 PathBuf: From<P>,
105 {
106 let mut new = Self::default();
107 let path = PathBuf::from(dirpath);
108 for id in ArrId::iter() {
109 let data = ArrangementFile::from_data_file(&path.join(id.file_stem(state)))?;
110 new[id.as_index()] = data;
111 }
112 Ok(new)
113 }
114
115 /// Writes a collection of [`ArrangementFile`]s to a directory.
116 ///
117 /// Will skip writing a particular [`ArrangementFile`] if the
118 /// `skip_unchanged` argument is `true` and the current [`ArrangementFile`]
119 /// matches the data in the existing file on the file system.
120 pub fn to_data_files<P>(
121 &self,
122 dirpath: P,
123 state: &SavedState,
124 skip_unchanged: bool,
125 ) -> Result<(), OtToolsIoError>
126 where
127 PathBuf: From<P>,
128 {
129 let path = PathBuf::from(dirpath);
130 for id in ArrId::iter() {
131 let arr = self.id_ref(&id);
132 if skip_unchanged {
133 if let Ok(exists) = ArrangementFile::from_data_file(&path.join(id.file_stem(state)))
134 {
135 if &exists != arr {
136 arr.to_data_file(&path.join(id.file_stem(state)))?;
137 }
138 } else {
139 // probably doesn't exist
140 arr.to_data_file(&path.join(id.file_stem(state)))?;
141 }
142 } else {
143 arr.to_data_file(&path.join(id.file_stem(state)))?;
144 }
145 }
146 Ok(())
147 }
148}
149
150/// Generic array wrapper for [`ArrangeRow`]s.
151///
152/// # Example: Extending in your own code with custom inner types
153/// ```
154/// use ot_tools_io::types::ArrangeRows;
155///
156/// use ot_tools_io::Defaults;
157/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
158/// use std::array::from_fn;
159///
160/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
161/// pub struct CustomReminder(String);
162///
163/// # fn main() -> Result<(), ()> {
164/// let mut my_arr_reminders = ArrangeRows::<CustomReminder>::default();
165/// let first = my_arr_reminders.first_mut().ok_or(())?;
166/// *first = CustomReminder(
167/// String::from(
168/// "imagine this is a long reminder value that goes on for longer than normally allowed"
169/// )
170/// );
171///
172/// // first one changed
173/// assert_eq!(
174/// my_arr_reminders[0],
175/// CustomReminder(
176/// String::from(
177/// "imagine this is a long reminder value that goes on for longer than normally allowed"
178/// )
179/// )
180/// );
181/// // rest are default
182/// assert_eq!(my_arr_reminders[1], CustomReminder::default());
183///
184/// // correct length
185/// assert_eq!(my_arr_reminders.len(), 256);
186///
187/// # Ok(()) }
188/// ```
189#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190pub struct ArrangeRows<T>([T; 256]);
191
192impl<T> ArrangeRows<T> {
193 pub fn new(value: [T; 256]) -> Self {
194 Self(value)
195 }
196}
197
198generic_newtype_asref!(ArrangeRows);
199generic_newtype_asmut!(ArrangeRows);
200generic_newtype_deref!(ArrangeRows, 256);
201generic_newtype_deref_mut!(ArrangeRows);
202
203impl<T: Defaults<[T; 256]> + Default> Default for ArrangeRows<T> {
204 fn default() -> Self {
205 Self(T::defaults())
206 }
207}
208
209impl<'de> Deserialize<'de> for ArrangeRows<ArrangeRow> {
210 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211 where
212 D: Deserializer<'de>,
213 {
214 Ok(Self(
215 <[ArrangeRow; 256] as BigArray<ArrangeRow>>::deserialize(deserializer)?,
216 ))
217 }
218}
219
220impl Serialize for ArrangeRows<ArrangeRow> {
221 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
222 where
223 S: Serializer,
224 {
225 <[ArrangeRow; 256] as BigArray<ArrangeRow>>::serialize(&self.0, serializer)
226 }
227}
228
229/// Generic collection for [`BankFile`]s.
230///
231/// # [`Serialize`]/[`Deserialize`]
232///
233/// Neither [`Serialize`] nor [`Deserialize`] are implemented for this type.
234///
235/// # `Box` (no `Copy`)
236///
237/// The inner array of this type is wrapped in a `Box`.
238/// -- we cannot fit all [`BankFile`]s on the stack.
239/// Unfortunately this means the type does not implement `Copy`.
240///
241/// # Example: Extending in your own code with custom inner types
242/// ```
243/// use ot_tools_io::types::Banks;
244/// use std::path::PathBuf;
245///
246/// use ot_tools_io::Defaults;
247/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
248/// use std::array::from_fn;
249///
250/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
251/// pub struct YamlPath(PathBuf);
252///
253/// # fn main() -> Result<(), ()> {
254/// let mut my_bank_yamls = Banks::<YamlPath>::default();
255/// let first = my_bank_yamls.first_mut().ok_or(())?;
256/// *first = YamlPath(PathBuf::from("some_path"));
257///
258/// // first one changed
259/// assert_eq!(my_bank_yamls[0], YamlPath(PathBuf::from("some_path")));
260/// // rest are default
261/// assert_eq!(my_bank_yamls[1], YamlPath::default());
262///
263/// // correct length
264/// assert_eq!(my_bank_yamls.len(), 16);
265///
266/// # Ok(()) }
267/// ```
268#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
269pub struct Banks<T>(Box<[T; 16]>);
270
271impl<T> Banks<T> {
272 pub fn new(value: [T; 16]) -> Self {
273 Self(Box::new(value))
274 }
275
276 pub fn new_boxed(value: Box<[T; 16]>) -> Self {
277 Self(value)
278 }
279}
280
281generic_newtype_id_lookups!(Banks, BankId);
282generic_newtype_unbox!(Banks, 16);
283generic_newtype_asref!(Banks);
284generic_newtype_asmut!(Banks);
285generic_newtype_deref!(Banks, 16);
286generic_newtype_deref_mut!(Banks);
287
288impl<T: Defaults<Box<[T; 16]>> + Default> Default for Banks<T> {
289 fn default() -> Self {
290 Self(T::defaults())
291 }
292}
293
294impl HasHeaderField for Banks<BankFile> {
295 fn check_header(&self) -> Result<bool, OtToolsIoError> {
296 Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
297 }
298}
299
300/// Concrete implementation for [`BankFile`]s.
301/// Implements methods for reading and writing all [`BankFile`]s within an octatrack project directory.
302impl Banks<BankFile> {
303 /// Reads a collection of [`BankFile`]s from a directory.
304 pub fn from_data_files<P>(dirpath: P, state: &SavedState) -> Result<Self, OtToolsIoError>
305 where
306 PathBuf: From<P>,
307 {
308 let mut new = Self::default();
309 let path = PathBuf::from(dirpath);
310 for id in BankId::iter() {
311 let bank = BankFile::from_data_file(&path.join(id.file_stem(state)))?;
312 new[id.as_index()] = bank;
313 }
314 Ok(new)
315 }
316
317 /// Writes a collection of [`BankFile`]s to a directory
318 ///
319 /// Will skip writing a particular [`BankFile`] if the `skip_unchanged`
320 /// argument is `true` and the current [`BankFile`] matches the data in the
321 /// existing file on the file system.
322 pub fn to_data_files<P>(
323 &self,
324 dirpath: P,
325 state: &SavedState,
326 skip_unchanged: bool,
327 ) -> Result<(), OtToolsIoError>
328 where
329 PathBuf: From<P>,
330 {
331 let path = PathBuf::from(dirpath);
332 for id in BankId::iter() {
333 let bank = self.id_ref(&id);
334 if skip_unchanged {
335 if let Ok(exists) = BankFile::from_data_file(&path.join(id.file_stem(state))) {
336 if &exists != bank {
337 bank.to_data_file(&path.join(id.file_stem(state)))?;
338 }
339 } else {
340 // probably doesn't exist
341 bank.to_data_file(&path.join(id.file_stem(state)))?;
342 }
343 } else {
344 bank.to_data_file(&path.join(id.file_stem(state)))?;
345 }
346 }
347 Ok(())
348 }
349}
350
351/// Generic collection for [`Part`]s
352///
353/// # [`Serialize`]/[`Deserialize`]
354///
355/// [`Serialize`] and [`Deserialize`] are implemented for this type.
356///
357/// # `Box` (no `Copy`)
358///
359/// The inner array of this type is wrapped in a `Box`.
360/// Fields within a [`Part`] already use a `Box` so we are forced to wrap the data in a `Box` here.
361///
362/// # Example: Extending in your own code with custom inner types
363/// ```
364/// use ot_tools_io::types::Parts;
365/// use std::path::PathBuf;
366///
367/// use ot_tools_io::Defaults;
368/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
369/// use std::array::from_fn;
370///
371/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
372/// pub struct LongPartName(String);
373///
374/// # fn main() -> Result<(), ()> {
375/// let mut my_part_names = Parts::<LongPartName>::default();
376/// let first = my_part_names.first_mut().ok_or(())?;
377/// *first = LongPartName(String::from("My awesome part name that is too long on the octatrack"));
378///
379/// // first one changed
380/// assert_eq!(
381/// my_part_names[0],
382/// LongPartName(
383/// String::from("My awesome part name that is too long on the octatrack")
384/// )
385/// );
386/// // rest are default
387/// assert_eq!(my_part_names[1], LongPartName::default());
388///
389///
390/// // correct length
391/// assert_eq!(my_part_names.len(), 4);
392///
393/// # Ok(()) }
394/// ```
395#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
396pub struct Parts<T>(Box<[T; 4]>);
397
398impl<T> Parts<T> {
399 pub fn new(value: [T; 4]) -> Self {
400 Self(Box::new(value))
401 }
402
403 pub fn new_boxed(value: Box<[T; 4]>) -> Self {
404 Self(value)
405 }
406}
407
408generic_newtype_id_lookups!(Parts, PartId);
409generic_newtype_unbox!(Parts, 4);
410generic_newtype_asref!(Parts);
411generic_newtype_asmut!(Parts);
412generic_newtype_deref!(Parts, 4);
413generic_newtype_deref_mut!(Parts);
414
415impl<T: Defaults<Box<[T; 4]>> + Default> Default for Parts<T> {
416 fn default() -> Self {
417 Self(T::defaults())
418 }
419}
420
421impl<'de> Deserialize<'de> for Parts<Part> {
422 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
423 where
424 D: Deserializer<'de>,
425 {
426 let boxed = Box::new(<[Part; 4] as BigArray<Part>>::deserialize(deserializer)?);
427 Ok(Self(boxed))
428 }
429}
430
431impl Serialize for Parts<Part> {
432 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
433 where
434 S: Serializer,
435 {
436 <[Part; 4] as BigArray<Part>>::serialize(&self.0, serializer)
437 }
438}
439
440impl HasHeaderField for Parts<Part> {
441 fn check_header(&self) -> Result<bool, OtToolsIoError> {
442 Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
443 }
444}
445
446/// Generic collection for [`Pattern`]s
447///
448/// # [`Serialize`]/[`Deserialize`]
449///
450/// [`Serialize`] and [`Deserialize`] are implemented for this type.
451///
452/// # `Box` (no `Copy`)
453///
454/// Unfortunately serde requires that we wrap the inner array of `Patterns` in a `Box`.
455/// So `Patterns`, and any type that contains a `Patterns` type, cannot implement the `Copy`
456/// trait.
457///
458/// # Example: Extending in your own code with custom inner types
459/// ```
460/// use ot_tools_io::types::Patterns;
461/// use std::path::PathBuf;
462///
463/// use ot_tools_io::Defaults;
464/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
465/// use std::array::from_fn;
466///
467/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
468/// pub struct SomeString(String);
469///
470/// # fn main() -> Result<(), ()> {
471/// let mut my_pattern_descriptions = Patterns::<SomeString>::default();
472/// let first = my_pattern_descriptions.first_mut().ok_or(())?;
473/// *first = SomeString(String::from("My awesome pattern"));
474///
475/// // first one changed
476/// assert_eq!(
477/// my_pattern_descriptions[0],
478/// SomeString(
479/// String::from("My awesome pattern")
480/// )
481/// );
482/// // rest are default
483/// assert_eq!(my_pattern_descriptions[1], SomeString::default());
484///
485/// // correct length
486/// assert_eq!(my_pattern_descriptions.len(), 16);
487///
488/// # Ok(()) }
489/// ```
490#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
491pub struct Patterns<T>(Box<[T; 16]>);
492
493impl<T> Patterns<T> {
494 pub fn new(value: [T; 16]) -> Self {
495 Self(Box::new(value))
496 }
497
498 pub fn new_boxed(value: Box<[T; 16]>) -> Self {
499 Self(value)
500 }
501}
502
503generic_newtype_id_lookups!(Patterns, PatternId);
504generic_newtype_unbox!(Patterns, 16);
505generic_newtype_asref!(Patterns);
506generic_newtype_asmut!(Patterns);
507generic_newtype_deref!(Patterns, 16);
508generic_newtype_deref_mut!(Patterns);
509
510impl<T: Defaults<Box<[T; 16]>> + Default> Default for Patterns<T> {
511 fn default() -> Self {
512 Self(T::defaults())
513 }
514}
515
516impl<'de> Deserialize<'de> for Patterns<Pattern> {
517 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
518 where
519 D: Deserializer<'de>,
520 {
521 let boxed = Box::new(<[Pattern; 16] as BigArray<Pattern>>::deserialize(
522 deserializer,
523 )?);
524 Ok(Self(boxed))
525 }
526}
527
528impl Serialize for Patterns<Pattern> {
529 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
530 where
531 S: Serializer,
532 {
533 <[Pattern; 16] as BigArray<Pattern>>::serialize(&self.0, serializer)
534 }
535}
536
537impl HasHeaderField for Patterns<Pattern> {
538 fn check_header(&self) -> Result<bool, OtToolsIoError> {
539 Ok(self.iter().all(|x| x.check_header().is_ok_and(|x| x)))
540 }
541}
542
543/// Generic collection for data related to tracks, e.g. for Recording Buffers, Audio Track Trigs,
544/// MIDI Track Trigs and the like.
545/// Used by a lot of fields on the [`Part`] and [`Pattern`] types.
546///
547/// # `Copy` (no `Box`)
548///
549/// This generic collection helper does not use `Box` and can be `Copy`.
550///
551/// # Example: Using in your own code with custom inner types
552///
553/// ```
554/// use ot_tools_io::types::Tracks;
555/// use ot_tools_io::Defaults;
556/// use ot_tools_io_derive::ArrayDefaults;
557/// use std::array::from_fn;
558///
559/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
560/// pub struct SomeNumber(u8);
561///
562/// fn main() -> Result<(), ()> {
563/// let mut my_tracks = Tracks::<SomeNumber>::default();
564/// let first = my_tracks.first_mut().ok_or(())?;
565/// *first = SomeNumber(100);
566///
567/// // first one changed
568/// assert_eq!(my_tracks[0], SomeNumber(100));
569/// // rest are default
570/// assert_eq!(my_tracks[1], SomeNumber(0));
571///
572/// // correct length
573/// assert_eq!(my_tracks.len(), 8);
574///
575/// Ok(())
576/// }
577/// ```
578#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
579pub struct Tracks<T>([T; 8]);
580
581generic_newtype_id_lookups!(Tracks, TrackId);
582generic_newtype_asref!(Tracks);
583generic_newtype_asmut!(Tracks);
584generic_newtype_deref!(Tracks, 8);
585generic_newtype_deref_mut!(Tracks);
586generic_newtype_index!(Tracks);
587generic_newtype_index_mut!(Tracks);
588
589impl<T: Defaults<[T; 8]> + Default> Default for Tracks<T> {
590 fn default() -> Self {
591 Self(T::defaults())
592 }
593}
594
595impl<const N: usize, T: Default> Defaults<[Self; N]> for Tracks<T> {
596 fn defaults() -> [Self; N]
597 where
598 Self: Default,
599 {
600 from_fn(|_| Self::default())
601 }
602}
603
604impl<T> Tracks<T> {
605 pub fn new(value: [T; 8]) -> Self {
606 Self(value)
607 }
608}
609
610impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tracks<T> {
611 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
612 where
613 D: Deserializer<'de>,
614 {
615 Ok(Self(<[T; 8] as BigArray<T>>::deserialize(deserializer)?))
616 }
617}
618
619impl<T: Serialize> Serialize for Tracks<T> {
620 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
621 where
622 S: Serializer,
623 {
624 <[T; 8] as BigArray<T>>::serialize(&self.0, serializer)
625 }
626}
627
628/// Generic collection for scene data.
629/// Used in the `scenes` and `scene_xlvs` fields on [`Part`].
630///
631/// # `Copy` (no `Box`)
632///
633/// This generic collection helper does not use `Box` and can be `Copy`.
634///
635/// # Example: Using in your own code with custom inner types
636///
637/// ```
638/// use ot_tools_io::types::Scenes;
639/// use ot_tools_io::Defaults;
640/// use ot_tools_io_derive::ArrayDefaults;
641/// use std::array::from_fn;
642///
643/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
644/// pub struct SomeNumber(u8);
645///
646/// fn main() -> Result<(), ()> {
647/// let mut my_scenes = Scenes::<SomeNumber>::default();
648/// let first = my_scenes.first_mut().ok_or(())?;
649/// *first = SomeNumber(100);
650///
651/// // first one changed
652/// assert_eq!(my_scenes[0], SomeNumber(100));
653/// // rest are default
654/// assert_eq!(my_scenes[1], SomeNumber(0));
655///
656/// // correct length
657/// assert_eq!(my_scenes.len(), 16);
658///
659/// Ok(())
660/// }
661/// ```
662#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
663pub struct Scenes<T>([T; 16]);
664
665generic_newtype_id_lookups!(Scenes, SceneId);
666generic_newtype_asref!(Scenes);
667generic_newtype_asmut!(Scenes);
668generic_newtype_deref!(Scenes, 16);
669generic_newtype_deref_mut!(Scenes);
670generic_newtype_index!(Scenes);
671generic_newtype_index_mut!(Scenes);
672
673impl<T: Defaults<[T; 16]> + Default> Default for Scenes<T> {
674 fn default() -> Self {
675 Self(T::defaults())
676 }
677}
678
679impl<T> Scenes<T> {
680 pub fn new(value: [T; 16]) -> Self {
681 Self(value)
682 }
683}
684
685impl<'de, T: Deserialize<'de>> Deserialize<'de> for Scenes<T> {
686 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
687 where
688 D: Deserializer<'de>,
689 {
690 Ok(Self(<[T; 16] as BigArray<T>>::deserialize(deserializer)?))
691 }
692}
693
694impl<T: Serialize> Serialize for Scenes<T> {
695 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
696 where
697 S: Serializer,
698 {
699 <[T; 16] as BigArray<T>>::serialize(&self.0, serializer)
700 }
701}
702
703/// Generic collection for slice data.
704///
705/// # Example: Using in your own code with custom inner types
706///
707/// ```
708/// use ot_tools_io::types::Slices;
709/// use ot_tools_io::Defaults;
710/// use ot_tools_io_derive::ArrayDefaults;
711/// use std::array::from_fn;
712///
713/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
714/// pub struct SomeNumber(u8);
715///
716/// fn main() -> Result<(), ()> {
717/// let mut my_slices = Slices::<SomeNumber>::default();
718/// let first = my_slices.first_mut().ok_or(())?;
719/// *first = SomeNumber(100);
720///
721/// // first one changed
722/// assert_eq!(my_slices[0], SomeNumber(100));
723/// // rest are default
724/// assert_eq!(my_slices[1], SomeNumber(0));
725///
726/// // correct length
727/// assert_eq!(my_slices.len(), 64);
728///
729/// Ok(())
730/// }
731/// ```
732#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
733pub struct Slices<T>([T; 64]);
734
735generic_newtype_id_lookups!(Slices, SliceId);
736generic_newtype_asref!(Slices);
737generic_newtype_asmut!(Slices);
738generic_newtype_deref!(Slices, 64);
739generic_newtype_deref_mut!(Slices);
740generic_newtype_index!(Slices);
741generic_newtype_index_mut!(Slices);
742generic_newtype_into_iter!(Slices, 64);
743
744impl<T: Defaults<[T; 64]> + Default> Default for Slices<T> {
745 fn default() -> Self {
746 Self(T::defaults())
747 }
748}
749
750impl<T> Slices<T> {
751 pub fn new(value: [T; 64]) -> Self {
752 Self(value)
753 }
754}
755
756impl<'de, T: Deserialize<'de>> Deserialize<'de> for Slices<T> {
757 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
758 where
759 D: Deserializer<'de>,
760 {
761 Ok(Self(<[T; 64] as BigArray<T>>::deserialize(deserializer)?))
762 }
763}
764
765impl<T: Serialize> Serialize for Slices<T> {
766 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
767 where
768 S: Serializer,
769 {
770 <[T; 64] as BigArray<T>>::serialize(&self.0, serializer)
771 }
772}
773
774/// Generic collection for data related to trigs.
775/// Used in [`Pattern`]s with parameter lock trig assignments and trig offset/repeat/conditions data.
776///
777/// # `Box` (no-`Copy`)
778///
779/// Unfortunately serde requires that we wrap the inner array type of [`Trigs`] in a `Box`.
780/// As a result, [`Trigs`] and any type that contains a [`Trigs`] type cannot implement the `Copy` trait.
781///
782/// # Example: Using in your own code with custom inner types
783///
784/// ```
785/// use ot_tools_io::types::Trigs;
786/// use ot_tools_io::Defaults;
787/// use ot_tools_io_derive::{ArrayDefaults, BoxedArrayDefaults};
788/// use std::array::from_fn;
789///
790/// #[derive(Debug, PartialEq, Default, ArrayDefaults, BoxedArrayDefaults)]
791/// pub struct SomeNumber(u8);
792///
793/// fn main() -> Result<(), ()> {
794/// let mut my_trigs = Trigs::<SomeNumber>::default();
795/// let first = my_trigs.first_mut().ok_or(())?;
796/// *first = SomeNumber(100);
797///
798/// // first one changed
799/// assert_eq!(my_trigs[0], SomeNumber(100));
800/// // rest are default
801/// assert_eq!(my_trigs[1], SomeNumber(0));
802///
803/// // correct length
804/// assert_eq!(my_trigs.len(), 64);
805///
806/// Ok(())
807/// }
808/// ```
809#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
810pub struct Trigs<T>(Box<[T; 64]>);
811
812generic_newtype_id_lookups!(Trigs, TrigId);
813generic_newtype_unbox!(Trigs, 64);
814generic_newtype_asref!(Trigs);
815generic_newtype_asmut!(Trigs);
816generic_newtype_deref!(Trigs, 64);
817generic_newtype_deref_mut!(Trigs);
818
819impl<T: Defaults<Box<[T; 64]>> + Default> Default for Trigs<T> {
820 fn default() -> Self {
821 Self(T::defaults())
822 }
823}
824
825impl<T> Trigs<T> {
826 pub fn new(value: [T; 64]) -> Self {
827 Self(Box::new(value))
828 }
829
830 pub fn new_boxed(value: Box<[T; 64]>) -> Self {
831 Self(value)
832 }
833}
834
835impl<'de, T: Deserialize<'de>> Deserialize<'de> for Trigs<T> {
836 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
837 where
838 D: Deserializer<'de>,
839 {
840 Ok(Self(Box::new(<[T; 64] as BigArray<T>>::deserialize(
841 deserializer,
842 )?)))
843 }
844}
845
846impl<T: Serialize> Serialize for Trigs<T> {
847 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
848 where
849 S: Serializer,
850 {
851 <[T; 64] as BigArray<T>>::serialize(&self.0, serializer)
852 }
853}
854
855impl<T, I> Index<I> for Trigs<T>
856where
857 [T]: Index<I>,
858{
859 type Output = <[T] as Index<I>>::Output;
860 #[inline]
861 fn index(&self, index: I) -> &Self::Output {
862 Index::index(&*self.0 as &[T], index)
863 }
864}
865
866impl<T, I> IndexMut<I> for Trigs<T>
867where
868 [T]: IndexMut<I>,
869{
870 #[inline]
871 fn index_mut(&mut self, index: I) -> &mut Self::Output {
872 IndexMut::index_mut(&mut *self.0 as &mut [T], index)
873 }
874}
875
876#[allow(rustdoc::invalid_rust_codeblocks)]
877/// Generic collection for **playback slot** items, e.g. Static and Flex slot data.
878///
879/// # Extending: Custom New Types
880///
881/// As this type is generic you can use custom new type structs in downstream applications /
882/// libraries, e.g. to use your own custom 'ApplicationSlot' type which has extra methods/fields.
883///
884/// ```
885/// use ot_tools_io::types::PlaybackSlots;
886/// use ot_tools_io::Defaults;
887/// use std::path::PathBuf;
888///
889/// #[derive(Debug, PartialEq)]
890/// pub struct PathOnlySlotType(PathBuf);
891///
892/// impl Default for PathOnlySlotType {
893/// fn default() -> Self {
894/// Self(PathBuf::from("default/path/to/some/file.ext"))
895/// }
896/// }
897///
898/// impl Defaults<Box<[Self; 128]>> for PathOnlySlotType {
899/// fn defaults() -> Box<[Self; 128]> where Self: Default {
900/// Box::new(std::array::from_fn(|_| Self::default()))
901/// }
902/// }
903///
904/// # fn main() -> Result<(), ()> {
905/// let mut my_slots = PlaybackSlots::<PathOnlySlotType>::default();
906/// let first = my_slots.first_mut().ok_or(())?;
907/// *first = PathOnlySlotType(PathBuf::from("some_path"));
908///
909/// // first one changed
910/// assert_eq!(my_slots[0], PathOnlySlotType(PathBuf::from("some_path")));
911/// // rest are default
912/// assert_eq!(my_slots[1], PathOnlySlotType::default());
913/// # Ok(()) }
914/// ```
915///
916/// # Note: Name of the type
917///
918/// This type should only hold elements related to static or flex slots.
919/// i.e. no recording buffer slots, ever (slot_type = flex, slot_id > 128 etc.).
920///
921/// Admittedly the name [`PlaybackSlots`] is somewhat of a misnomer
922/// - Flex slots can be recorded to
923/// - Sample data stored in Recording Buffers can be played back on audio tracks
924///
925/// However, those are *special cases* of Octatrack usage:
926/// - The designed intent of Recording Buffers is to **record to the slot**.
927/// - The designed intent of Flex Slots is to **edit the audio data and playback**.
928/// - The designed intent of Static Slots is to **only playback**.
929///
930/// # Explainer: Why does this type exist?
931///
932/// > **note**: the examples in this section may not compile.
933/// > they are here to demonstrate problems when using previous versions of slot data types.
934/// > they are not here as examples to actually implement yourself!
935///
936/// Recording Buffers are Flex slots with a Slot ID > 128 under the hood (or more literally the
937/// metal case) of the Octatrack.
938/// I originally decided to represent this in this "io" library.
939/// Recording buffers are actually Flex slots.
940/// That was it.
941/// Done.
942///
943/// The point of the library was to load data from the binary files at the lowest granularity
944/// possible
945/// -- representing how data is stored in each binary file.
946/// The thinking behind this decision was that it would allow more flexibility when using the
947/// library
948/// -- access to data at the lowest granularity means the ability to do "more stuff", right?
949///
950/// Having worked with the data in that format for a while
951/// -- it is a massive pain in the arse dealing with the "recording buffers are actually flex slots"
952/// special case.
953///
954/// The library now treats Recording Buffers as a different special type of slot during
955/// Serialization/Deserialization
956/// -- separate to Flex and Static slots which are considered **Playback Slots**.
957///
958/// ## first example of the pain
959/// - simple matches need an extra case where you need to be aware of the `slot_id > 128` rule.
960/// - users needing to be aware of some hidden logic/rule == bad.
961/// - my first draft of this example got the rule wrong! i used `slot_id > 129`! and i'm the author
962/// of this library!
963/// ```compile_fail
964/// match slot.slot_type {
965/// SlotType::Static => {}, // do something to static slots
966/// SlotType::Flex => {}, // do something to flex slots
967/// SlotType::Flex if slot.slot_id > 128 => _, // this one
968/// }
969/// ```
970/// ## second example of the pain
971/// - our `flex_slots` variable contains `Option<SlotAttributes>` instances with the `slot_type` field
972/// equal to `SlotType::Flex` -- so, only flex slot data, right?
973/// - not necessarily ... we often still need to check whether an element returned from the
974/// collection is actually a flex playback slot and not a recording buffer slot.
975/// - so we have to doa manual check on the `slot_id` field
976/// - again, users needing to be aware of some hidden logic/rule == bad.
977/// ```compile_fail
978/// // (note: rust 2024 required for let chains)
979/// let flex_slot_maybe = flex_slots
980/// .get(some_index)
981/// .map(|x| {
982/// if let Some(slot_attrs) = x
983/// && slot_attrs.slot_type == SlotType::Flex
984/// && slot_attrs.slot_id <= 128 // our "ignore recording buffers" rule
985/// { Some(possible_flex_slot) }
986/// } else {
987/// None
988/// }
989/// });
990///
991/// let actual_flex_slot = flex_slot_maybe.ok_or(SomeErr)?;
992/// ```
993///
994/// ## third example of the pain
995/// - can't do this with raw data -- the underlying arrays are different sizes so they are different
996/// types!
997/// - [`as_array`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_array) will return
998/// `None` for our flex slots!
999///
1000/// > `pub const fn as_array<const N: usize>(&self) -> Option<&[T; N]>`
1001///
1002/// > If `N` is not exactly equal to the length of `self`, then this method returns `None`.
1003/// ```compile_fail
1004/// fn get_slot_attrs_by_type(
1005/// project_file: &ProjectFile,
1006/// slot_type: SlotType,
1007/// ) -> Option<&[Option<SlotAttributes>; 128]> {
1008/// match slot_type {
1009/// SlotType::Static => project_file.slots.static_slots.as_array(),
1010/// SlotType::Flex => project_file.slots.flex_slots.as_array(),
1011/// }
1012/// }
1013///
1014/// // always `None` due to the size issue
1015/// assert_eq!(get_slot_attrs_for_type(&file, SlotType::Flex), None)
1016/// ```
1017/// ## possible solutions to the pain?
1018///
1019/// one possible solution is making `get_slot_attrs_by_type` accept a generic size argument ...
1020/// ```compile_fail
1021/// fn get_slot_attrs_by_type<const N: usize>(
1022/// project_file: &ProjectFile,
1023/// slot_type: SlotType,
1024/// ) -> &[Option<SlotAttributes>; N];
1025/// ```
1026///
1027/// ... but this creates additional problems downstream
1028/// -- you need to create different types for different slot types with different lengths.
1029/// you then end up having to recreate this library's "Slots" array/collection data type(s) for your
1030/// own library and maintain them!
1031/// not ideal for a library!
1032/// our types are supposed to be re-usable!
1033///
1034/// another solution is just to give up!
1035/// stick everything in a `Vec` with cloned data!
1036/// forget references to arrays in the original file!
1037/// ... right?
1038///
1039/// ```compile_fail
1040/// fn get_slot_attrs_by_type(
1041/// project_file: &ProjectFile,
1042/// slot_type: SlotType,
1043/// ) -> Vec<Option<SlotAttributes>> {
1044/// let mut slots = vec![];
1045///
1046/// match slot_type {
1047/// SlotType::Static => for slot in project_file.slots.static_slots { slots.push(slot.clone()) },
1048/// SlotType::Flex => for slot in project_file.slots.flex_slots { slots.push(slot.clone()) },
1049/// };
1050///
1051/// slots
1052/// }
1053/// ```
1054///
1055/// but this means we can add more than 128 (or 136) slots to the `Vec` ...
1056/// either we check each time we push to this `Vec` that we haven't exceeded the size limit
1057/// (we're manually tracking size again!)
1058/// or we make this function generic -- which means tracking size in types downstream again!
1059/// oh, and because we have to `clone()` to escape the shared reference
1060/// -- we can have multiple owned versions of slots data which we can modify independently of each
1061/// other!
1062///
1063/// great!
1064/// now we can absolutely and completely bork someone's project files for sure!
1065///
1066/// ## the real solution to the pain
1067///
1068/// split recording buffers out separately with a different type -- [`RecordingBufferSlots<T>`] --
1069/// when we parse [`ProjectFile`][pf]s ...
1070///
1071/// so that's what happens now.
1072/// See the [`Slots<T>`] for more information.
1073///
1074/// [pf]: crate::types::ProjectFile
1075#[derive(Clone, PartialEq, Debug, Hash, PartialOrd, Ord, Eq)]
1076pub struct PlaybackSlots<T>(Box<[T; 128]>);
1077
1078generic_newtype_id_lookups!(PlaybackSlots, PlaybackSlotId);
1079generic_newtype_asref!(PlaybackSlots);
1080generic_newtype_asmut!(PlaybackSlots);
1081generic_newtype_deref!(PlaybackSlots, 128);
1082generic_newtype_deref_mut!(PlaybackSlots);
1083
1084impl<T: Defaults<Box<[T; 128]>> + Default> Default for PlaybackSlots<T> {
1085 fn default() -> Self {
1086 Self(T::defaults())
1087 }
1088}
1089
1090impl<T> PlaybackSlots<T> {
1091 pub fn new(value: [T; 128]) -> Self {
1092 Self(Box::new(value))
1093 }
1094
1095 pub fn new_boxed(value: Box<[T; 128]>) -> Self {
1096 Self(value)
1097 }
1098}
1099
1100impl PlaybackSlots<Option<ActiveSlot<SlotAttributes, SlotMarkers>>> {
1101 /// Insert or replace the slot in the collection based on the `slot_id` field in the provided
1102 /// [`ActiveSlot<SlotAttributes, SlotMarkers>`] instance.
1103 /// Will return
1104 /// - the element of the [`PlaybackSlots`] which is replaced
1105 /// - `None` if the slot is inserted into an empty slot
1106 /// - `None` if the `slot_id` field value is invalid
1107 pub fn insert_or_replace_by_slot_id_field(
1108 &mut self,
1109 value: ActiveSlot<SlotAttributes, SlotMarkers>,
1110 ) -> Option<ActiveSlot<SlotAttributes, SlotMarkers>> {
1111 if let Some(slot_id) = PlaybackSlotId::from_id(value.attrs_ref().slot_id) {
1112 let to_replace = self.id_mut(&slot_id);
1113 let old = to_replace.clone();
1114 *to_replace = Some(value);
1115 old
1116 } else {
1117 None
1118 }
1119 }
1120
1121 pub fn take_by_id(
1122 &mut self,
1123 id: &PlaybackSlotId,
1124 ) -> Option<ActiveSlot<SlotAttributes, SlotMarkers>> {
1125 if let Some(slot) = self.get_mut(id.as_index()) {
1126 slot.take()
1127 } else {
1128 None
1129 }
1130 }
1131}
1132
1133impl Default for PlaybackSlots<Option<ActiveSlot<SlotAttributes, SlotMarkers>>> {
1134 fn default() -> Self {
1135 Self(Box::new(from_fn(|_| None)))
1136 }
1137}
1138
1139impl<'de, T: Deserialize<'de>> Deserialize<'de> for PlaybackSlots<T> {
1140 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1141 where
1142 D: Deserializer<'de>,
1143 {
1144 Ok(Self(Box::new(<[T; 128] as BigArray<T>>::deserialize(
1145 deserializer,
1146 )?)))
1147 }
1148}
1149
1150impl<T: Serialize> Serialize for PlaybackSlots<T> {
1151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1152 where
1153 S: Serializer,
1154 {
1155 <[T; 128] as BigArray<T>>::serialize(&self.0, serializer)
1156 }
1157}
1158
1159/// Generic collection for data related to Recording Buffers.
1160/// Used in the [`Slots`] type.
1161///
1162/// Recording buffers are always 8x slots -- we could re-use the [`Tracks`] generic collection
1163/// type instead of creating a special case, but it feels strange to me to use [`TrackId`]
1164/// to access slot data.
1165///
1166/// It is simple enough to convert from a [`TrackId`] to a [`RecBufId`]
1167/// ```
1168/// use ot_tools_io::identifiers::{TrackId, RecBufId};
1169/// # use ot_tools_io::OtToolsIoError;
1170/// # fn main() -> Result<(), OtToolsIoError> {
1171/// RecBufId::try_from(TrackId::One as u8)?;
1172/// # Ok(()) }
1173/// ```
1174///
1175/// # `Copy` (no `Box`)
1176///
1177/// This generic collection helper does not use `Box` and can be `Copy`.
1178///
1179/// # Example: Using in your own code with custom inner types
1180///
1181/// ```
1182/// use ot_tools_io::types::RecordingBufferSlots;
1183/// use ot_tools_io::Defaults;
1184/// use ot_tools_io_derive::ArrayDefaults;
1185/// use std::array::from_fn;
1186///
1187/// #[derive(Debug, PartialEq, Default, ArrayDefaults)]
1188/// pub struct SomeNumber(u8);
1189///
1190/// fn main() -> Result<(), ()> {
1191/// let mut my_recbufs = RecordingBufferSlots::<SomeNumber>::default();
1192/// let first = my_recbufs.first_mut().ok_or(())?;
1193/// *first = SomeNumber(100);
1194///
1195/// // first one changed
1196/// assert_eq!(my_recbufs[0], SomeNumber(100));
1197/// // rest are default
1198/// assert_eq!(my_recbufs[1], SomeNumber(0));
1199///
1200/// // correct length
1201/// assert_eq!(my_recbufs.len(), 8);
1202///
1203/// Ok(())
1204/// }
1205/// ```
1206#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1207pub struct RecordingBufferSlots<T>([T; 8]);
1208
1209generic_newtype_id_lookups!(RecordingBufferSlots, RecBufId);
1210generic_newtype_asref!(RecordingBufferSlots);
1211generic_newtype_asmut!(RecordingBufferSlots);
1212generic_newtype_deref!(RecordingBufferSlots, 8);
1213generic_newtype_deref_mut!(RecordingBufferSlots);
1214generic_newtype_index!(RecordingBufferSlots);
1215generic_newtype_index_mut!(RecordingBufferSlots);
1216
1217impl<T: Defaults<[T; 8]> + Default> Default for RecordingBufferSlots<T> {
1218 fn default() -> Self {
1219 Self(T::defaults())
1220 }
1221}
1222
1223impl<const N: usize, T: Default> Defaults<[Self; N]> for RecordingBufferSlots<T> {
1224 fn defaults() -> [Self; N]
1225 where
1226 Self: Default,
1227 {
1228 from_fn(|_| Self::default())
1229 }
1230}
1231
1232impl<T> RecordingBufferSlots<T> {
1233 pub fn new(value: [T; 8]) -> Self {
1234 Self(value)
1235 }
1236}
1237
1238impl<'de, T: Deserialize<'de>> Deserialize<'de> for RecordingBufferSlots<T> {
1239 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1240 where
1241 D: Deserializer<'de>,
1242 {
1243 Ok(Self(<[T; 8] as BigArray<T>>::deserialize(deserializer)?))
1244 }
1245}
1246
1247impl<T: Serialize> Serialize for RecordingBufferSlots<T> {
1248 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1249 where
1250 S: Serializer,
1251 {
1252 <[T; 8] as BigArray<T>>::serialize(&self.0, serializer)
1253 }
1254}
1255
1256/// Generic collection for all slots data -- flex, static and recording buffers.
1257///
1258/// See the explanation in [`PlaybackSlots`] for why [`RecordingBufferSlots`] are handled
1259/// differently to [`PlaybackSlots`].
1260///
1261/// # [`Serialize`] / [`Deserialize`]
1262///
1263/// The `project.*` text file(s) are parsed differently to binary `markers.*` file(s).
1264/// As a result, this type does not have a generic [`Serialize`] / [`Deserialize`] implementations.
1265/// Rather, it has TODO
1266#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1267pub struct Slots<T> {
1268 // order is on purpose -- this is the order in the MarkersFile, which will be more authoritative
1269 // regarding slot data ordering than ProjectFile.
1270 pub flex_slots: PlaybackSlots<T>,
1271 pub recording_buffers: RecordingBufferSlots<T>,
1272 pub static_slots: PlaybackSlots<T>,
1273}
1274
1275impl<T> Slots<T> {
1276 pub fn new(
1277 flex_slots: PlaybackSlots<T>,
1278 recording_buffers: RecordingBufferSlots<T>,
1279 static_slots: PlaybackSlots<T>,
1280 ) -> Self {
1281 Self {
1282 flex_slots,
1283 recording_buffers,
1284 static_slots,
1285 }
1286 }
1287}
1288
1289/// Convenience type for combining [`SlotAttributes`] and [`SlotMarkers`],
1290/// or types which have the respective [`AsRef`] trait implemented.
1291/// This essentially gives you a type representative of the Octatrack's slot editing pages
1292/// -- all markers data and all attributes data.
1293#[derive(Default, Clone, Copy, PartialEq, Debug, Hash, PartialOrd, Ord, Eq)]
1294pub struct ActiveSlot<A, M>
1295where
1296 A: AsRef<SlotAttributes>,
1297 M: AsRef<SlotMarkers>,
1298{
1299 pub attrs: A,
1300 pub marks: M,
1301}
1302
1303impl AsRef<ActiveSlot<SlotAttributes, SlotMarkers>> for ActiveSlot<SlotAttributes, SlotMarkers> {
1304 fn as_ref(&self) -> &ActiveSlot<SlotAttributes, SlotMarkers> {
1305 self
1306 }
1307}
1308
1309impl<A, M> ActiveSlot<A, M>
1310where
1311 A: AsRef<SlotAttributes>,
1312 M: AsRef<SlotMarkers>,
1313{
1314 /// Creates a new [`ActiveSlot`] instance from the two component slots data types.
1315 ///
1316 /// # `AsRef`
1317 ///
1318 /// This method accepts any input type where, for each respective argument,
1319 /// `AsRef<SlotSlotAttributes>` or `AsRef<SlotSlotMarkers>` has been implemented appropriately.
1320 /// Therefore, any type that can be `AsRef`'d into one of these types is accepted as the
1321 /// relevant argument.
1322 ///
1323 /// # [`SlotAttributes`] example
1324 ///
1325 /// ```
1326 /// use ot_tools_io::types::ActiveSlot;
1327 /// use ot_tools_io::types::SlotAttributes;
1328 /// use ot_tools_io::types::SlotMarkers;
1329 /// # use ot_tools_io::types::SlotType;
1330 ///
1331 /// // example data
1332 /// let attrs = SlotAttributes {
1333 /// slot_type: SlotType::Static,
1334 /// slot_id: 1,
1335 /// path: None,
1336 /// timestrech_mode: Default::default(),
1337 /// loop_mode: Default::default(),
1338 /// trig_quantization_mode: Default::default(),
1339 /// gain: 48,
1340 /// bpm: 2880
1341 /// };
1342 ///
1343 /// pub struct AttrsNewType(SlotAttributes);
1344 ///
1345 /// impl AsRef<SlotAttributes> for AttrsNewType {
1346 /// fn as_ref(&self) -> &SlotAttributes {
1347 /// &self.0
1348 /// }
1349 /// }
1350 ///
1351 /// ActiveSlot::new(AttrsNewType(attrs), SlotMarkers::default());
1352 /// ```
1353 ///
1354 /// # [`SlotMarkers`] example
1355 ///
1356 /// ```
1357 /// use ot_tools_io::types::SlotMarkers;
1358 /// use ot_tools_io::types::ActiveSlot;
1359 /// # use ot_tools_io::types::SlotType;
1360 /// # use ot_tools_io::types::SlotAttributes;
1361 /// #
1362 /// # let attrs = SlotAttributes {
1363 /// # slot_type: SlotType::Static,
1364 /// # slot_id: 1,
1365 /// # path: None,
1366 /// # timestrech_mode: Default::default(),
1367 /// # loop_mode: Default::default(),
1368 /// # trig_quantization_mode: Default::default(),
1369 /// # gain: 48,
1370 /// # bpm: 2880
1371 /// # };
1372 ///
1373 /// let marks = SlotMarkers::default();
1374 ///
1375 /// pub struct MarksNewType(SlotMarkers);
1376 ///
1377 /// impl AsRef<SlotMarkers> for MarksNewType {
1378 /// fn as_ref(&self) -> &SlotMarkers {
1379 /// &self.0
1380 /// }
1381 /// }
1382 ///
1383 /// // can use the type as an input here
1384 /// ActiveSlot::new(attrs, MarksNewType(marks));
1385 /// ```
1386 pub fn new(attrs: A, marks: M) -> Self {
1387 Self { attrs, marks }
1388 }
1389
1390 /// getter for attributes data
1391 pub fn attrs(self) -> A {
1392 self.attrs
1393 }
1394
1395 pub fn attrs_ref(&self) -> &A {
1396 &self.attrs
1397 }
1398
1399 /// reference getter for markers data
1400 pub fn marks(self) -> M {
1401 self.marks
1402 }
1403
1404 /// reference getter for markers data
1405 pub fn marks_ref(&self) -> &M {
1406 &self.marks
1407 }
1408}
1409
1410impl<A, M> ActiveSlot<A, M>
1411where
1412 A: AsRef<SlotAttributes> + AsMut<SlotAttributes>,
1413 M: AsRef<SlotMarkers> + AsMut<SlotMarkers>,
1414{
1415 /// mutable reference getter for attributes data
1416 pub fn attrs_mut(&mut self) -> &mut A {
1417 &mut self.attrs
1418 }
1419
1420 /// mutable reference getter for markers data
1421 pub fn marks_mut(&mut self) -> &mut M {
1422 &mut self.marks
1423 }
1424}
1425
1426// todo: ActiveSlot<A, M> -> ActiveSlot<Attrs, Marks>
1427// ... apparently From<T> -> T exists in core
1428// ... so how should this be called? do i even need to implement it?
1429
1430impl<A, M> From<ActiveSlot<A, M>> for SampleSettingsFile
1431where
1432 A: AsRef<SlotAttributes>,
1433 M: AsRef<SlotMarkers>,
1434{
1435 fn from(value: ActiveSlot<A, M>) -> Self {
1436 SampleSettingsFile::from((value.attrs_ref(), value.marks_ref()))
1437 }
1438}
1439
1440impl<A, M> TryFrom<Option<ActiveSlot<A, M>>> for SampleSettingsFile
1441where
1442 A: AsRef<SlotAttributes>,
1443 M: AsRef<SlotMarkers>,
1444{
1445 type Error = OtToolsIoError;
1446 fn try_from(value: Option<ActiveSlot<A, M>>) -> Result<Self, Self::Error> {
1447 // todo: error
1448 let value = value.ok_or(OtToolsIoError::InvalidIndex)?;
1449 Ok(SampleSettingsFile::from((
1450 value.attrs_ref(),
1451 value.marks_ref(),
1452 )))
1453 }
1454}