use alloc::string::String;
use alloc::{vec, vec::Vec};
use super::helper::*;
use crate::core::fixed::units::{ChannelVolume, Finetune, Panning, Volume};
use crate::core::instrument::{Instrument, InstrumentType};
use crate::core::sample::{LoopType, Sample, SampleDataType};
use crate::tracker::import::bin_reader::{
bytes_to_trimmed_string, string_to_fixed_array, write_bytes, write_i8, write_u32_le, write_u8,
BinReader, ImportError,
};
pub const XMSAMPLE_HEADER_SIZE: usize = 40;
#[derive(Default, Debug)]
pub struct XmSampleHeader {
length: u32,
loop_start: u32,
loop_length: u32,
volume: u8,
finetune: i8,
flags: u8,
panning: u8,
relative_pitch: i8,
reserved: u8,
name: String,
}
impl XmSampleHeader {
fn read(r: &mut BinReader) -> Result<Self, ImportError> {
let length = r.read_u32_le()?;
let loop_start = r.read_u32_le()?;
let loop_length = r.read_u32_le()?;
let volume = r.read_u8()?;
let finetune = r.read_i8()?;
let flags = r.read_u8()?;
let panning = r.read_u8()?;
let relative_pitch = r.read_i8()?;
let reserved = r.read_u8()?;
let name_bytes: [u8; 22] = r.read_array()?;
Ok(Self {
length,
loop_start,
loop_length,
volume,
finetune,
flags,
panning,
relative_pitch,
reserved,
name: bytes_to_trimmed_string(&name_bytes),
})
}
fn write(&self, out: &mut Vec<u8>) {
write_u32_le(out, self.length);
write_u32_le(out, self.loop_start);
write_u32_le(out, self.loop_length);
write_u8(out, self.volume);
write_i8(out, self.finetune);
write_u8(out, self.flags);
write_u8(out, self.panning);
write_i8(out, self.relative_pitch);
write_u8(out, self.reserved);
write_bytes(out, &string_to_fixed_array::<22>(&self.name));
}
}
#[derive(Debug, Default)]
pub struct XmSample {
header: XmSampleHeader,
data: Option<SampleDataType>,
}
impl XmSample {
pub fn load(data: &[u8]) -> Result<(&[u8], XmSample), ImportError> {
let mut r = BinReader::new(data);
let header = XmSampleHeader::read(&mut r)?;
let xms = XmSample { header, data: None };
Ok((r.tail(), xms))
}
pub fn set_name(&mut self, name: String) {
self.header.name = name;
}
pub fn add_sample<'a>(&mut self, data: &'a [u8]) -> Result<&'a [u8], ImportError> {
let data_len: usize = self.header.length as usize;
if data.len() < data_len {
return Err(ImportError::Other("Sample data too short"));
}
let slice = &data[..data_len];
let d3 = if self.header.flags & 0b0001_0000 != 0 {
let sample = u8_slice_to_vec_u16(slice);
let sample2 = delta16_to_sample(sample);
SampleDataType::Mono16(sample2)
} else {
let sample = slice.to_vec();
let sample2 = delta8_to_sample(sample);
SampleDataType::Mono8(sample2)
};
self.data = Some(d3);
Ok(&data[data_len..])
}
pub fn save(&mut self) -> Vec<u8> {
self.header.length = match &self.data {
Some(SampleDataType::Mono8(d)) => d.len() as u32,
Some(SampleDataType::Mono16(d)) => {
self.header.flags |= 0b0001_0000;
2 * d.len() as u32
}
_ => 0,
};
let mut out = Vec::with_capacity(XMSAMPLE_HEADER_SIZE);
self.header.write(&mut out);
out
}
pub fn save_sample(&mut self) -> Vec<u8> {
match &self.data {
Some(SampleDataType::Mono8(d)) => sample8_to_delta(d),
Some(SampleDataType::Mono16(d)) => {
let d = sample16_to_delta(d);
vec_u16_to_u8_slice(d)
}
_ => vec![],
}
}
pub fn to_sample(&self) -> Sample {
let mut loop_start = self.header.loop_start;
let mut loop_length = self.header.loop_length;
if let Some(SampleDataType::Mono16(_)) = &self.data {
loop_start >>= 1;
loop_length >>= 1;
}
let sample_length = self.len();
if sample_length == 0 {
loop_start = 0;
loop_length = 0;
} else {
if loop_start >= sample_length {
loop_start = sample_length - 1;
}
if loop_length > sample_length - loop_start {
loop_length = sample_length - loop_start;
}
}
let data: SampleDataType = match &self.data {
Some(d) => d.clone(),
None => SampleDataType::Mono8(vec![]),
};
Sample {
name: self.header.name.clone(),
relative_pitch: self.header.relative_pitch,
finetune: Finetune::from_ratio(self.header.finetune as i32, 127),
volume: ChannelVolume::from_byte_64(self.header.volume),
default_note_volume: Volume::FULL,
panning: Panning::from_byte_255(self.header.panning),
loop_flag: match self.header.flags & 0b0000_0011 {
1 => LoopType::Forward,
2 => LoopType::PingPong,
3 => LoopType::PingPong,
_ => LoopType::No,
},
loop_start,
loop_length,
sustain_loop_flag: LoopType::No,
sustain_loop_start: 0,
sustain_loop_length: 0,
data: Some(data),
}
}
pub fn from_instr(i: &Instrument) -> Vec<XmSample> {
let mut output: Vec<XmSample> = vec![];
if let InstrumentType::Default(id) = &i.instr_type {
for s in id.sample.iter().flatten() {
let mut loop_start = s.loop_start;
let mut loop_length = s.loop_length;
if let Some(SampleDataType::Mono16(_)) = &s.data {
loop_start <<= 1;
loop_length <<= 1;
}
let mut xms = XmSample::default();
xms.header.length = match &s.data {
Some(SampleDataType::Mono8(d)) => d.len() as u32,
Some(SampleDataType::Mono16(d)) => 2 * d.len() as u32,
Some(SampleDataType::Stereo8(d)) => 2 * d.len() as u32,
Some(SampleDataType::Stereo16(d)) => 2 * 2 * d.len() as u32,
Some(SampleDataType::StereoFloat(d)) => 4 * 2 * d.len() as u32,
None => 0,
};
xms.header.loop_start = loop_start;
xms.header.loop_length = loop_length;
xms.header.volume = s.volume.to_byte_64();
xms.header.finetune = s.finetune.to_signed_byte(127);
xms.header.flags = s.loop_flag.into();
if let Some(SampleDataType::Mono16(_)) = &s.data {
xms.header.flags |= 0b0001_0000;
}
xms.header.panning = s.panning.to_byte_255();
xms.header.relative_pitch = s.relative_pitch;
xms.header.name = s.name.clone();
xms.data = s.data.clone();
output.push(xms);
}
}
output
}
pub fn len(&self) -> u32 {
match &self.data {
Some(SampleDataType::Mono8(d)) => d.len() as u32,
Some(SampleDataType::Mono16(d)) => d.len() as u32,
_ => 0,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}