use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use bincode::error::DecodeError;
use serde::Deserialize;
use serde_big_array::BigArray;
use crate::prelude::*;
use super::serde_helper::deserialize_string_12;
use super::serde_helper::deserialize_string_26;
use super::serde_helper::deserialize_string_4;
#[derive(Deserialize, Debug)]
#[repr(C)]
pub struct ItInstrumentHeaderPre2 {
#[serde(deserialize_with = "deserialize_string_4")]
pub id: String,
#[serde(deserialize_with = "deserialize_string_12")]
pub dos_filename: String,
pub reserved1: u8,
pub flags: u8,
pub loop_start: u8,
pub loop_end: u8,
pub sustain_loop_start: u8,
pub sustain_loop_end: u8,
pub reserved2: u16,
pub fadeout: u16,
pub nna: u8,
pub dnc: u8,
pub tracker_version: u16,
pub number_of_samples: u8,
pub reserved3: u8,
#[serde(deserialize_with = "deserialize_string_26")]
pub instrument_name: String,
pub reserved4: [u8; 6],
#[serde(with = "BigArray")]
pub note_sample_keyboard_table: [(u8, u8); 120],
}
impl ItInstrumentHeaderPre2 {
pub fn is_it_instrument(&self) -> bool {
self.id == "IMPI"
}
}
#[derive(Deserialize, Debug)]
#[repr(C)]
pub struct ItEnvelopePre2 {
#[serde(with = "BigArray")]
pub envelope: [u8; 200],
pub node_points: [(u8, u8); 25],
}
impl ItEnvelopePre2 {
pub fn to_envelope(&self) -> Vec<EnvelopePoint> {
let mut points = Vec::new();
for (tick, magnitude) in self.node_points.iter() {
if *tick == 0 && *magnitude == 0 && !points.is_empty() {
break;
}
points.push(EnvelopePoint {
frame: *tick as usize,
value: (*magnitude as f32) / 64.0,
});
}
points.sort_by_key(|a| a.frame);
points
}
}
#[derive(Deserialize, Debug)]
#[repr(C)]
pub struct ItInstrumentHeaderPost2 {
#[serde(deserialize_with = "deserialize_string_4")]
pub id: String,
#[serde(deserialize_with = "deserialize_string_12")]
pub dos_filename: String,
pub reserved1: u8,
pub nna: u8,
pub duplicate_check_type: u8,
pub duplicate_check_action: u8,
pub fadeout: i16,
pub pitch_pan_separation: i8,
pub pitch_pan_center: u8,
pub global_volume: u8,
pub default_pan: u8,
pub random_volume_variation: u8,
pub random_pan_variation: u8,
pub tracker_version: u16,
pub num_samples: u8,
pub reserved2: u8,
#[serde(deserialize_with = "deserialize_string_26")]
pub instrument_name: String,
pub initial_filter_cutoff: u8,
pub initial_filter_resonance: u8,
pub midi_channel: u8,
pub midi_program: u8,
pub midi_bank: u16,
#[serde(with = "BigArray")]
pub note_sample_keyboard_table: [(u8, u8); 120],
}
impl ItInstrumentHeaderPost2 {
pub fn is_it_instrument(&self) -> bool {
self.id == "IMPI"
}
}
#[derive(Deserialize, Debug, Default)]
#[repr(C)]
pub struct ItEnvelopePost2 {
pub flags: u8,
pub node_count: u8,
pub loop_start: u8,
pub loop_end: u8,
pub sustain_loop_start: u8,
pub sustain_loop_end: u8,
pub node_points: [(u8, u16); 25],
}
impl ItEnvelopePost2 {
pub fn to_envelope(&self) -> Vec<EnvelopePoint> {
let mut points = Vec::new();
let count = (self.node_count as usize).min(25);
for (value, tick) in self.node_points[..count].iter() {
points.push(EnvelopePoint {
frame: *tick as usize,
value: (*value as f32) / 64.0,
});
}
points.sort_by_key(|a| a.frame);
points
}
pub fn to_envelope_struct(&self) -> Envelope {
Envelope {
enabled: self.flags & 0b0000_0001 != 0,
point: self.to_envelope(),
sustain_enabled: self.flags & 0b0000_0100 != 0,
sustain_start_point: self.sustain_loop_start as usize,
sustain_end_point: self.sustain_loop_end as usize,
loop_enabled: self.flags & 0b0000_0010 != 0,
loop_start_point: self.loop_start as usize,
loop_end_point: self.loop_end as usize,
}
}
}
#[derive(Deserialize, Debug)]
pub struct ItInstrumentPre2 {
pub instr: ItInstrumentHeaderPre2,
pub volume_envelope: ItEnvelopePre2,
}
impl ItInstrumentPre2 {
pub fn is_it_instrument(&self) -> bool {
self.instr.is_it_instrument()
}
}
#[derive(Deserialize, Debug)]
pub struct ItInstrumentPost2 {
pub instr: ItInstrumentHeaderPost2,
pub volume_envelope: ItEnvelopePost2,
pub panning_envelope: ItEnvelopePost2,
pub pitch_envelope: ItEnvelopePost2,
}
impl ItInstrumentPost2 {
pub fn is_it_instrument(&self) -> bool {
self.instr.is_it_instrument()
}
}
#[derive(Deserialize, Debug)]
pub enum ItInstrument {
Pre2(ItInstrumentPre2),
Post2(ItInstrumentPost2),
}
impl ItInstrument {
pub fn is_it_instrument(&self) -> bool {
match self {
ItInstrument::Pre2(i) => i.is_it_instrument(),
ItInstrument::Post2(i) => i.is_it_instrument(),
}
}
pub fn load_pre2(source: &[u8]) -> Result<Self, DecodeError> {
let mut data = source;
let instr_h = bincode::serde::decode_from_slice::<ItInstrumentHeaderPre2, _>(
data,
bincode::config::legacy(),
)?;
if !instr_h.0.is_it_instrument() {
return Err(DecodeError::OtherString(
"Not an IT Instrument?".to_string(),
));
}
data = &data[instr_h.1..];
let vol = bincode::serde::decode_from_slice::<ItEnvelopePre2, _>(
data,
bincode::config::legacy(),
)?;
let instr = ItInstrumentPre2 {
instr: instr_h.0,
volume_envelope: vol.0,
};
Ok(ItInstrument::Pre2(instr))
}
pub fn load_post2(source: &[u8]) -> Result<Self, DecodeError> {
let mut data = source;
let instr_h = bincode::serde::decode_from_slice::<ItInstrumentHeaderPost2, _>(
data,
bincode::config::legacy(),
)?;
if !instr_h.0.is_it_instrument() {
return Err(DecodeError::OtherString(
"Not an IT Instrument?".to_string(),
));
}
data = &data[instr_h.1..];
let vol = bincode::serde::decode_from_slice::<ItEnvelopePost2, _>(
data,
bincode::config::legacy(),
)?;
data = &data[1 + vol.1..];
let pan = bincode::serde::decode_from_slice::<ItEnvelopePost2, _>(
data,
bincode::config::legacy(),
)?;
data = &data[1 + pan.1..];
let pitch = bincode::serde::decode_from_slice::<ItEnvelopePost2, _>(
data,
bincode::config::legacy(),
)?;
let instr = ItInstrumentPost2 {
instr: instr_h.0,
volume_envelope: vol.0,
panning_envelope: pan.0,
pitch_envelope: pitch.0,
};
Ok(ItInstrument::Post2(instr))
}
pub fn prepare_instrument(&self) -> Instrument {
#[allow(unused_assignments)]
let mut name = String::new();
let mut muted = false;
let mut instr = InstrDefault::default();
match self {
ItInstrument::Pre2(source) => {
name = if !source.instr.instrument_name.is_empty() {
source.instr.instrument_name.clone()
} else {
source.instr.dos_filename.clone()
};
for (note, sample) in source.instr.note_sample_keyboard_table.iter() {
if *note < 120 && *sample != 0 {
instr.sample_for_pitch[*note as usize] = Some(*sample as usize - 1);
}
}
instr.volume_envelope = Envelope {
enabled: source.instr.flags & 0b0000_0001 != 0,
point: source.volume_envelope.to_envelope(),
sustain_enabled: source.instr.flags & 0b0000_0100 != 0,
sustain_start_point: source.instr.sustain_loop_start as usize,
sustain_end_point: source.instr.sustain_loop_end as usize,
loop_enabled: source.instr.flags & 0b0000_0010 != 0,
loop_start_point: source.instr.loop_start as usize,
loop_end_point: source.instr.loop_end as usize,
};
instr.volume_fadeout = (source.instr.fadeout as f32 / 64.0) / 512.0;
let nna = match source.instr.nna {
1 => NewNoteAction::Continue,
2 => NewNoteAction::NoteOff,
3 => NewNoteAction::NoteFadeOut,
_ => NewNoteAction::NoteCut,
};
instr.duplicate_check = DuplicateCheckType::Off(nna);
muted = source.instr.dnc == 0;
}
ItInstrument::Post2(source) => {
name = if !source.instr.instrument_name.is_empty() {
source.instr.instrument_name.clone()
} else {
source.instr.dos_filename.clone()
};
for (note, sample) in source.instr.note_sample_keyboard_table.iter() {
if *note < 120 && *sample != 0 {
instr.sample_for_pitch[*note as usize] = Some(*sample as usize - 1);
}
}
instr.volume_envelope = source.volume_envelope.to_envelope_struct();
instr.pan_envelope = source.panning_envelope.to_envelope_struct();
instr.pitch_envelope = source.pitch_envelope.to_envelope_struct();
instr.pitch_envelope_as_low_pass_filter =
source.pitch_envelope.flags & 0b1000_0000 != 0;
let nna = match source.instr.nna {
1 => NewNoteAction::Continue,
2 => NewNoteAction::NoteOff,
3 => NewNoteAction::NoteFadeOut,
_ => NewNoteAction::NoteCut,
};
let dca = match source.instr.duplicate_check_action {
1 => DuplicateCheckAction::NoteOff(nna),
2 => DuplicateCheckAction::NoteFadeOut(nna),
_ => DuplicateCheckAction::NoteCut(nna),
};
instr.duplicate_check = match source.instr.duplicate_check_type {
1 => DuplicateCheckType::Note(dca),
2 => DuplicateCheckType::Sample(dca),
3 => DuplicateCheckType::Instrument(dca),
_ => DuplicateCheckType::Off(nna),
};
instr.volume_fadeout = (source.instr.fadeout as f32 / 64.0) / 512.0;
instr.pitch_pan_center = source
.instr
.pitch_pan_center
.try_into()
.unwrap_or(Pitch::C4);
instr.pitch_pan_separation = source.instr.pitch_pan_separation as f32 / 32.0;
instr.global_volume = source.instr.global_volume as f32 / 128.0;
instr.default_pan = if source.instr.default_pan & 0b1000_0000 == 0 {
source.instr.default_pan as f32 / 64.0
} else {
0.5
};
instr.random_volume_variation = source.instr.random_volume_variation as f32 / 100.0;
instr.random_pan_variation = source.instr.random_pan_variation as f32 / 100.0;
instr.initial_filter_cutoff = source.instr.initial_filter_cutoff;
instr.initial_filter_resonance = source.instr.initial_filter_resonance;
instr.midi = InstrMidi {
muted: true,
channel: source.instr.midi_channel,
program: source.instr.midi_program as u16,
bank: source.instr.midi_bank,
bend: 0,
};
}
}
Instrument {
name,
instr_type: InstrumentType::Default(instr),
muted,
}
}
}