use crate::generics::Slices;
use crate::markers::SlotMarkers;
use crate::projects::{SlotAttributes, DEFAULT_GAIN, DEFAULT_TEMPO};
use crate::settings::{LoopMode, SlotType, TimeStretchMode, TrigQuantizationMode};
use crate::slices::Slice;
use crate::traits::SwapBytes;
use crate::{
HasChecksumField, HasFileVersionField, HasHeaderField, OctatrackFileIO, OtToolsIoError,
};
use ot_tools_io_derive::{AsMutDerive, AsRefDerive, IntegrityChecks};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;
#[cfg(test)]
mod test_utils {
use super::{
OtToolsIoError, SampleSettingsFile, SlotMarkers, SAMPLES_FILE_VERSION, SAMPLES_HEADER,
};
use crate::projects::DEFAULT_GAIN;
pub(crate) fn create_mock_new_sample_settings(
tempo: Option<u32>,
gain: Option<u16>,
) -> Result<SampleSettingsFile, OtToolsIoError> {
SampleSettingsFile::new(
SlotMarkers::default(),
gain,
tempo,
None,
None,
None,
None,
None,
)
}
pub(crate) fn mock_0_slice_test_file() -> SampleSettingsFile {
SampleSettingsFile {
header: SAMPLES_HEADER,
datatype_version: SAMPLES_FILE_VERSION,
unknown: 1,
tempo: 2281,
trim_bar_len: 800,
loop_bar_len: 800,
stretch: 2,
loop_mode: 0,
gain: DEFAULT_GAIN as u16,
quantization: 255,
trim_start: 0,
trim_end: 890839,
loop_start: 0,
slices: SlotMarkers::default().slices,
slices_len: 0,
checksum: 998,
}
}
}
#[derive(Debug, Error)]
pub enum SampleSettingsError {
#[error("invalid gain, must be u16 in range 0 <= x <= 96: {value}")]
GainOutOfBounds { value: u32 },
#[error("invalid tempo value, must be u32 in range 720 <= x <= 7200: {value}")]
TempoOutOfBounds { value: u32 },
#[error("invalid slot_id value, must be 1 to 128 for Static slot type or 136 for Flex slot type: id: {id} type: {slot_type}")]
SlotIdOutOfBounds { id: u8, slot_type: SlotType },
#[error("invalid checksum value for sample settings file: {checksum}")]
InvalidChecksum { checksum: u16 },
#[error("invalid file version value for sample settings file: {version}")]
InvalidFileVersion { version: u8 },
}
pub const SAMPLES_HEADER: [u8; 21] = [
0x46, 0x4F, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x44, 0x50, 0x53, 0x31, 0x53, 0x4D, 0x50, 0x41,
0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const SAMPLES_FILE_VERSION: u8 = 2;
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
IntegrityChecks,
)]
pub struct SampleSettingsFile {
pub header: [u8; 21],
pub datatype_version: u8,
pub unknown: u8,
pub tempo: u32,
pub trim_bar_len: u32,
pub loop_bar_len: u32,
pub stretch: u32,
pub loop_mode: u32,
pub gain: u16,
pub quantization: u8,
pub trim_start: u32,
pub trim_end: u32,
pub loop_start: u32,
pub slices: Slices<Slice>,
pub slices_len: u32,
pub checksum: u16,
}
impl SampleSettingsFile {
#[allow(clippy::too_many_arguments)]
pub fn new<M: AsRef<SlotMarkers>>(
markers: M,
gain: Option<u16>,
tempo: Option<u32>,
trim_bar_len: Option<u32>,
loop_bar_len: Option<u32>,
stretch: Option<TimeStretchMode>,
quantization: Option<TrigQuantizationMode>,
loop_mode: Option<LoopMode>,
) -> Result<Self, OtToolsIoError> {
let marks = markers.as_ref();
let mut new = Self {
trim_start: marks.trim_offset,
trim_end: marks.trim_end,
loop_start: marks.loop_point,
slices: marks.slices,
slices_len: marks.slice_count,
header: SAMPLES_HEADER,
datatype_version: SAMPLES_FILE_VERSION,
unknown: 0,
gain: gain.unwrap_or(DEFAULT_GAIN as u16),
tempo: tempo.unwrap_or(DEFAULT_TEMPO as u32),
stretch: stretch.unwrap_or_default().into(),
quantization: quantization.unwrap_or_default().into(),
loop_mode: loop_mode.unwrap_or_default().into(),
trim_bar_len: trim_bar_len.unwrap_or(0), loop_bar_len: loop_bar_len.unwrap_or(0), checksum: 0, };
new.checksum = new.calculate_checksum()?;
new.validate()?;
Ok(new)
}
pub fn validate(&self) -> Result<(), OtToolsIoError> {
#[allow(clippy::manual_range_contains)]
if self.tempo < 720 || self.tempo > 7200 {
return Err(SampleSettingsError::TempoOutOfBounds { value: self.tempo }.into());
}
if self.gain > 96 {
return Err(SampleSettingsError::GainOutOfBounds {
value: self.gain as u32,
}
.into());
}
SlotMarkers::from(self).validate()?;
LoopMode::try_from(self.loop_mode)?;
TimeStretchMode::try_from(self.stretch)?;
TrigQuantizationMode::try_from(self.quantization)?;
if !self.check_checksum()? {
return Err(SampleSettingsError::InvalidChecksum {
checksum: self.checksum,
}
.into());
};
if !self.check_header()? {
return Err(OtToolsIoError::FileHeader);
};
if !self.check_file_version()? {
return Err(SampleSettingsError::InvalidFileVersion {
version: self.datatype_version,
}
.into());
};
if !self.check_checksum()? {
return Err(SampleSettingsError::InvalidChecksum {
checksum: self.checksum,
}
.into());
};
Ok(())
}
pub fn to_slot_attr(
&self,
slot_type: SlotType,
slot_id: u8,
slot_path: Option<PathBuf>,
) -> Result<SlotAttributes, OtToolsIoError> {
if slot_id == 0 {
return Err(SampleSettingsError::SlotIdOutOfBounds {
id: slot_id,
slot_type,
}
.into());
}
if slot_type == SlotType::Static && slot_id > 128 {
return Err(SampleSettingsError::SlotIdOutOfBounds {
id: slot_id,
slot_type,
}
.into());
}
if slot_type == SlotType::Flex && slot_id > 136 {
return Err(SampleSettingsError::SlotIdOutOfBounds {
id: slot_id,
slot_type,
}
.into());
}
Ok(SlotAttributes {
slot_type,
slot_id,
path: slot_path,
timestrech_mode: TimeStretchMode::try_from(self.stretch)?,
loop_mode: LoopMode::try_from(self.loop_mode)?,
trig_quantization_mode: TrigQuantizationMode::try_from(self.quantization)?,
gain: self.gain as u8,
bpm: self.tempo as u16,
})
}
pub fn timestretch_mode_into_setting(&self) -> Result<TimeStretchMode, OtToolsIoError> {
TimeStretchMode::try_from(self.stretch).map_err(OtToolsIoError::SettingValue)
}
pub fn loop_mode_into_setting(&self) -> Result<LoopMode, OtToolsIoError> {
LoopMode::try_from(self.loop_mode).map_err(OtToolsIoError::SettingValue)
}
pub fn trig_quantization_into_setting(&self) -> Result<TrigQuantizationMode, OtToolsIoError> {
TrigQuantizationMode::try_from(self.loop_mode).map_err(OtToolsIoError::SettingValue)
}
}
#[cfg(test)]
mod new {
use super::test_utils::create_mock_new_sample_settings;
use super::SampleSettingsError;
use crate::OtToolsIoError;
#[test]
fn invalid_oob_temp_high() -> Result<(), OtToolsIoError> {
assert_eq!(
create_mock_new_sample_settings(Some(7201), None)
.unwrap_err()
.to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::TempoOutOfBounds { value: 7201 })
.to_string()
);
Ok(())
}
#[test]
fn invalid_oob_temp_low() -> Result<(), OtToolsIoError> {
let s_f = create_mock_new_sample_settings(Some(29), None);
assert_eq!(
s_f.unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::TempoOutOfBounds { value: 29 })
.to_string()
);
Ok(())
}
#[test]
fn invalid_oob_gain() -> Result<(), OtToolsIoError> {
assert_eq!(
create_mock_new_sample_settings(None, Some(97))
.unwrap_err()
.to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::GainOutOfBounds { value: 97 })
.to_string()
);
Ok(())
}
}
#[cfg(test)]
mod validate {
use super::test_utils::create_mock_new_sample_settings;
use super::SampleSettingsError;
use crate::settings::InvalidValueError;
use crate::traits::HasChecksumField;
use crate::OtToolsIoError;
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let s_f = create_mock_new_sample_settings(None, None)?;
assert!(s_f.validate().is_ok());
Ok(())
}
#[test]
fn err_stretch() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.stretch = 255;
s_f.checksum = s_f.calculate_checksum()?;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::SettingValue(InvalidValueError::TimeStretchMode).to_string()
);
Ok(())
}
#[test]
fn err_loop() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.loop_mode = 255;
s_f.checksum = s_f.calculate_checksum()?;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::SettingValue(InvalidValueError::LoopMode).to_string()
);
Ok(())
}
#[test]
fn err_quant() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.quantization = 250; s_f.checksum = s_f.calculate_checksum()?;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::SettingValue(InvalidValueError::TrigQuantizationMode).to_string()
);
Ok(())
}
#[test]
fn err_chksum() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.checksum = 1;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::InvalidChecksum {
checksum: s_f.checksum
})
.to_string(),
);
Ok(())
}
#[test]
fn err_header() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.header[0] = 255;
s_f.checksum = s_f.calculate_checksum()?;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::FileHeader.to_string(),
);
Ok(())
}
#[test]
fn err_file_version() -> Result<(), OtToolsIoError> {
let mut s_f = create_mock_new_sample_settings(None, None)?;
s_f.datatype_version = 255;
s_f.checksum = s_f.calculate_checksum()?;
assert_eq!(
s_f.validate().unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::InvalidFileVersion {
version: s_f.datatype_version
})
.to_string(),
);
Ok(())
}
}
#[cfg(test)]
mod to_slot_attrs {
use super::test_utils::create_mock_new_sample_settings;
use crate::projects::SlotAttributes;
use crate::samples::SampleSettingsError;
use crate::settings::SlotType;
use crate::OtToolsIoError;
use std::path::PathBuf;
#[test]
fn basic_static() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs = settings.to_slot_attr(
SlotType::Static,
1,
Some(PathBuf::from("../AUDIO/path.wav")),
)?;
assert_eq!(
attrs,
SlotAttributes {
slot_type: SlotType::Static,
slot_id: 1,
path: Some(PathBuf::from("../AUDIO/path.wav")),
timestrech_mode: Default::default(),
loop_mode: Default::default(),
trig_quantization_mode: Default::default(),
gain: 48,
bpm: 2880,
},
);
Ok(())
}
#[test]
fn invalid_static_slot_id_0() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs = settings.to_slot_attr(
SlotType::Static,
0,
Some(PathBuf::from("../AUDIO/path.wav")),
);
assert_eq!(
attrs.unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::SlotIdOutOfBounds {
id: 0,
slot_type: SlotType::Static
})
.to_string(),
);
Ok(())
}
#[test]
fn invalid_static_slot_id_129() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs = settings.to_slot_attr(
SlotType::Static,
129,
Some(PathBuf::from("../AUDIO/path.wav")),
);
assert_eq!(
attrs.unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::SlotIdOutOfBounds {
id: 129,
slot_type: SlotType::Static
})
.to_string(),
);
Ok(())
}
#[test]
fn basic_flex() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs =
settings.to_slot_attr(SlotType::Flex, 1, Some(PathBuf::from("../AUDIO/path.wav")))?;
assert_eq!(
attrs,
SlotAttributes {
slot_type: SlotType::Flex,
slot_id: 1,
path: Some(PathBuf::from("../AUDIO/path.wav")),
timestrech_mode: Default::default(),
loop_mode: Default::default(),
trig_quantization_mode: Default::default(),
gain: 48,
bpm: 2880,
},
);
Ok(())
}
#[test]
fn invalid_flex_slot_id_0() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs =
settings.to_slot_attr(SlotType::Flex, 0, Some(PathBuf::from("../AUDIO/path.wav")));
assert_eq!(
attrs.unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::SlotIdOutOfBounds {
id: 0,
slot_type: SlotType::Flex
})
.to_string(),
);
Ok(())
}
#[test]
fn invalid_flex_slot_id_137() -> Result<(), OtToolsIoError> {
let settings = create_mock_new_sample_settings(None, None)?;
let attrs = settings.to_slot_attr(
SlotType::Flex,
137,
Some(PathBuf::from("../AUDIO/path.wav")),
);
assert_eq!(
attrs.unwrap_err().to_string(),
OtToolsIoError::SampleSettings(SampleSettingsError::SlotIdOutOfBounds {
id: 137,
slot_type: SlotType::Flex
})
.to_string(),
);
Ok(())
}
}
impl SwapBytes for SampleSettingsFile {
fn swap_bytes(self) -> Self {
let mut bswapped_slices = self.slices;
for (i, slice) in self.slices.iter().enumerate() {
bswapped_slices[i] = slice.swap_bytes();
}
Self {
header: SAMPLES_HEADER,
datatype_version: SAMPLES_FILE_VERSION,
unknown: self.unknown,
tempo: self.tempo.swap_bytes(),
trim_bar_len: self.trim_bar_len.swap_bytes(),
loop_bar_len: self.loop_bar_len.swap_bytes(),
stretch: self.stretch.swap_bytes(),
loop_mode: self.loop_mode.swap_bytes(),
gain: self.gain.swap_bytes(),
quantization: self.quantization.swap_bytes(),
trim_start: self.trim_start.swap_bytes(),
trim_end: self.trim_end.swap_bytes(),
loop_start: self.loop_start.swap_bytes(),
slices: bswapped_slices,
slices_len: self.slices_len.swap_bytes(),
checksum: self.checksum.swap_bytes(),
}
}
}
impl OctatrackFileIO for SampleSettingsFile {
fn to_bytes(&self) -> Result<Vec<u8>, OtToolsIoError> {
#[allow(clippy::clone_on_copy)]
let mut chkd = self.clone();
chkd.checksum = self.calculate_checksum()?;
let encoded = if cfg!(target_endian = "little") {
bincode::serialize(&chkd.swap_bytes())?
} else {
bincode::serialize(&chkd)?
};
Ok(encoded)
}
fn from_bytes(bytes: &[u8]) -> Result<Self, OtToolsIoError> {
let decoded: Self = bincode::deserialize(bytes)?;
#[allow(clippy::clone_on_copy)]
let mut bswapd = decoded.clone();
if cfg!(target_endian = "little") {
bswapd = decoded.swap_bytes();
}
Ok(bswapd)
}
}
#[cfg(test)]
mod from_bytes {
use crate::read_bin_file;
use crate::samples::test_utils::mock_0_slice_test_file;
use crate::test_utils::get_samples_dirpath;
use crate::{OctatrackFileIO, OtToolsIoError, SampleSettingsFile};
#[test]
fn valid() -> Result<(), OtToolsIoError> {
let path = get_samples_dirpath().join("checksum").join("0slices.ot");
let bytes = read_bin_file(&path)?;
let s = SampleSettingsFile::from_bytes(&bytes)?;
assert_eq!(s, mock_0_slice_test_file());
Ok(())
}
}
#[cfg(test)]
mod to_bytes {
use crate::read_bin_file;
use crate::samples::test_utils::mock_0_slice_test_file;
use crate::test_utils::get_samples_dirpath;
use crate::{OctatrackFileIO, OtToolsIoError};
#[test]
fn valid() -> Result<(), OtToolsIoError> {
let path = get_samples_dirpath().join("checksum").join("0slices.ot");
let bytes = read_bin_file(&path)?;
let b = mock_0_slice_test_file().to_bytes()?;
assert_eq!(b, bytes);
Ok(())
}
}
impl HasChecksumField for SampleSettingsFile {
fn calculate_checksum(&self) -> Result<u16, OtToolsIoError> {
let bytes = bincode::serialize(&self)?;
let checksum_bytes = &bytes[16..bytes.len() - 2];
let chk: u32 = checksum_bytes
.iter()
.map(|x| *x as u32)
.sum::<u32>()
.rem_euclid(u16::MAX as u32 + 1);
Ok(chk as u16)
}
fn update_checksum(&mut self) -> Result<(), OtToolsIoError> {
self.checksum = self.calculate_checksum()?;
Ok(())
}
fn check_checksum(&self) -> Result<bool, OtToolsIoError> {
Ok(self.checksum == self.calculate_checksum()?)
}
}
#[cfg(test)]
mod checksum_field {
use super::test_utils::create_mock_new_sample_settings;
use crate::{HasChecksumField, OtToolsIoError};
#[test]
fn valid() -> Result<(), OtToolsIoError> {
let mut x = create_mock_new_sample_settings(None, None)?;
x.checksum = x.calculate_checksum()?;
assert!(x.check_checksum()?);
Ok(())
}
#[test]
fn invalid() -> Result<(), OtToolsIoError> {
let mut x = create_mock_new_sample_settings(None, None)?;
x.checksum = x.calculate_checksum()?;
x.checksum = 0;
assert!(!x.check_checksum()?);
Ok(())
}
mod files {
use crate::test_utils::get_samples_dirpath;
use crate::{HasChecksumField, OctatrackFileIO, OtToolsIoError, SampleSettingsFile};
fn helper(test_name: String) -> Result<(u16, u16), OtToolsIoError> {
let mut src_path = get_samples_dirpath().join("checksum").join(&test_name);
src_path.set_extension("ot");
let valid = SampleSettingsFile::from_data_file(&src_path)?;
#[allow(clippy::clone_on_copy)] let mut x = valid.clone();
x.checksum = 0;
Ok((x.calculate_checksum()?, valid.checksum))
}
#[test]
fn zero_slices_trig_quant_one_step() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("0slices-tq1step".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn zero_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("0slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn one_slice() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("1slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn two_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("2slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn four_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("4slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn eight_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("8slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn sixteen_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("16slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn thirty_two_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("32slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn forty_eight_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("48slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn sixty_two_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("62slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn sixty_three_slices() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("63slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
#[test]
fn sixty_four_slices_correct() -> Result<(), OtToolsIoError> {
let (test, valid) = helper("64slices".to_string())?;
assert_eq!(test, valid);
Ok(())
}
}
}
impl HasHeaderField for SampleSettingsFile {
fn check_header(&self) -> Result<bool, OtToolsIoError> {
Ok(self.header == SAMPLES_HEADER)
}
}
#[cfg(test)]
mod header_field {
use super::test_utils::create_mock_new_sample_settings;
use crate::{HasHeaderField, OtToolsIoError};
#[test]
fn valid() -> Result<(), OtToolsIoError> {
assert!(create_mock_new_sample_settings(None, None)?.check_header()?);
Ok(())
}
#[test]
fn invalid() -> Result<(), OtToolsIoError> {
let mut mutated = create_mock_new_sample_settings(None, None)?;
mutated.header[0] = 0x00;
mutated.header[20] = 111;
assert!(!mutated.check_header()?);
Ok(())
}
}
impl HasFileVersionField for SampleSettingsFile {
fn check_file_version(&self) -> Result<bool, OtToolsIoError> {
Ok(self.datatype_version == SAMPLES_FILE_VERSION)
}
}
#[cfg(test)]
mod file_version_field {
use super::test_utils::create_mock_new_sample_settings;
use crate::{HasFileVersionField, OtToolsIoError};
#[test]
fn valid() -> Result<(), OtToolsIoError> {
assert!(create_mock_new_sample_settings(None, None)?.check_file_version()?);
Ok(())
}
#[test]
fn invalid() -> Result<(), OtToolsIoError> {
let mut mutated = create_mock_new_sample_settings(None, None)?;
mutated.datatype_version = 0;
assert!(!mutated.check_file_version()?);
Ok(())
}
}
impl<A, M> From<(A, M)> for SampleSettingsFile
where
A: AsRef<SlotAttributes>,
M: AsRef<SlotMarkers>,
{
fn from(value: (A, M)) -> Self {
let attrs = value.0.as_ref();
let markers = value.1.as_ref();
SampleSettingsFile {
header: SAMPLES_HEADER,
datatype_version: SAMPLES_FILE_VERSION,
unknown: 0,
tempo: attrs.bpm as u32,
trim_bar_len: 0,
loop_bar_len: 0,
stretch: attrs.timestrech_mode.into(),
loop_mode: attrs.loop_mode.into(),
gain: attrs.gain as u16,
quantization: attrs.trig_quantization_mode.into(),
trim_start: markers.trim_offset,
trim_end: markers.trim_end,
loop_start: markers.loop_point,
slices: markers.slices,
slices_len: markers.slice_count,
checksum: 0,
}
}
}
#[cfg(test)]
mod sample_settings_from {
use crate::generics::Slots;
use crate::projects::SlotAttributes;
use crate::MarkersFile;
use crate::OtToolsIoError;
use crate::SampleSettingsFile;
#[test]
fn from_owned_valid() -> Result<(), OtToolsIoError> {
let slots = Slots::<Option<SlotAttributes>>::default();
let slot = slots.recording_buffers[0].clone();
let markers = MarkersFile::default().flex_slots[0];
let _ = SampleSettingsFile::from((slot.unwrap(), markers));
Ok(())
}
#[test]
fn from_borrowed_valid() -> Result<(), OtToolsIoError> {
let slots = Slots::<Option<SlotAttributes>>::default();
let slot = slots.recording_buffers[0].clone();
let markers = MarkersFile::default().flex_slots[0];
let _ = SampleSettingsFile::from((&slot.unwrap(), &markers));
Ok(())
}
}