use crate::generics::Slices;
use crate::slices::Slice;
use crate::traits::SwapBytes;
use crate::Defaults;
use crate::{IsDefault, OtToolsIoError, SampleSettingsFile};
use ot_tools_io_derive::{
ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults, IsDefaultCheck,
};
use serde::{Deserialize, Serialize};
use std::array::from_fn;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SlotMarkersError {
#[error("invalid loop point: {value}")]
Loop { value: u32 },
#[error("invalid trim: start={start} end={end}")]
Trim { start: u32, end: u32 },
#[error("invalid slice count: {value}")]
SliceCount { value: u32 },
}
#[derive(
Copy,
Clone,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
IsDefaultCheck,
ArrayDefaults,
BoxedArrayDefaults,
)]
pub struct SlotMarkers {
pub trim_offset: u32,
pub trim_end: u32,
pub loop_point: u32,
pub slices: Slices<Slice>,
pub slice_count: u32,
}
impl SwapBytes for SlotMarkers {
fn swap_bytes(self) -> Self {
let mut slices = self.slices;
for (i, slice) in self.slices.iter().enumerate() {
slices[i] = slice.swap_bytes();
}
Self {
trim_offset: self.trim_offset.swap_bytes(),
trim_end: self.trim_end.swap_bytes(),
loop_point: self.loop_point.swap_bytes(),
slices,
slice_count: self.slice_count.swap_bytes(),
}
}
}
impl SlotMarkers {
pub fn new(
trim_start: u32,
trim_end: u32,
loop_point: Option<u32>,
slices: Option<Slices<Slice>>,
) -> Result<Self, OtToolsIoError> {
let slices = slices.unwrap_or_default();
let slice_count = slices.iter().filter(|x| !x.is_default()).count() as u32;
let new = Self {
trim_offset: trim_start,
trim_end,
loop_point: loop_point.unwrap_or(trim_start),
slices,
slice_count,
};
new.validate()?;
Ok(new)
}
pub fn validate(&self) -> Result<(), OtToolsIoError> {
if self.is_default() {
return Ok(());
}
if self.trim_offset > self.trim_end {
return Err(SlotMarkersError::Trim {
start: self.trim_offset,
end: self.trim_end,
}
.into());
}
if !crate::loop_point_is_in_trim_range(self.loop_point, self.trim_offset, self.trim_end) {
return Err(SlotMarkersError::Loop {
value: self.loop_point,
}
.into());
}
let slice_count = self.slices.iter().filter(|x| !x.is_default()).count();
if self.slice_count != slice_count as u32 {
return Err(SlotMarkersError::SliceCount {
value: self.slice_count,
}
.into());
}
for slice in self.slices {
slice.validate()?;
}
Ok(())
}
pub fn deinit(&mut self) {
self.trim_offset = 0;
self.trim_end = 0;
self.loop_point = 0;
self.slice_count = 0;
for slice in self.slices.iter_mut() {
slice.deinit();
}
}
}
impl<T> From<T> for SlotMarkers
where
T: AsRef<SampleSettingsFile>,
{
fn from(value: T) -> Self {
let value = value.as_ref();
SlotMarkers {
trim_offset: value.trim_start,
trim_end: value.trim_end,
loop_point: value.loop_start,
slices: value.slices,
slice_count: value.slices_len,
}
}
}
#[cfg(test)]
mod deinit {
use crate::markers::SlotMarkers;
use crate::{IsDefault, OtToolsIoError};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let mut x = SlotMarkers::new(100, 200, Some(150), None)?;
x.deinit();
assert!(x.is_default());
Ok(())
}
}
#[cfg(test)]
mod new {
use super::{SlotMarkers, SlotMarkersError};
use crate::generics::Slices;
use crate::slices::Slice;
use crate::OtToolsIoError;
#[test]
fn valid() -> Result<(), OtToolsIoError> {
assert_eq!(
SlotMarkers::new(0, 100, None, None)?,
SlotMarkers {
trim_offset: 0,
trim_end: 100,
loop_point: 0,
slices: Slices::<Slice>::default(),
slice_count: 0,
},
);
Ok(())
}
#[test]
fn invalid_trim_offset() -> Result<(), OtToolsIoError> {
let x = SlotMarkers::new(100, 0, None, None);
assert_eq!(
x.unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Trim { start: 100, end: 0 }).to_string()
);
Ok(())
}
#[test]
fn invalid_loop_point() -> Result<(), OtToolsIoError> {
let x = SlotMarkers::new(0, 0, Some(100), None);
assert_eq!(
x.unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Loop { value: 100 }).to_string()
);
Ok(())
}
}
#[cfg(test)]
mod validate {
use super::{SlotMarkers, SlotMarkersError};
use crate::generics::Slices;
use crate::slices::{Slice, SliceError, SLICE_LOOP_POINT_DISABLED};
use crate::OtToolsIoError;
#[test]
fn valid() -> Result<(), OtToolsIoError> {
assert!(SlotMarkers::default().validate().is_ok());
Ok(())
}
#[test]
fn invalid_trim() -> Result<(), OtToolsIoError> {
let x = SlotMarkers {
trim_offset: 100,
trim_end: 0,
..SlotMarkers::default()
};
assert_eq!(
x.validate().unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Trim { start: 100, end: 0 }).to_string()
);
Ok(())
}
#[test]
fn invalid_loop_point_disabled() -> Result<(), OtToolsIoError> {
let x = SlotMarkers {
loop_point: SLICE_LOOP_POINT_DISABLED,
..Default::default()
}
.validate();
assert_eq!(
x.unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Loop {
value: SLICE_LOOP_POINT_DISABLED
})
.to_string()
);
Ok(())
}
#[test]
fn invalid_loop_point_before_trim_range() -> Result<(), OtToolsIoError> {
let x = SlotMarkers {
loop_point: 0,
trim_offset: 100,
trim_end: 200,
..SlotMarkers::default()
};
assert_eq!(
x.validate().unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Loop { value: 0 }).to_string()
);
Ok(())
}
#[test]
fn invalid_loop_point_after_trim_range() -> Result<(), OtToolsIoError> {
let x = SlotMarkers {
loop_point: 100,
trim_end: 100,
..SlotMarkers::default()
};
assert_eq!(
x.validate().unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::Loop { value: 100 }).to_string()
);
Ok(())
}
#[test]
fn invalid_slice_count_no_slices() -> Result<(), OtToolsIoError> {
let x = SlotMarkers {
slice_count: 100,
trim_end: 1000,
..SlotMarkers::default()
};
assert_eq!(
x.validate().unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::SliceCount { value: 100 }).to_string()
);
Ok(())
}
#[test]
fn invalid_slice_count_one_slice() -> Result<(), OtToolsIoError> {
let mut slices = Slices::<Slice>::default();
slices[0] = Slice {
trim_start: 100,
trim_end: 200,
loop_start: 100,
};
let x = SlotMarkers {
trim_offset: 100,
trim_end: 200,
loop_point: 100,
slice_count: 10,
slices,
}
.validate();
assert_eq!(
x.unwrap_err().to_string(),
OtToolsIoError::Markers(SlotMarkersError::SliceCount { value: 10 }).to_string()
);
Ok(())
}
#[test]
fn invalid_slice_loop_point_oob() -> Result<(), OtToolsIoError> {
let mut slices = Slices::<Slice>::default();
slices[0] = Slice {
trim_start: 100,
trim_end: 200,
loop_start: 0,
};
let x = SlotMarkers {
trim_offset: 100,
trim_end: 200,
loop_point: 100,
slice_count: 1,
slices,
}
.validate();
assert_eq!(
x.unwrap_err().to_string(),
OtToolsIoError::Slice(SliceError::LoopPoint { value: 0 }).to_string()
);
Ok(())
}
#[test]
fn valid_slice_out_of_trim_range() -> Result<(), OtToolsIoError> {
let mut slices = Slices::<Slice>::default();
slices[0] = Slice {
trim_start: 300,
trim_end: 500,
loop_start: 300,
};
let x = SlotMarkers {
trim_offset: 100,
trim_end: 200,
loop_point: 100,
slice_count: 1,
slices,
}
.validate();
assert!(x.is_ok());
Ok(())
}
}