use std::fs;
use std::io::Write;
use std::path::Path;
extern crate hound;
pub struct OTSlice {
pub loop_point: u32,
pub start_point: u32,
pub length: u32,
}
pub struct Slicer {
pub output_folder: String,
pub output_filename: String,
pub sample_rate: u32,
pub slices: Vec<OTSlice>,
pub filelist: Vec<std::path::PathBuf>,
pub stereo: bool,
max_file_length: usize,
start_offset: u32,
pub tempo: u32,
pub data_buffer: Vec<u8>,
}
impl Slicer {
pub fn new(slices: Vec<OTSlice>, sample_rate: u32, tempo: u32) -> Self {
Self {
slices,
filelist: Vec::new(),
output_folder: "".to_string(),
output_filename: "output".to_string(),
sample_rate,
start_offset: 0,
max_file_length: 0,
stereo: false,
tempo,
data_buffer: Vec::new(),
}
}
pub fn default() -> Self {
Self {
slices: Vec::new(),
filelist: Vec::new(),
output_folder: "".to_string(),
output_filename: "output".to_string(),
sample_rate: 44100,
start_offset: 0,
max_file_length: 0,
stereo: false,
tempo: 124,
data_buffer: Vec::new(),
}
}
pub fn clear(&mut self) {
self.slices.clear();
self.start_offset = 0;
self.sample_rate = 44100;
self.tempo = 124;
}
pub fn add_file(&mut self, filepath: String) -> Result<&'static str, &'static str> {
let path = std::path::PathBuf::from(filepath.clone());
println!("Adding file to list: {}", filepath);
let spec = hound::WavSpec {
channels: 1,
sample_rate: self.sample_rate.clone(),
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
match path.is_file() {
true => {
let mut reader = hound::WavReader::open(filepath).unwrap();
if reader.spec() == spec {
let samples: Vec<i16> = reader.samples().map(|s| s.unwrap()).collect();
let total_samples = samples.len();
if total_samples > self.max_file_length {
self.max_file_length = total_samples;
}
self.filelist.push(path);
Ok("File added succesfully.")
} else {
Err("Invalid file (invalid sample rate / bit rate / channel number)")
}
}
false => Err("File not found."),
}
}
fn process_file(
&mut self,
filepath: std::path::PathBuf,
evenly_spaced: bool,
) -> Result<&'static str, Box<dyn std::error::Error>> {
println!("Processing file: {}", filepath.display());
let spec = hound::WavSpec {
channels: 1,
sample_rate: self.sample_rate,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
if self.slices.len() < 65 {
let mut reader = hound::WavReader::open(filepath)?;
let samples: Vec<i16> = reader.samples::<i16>().collect::<Result<Vec<_>, _>>()?;
let output_folder_path: &Path = self.output_folder.as_ref();
let wav_file_name = format!("{}.wav", self.output_filename);
let temp_file_path = Path::join(output_folder_path, wav_file_name);
let slice_len: u32 = match temp_file_path.is_file() {
true => {
let temp_wav_file = hound::WavWriter::append(temp_file_path)?;
self.fill_wav_file(temp_wav_file, samples, evenly_spaced)
}
false => {
let temp_wav_file = hound::WavWriter::create(temp_file_path, spec)?;
self.fill_wav_file(temp_wav_file, samples, evenly_spaced)
}
};
let new_ot_slice = OTSlice {
start_point: self.start_offset,
length: slice_len,
loop_point: slice_len,
};
self.slices.push(new_ot_slice);
self.start_offset += slice_len;
Ok("File successfully parsed.")
} else {
Err("No more slice slots available.".into())
}
}
fn fill_wav_file(
&mut self,
mut writer: hound::WavWriter<std::io::BufWriter<std::fs::File>>,
samples: Vec<i16>,
evenly_spaced: bool,
) -> u32 {
if evenly_spaced {
for &sample in samples.iter() {
writer.write_sample(sample).expect("Failed to write sample");
}
for _ in samples.len()..self.max_file_length {
writer.write_sample(0).expect("Failed to write sample");
}
} else {
for &sample in samples.iter() {
writer.write_sample(sample).expect("Failed to write sample");
}
}
writer.finalize().expect("Failed to finalize WAV");
samples.len() as u32
}
pub fn generate_ot_file(&mut self, evenly_spaced: bool) -> Result<&'static str, &'static str> {
println!("Generating Octatrack files...");
let output_folder_path: &Path = self.output_folder.as_ref();
let mut wav_file_name: String = self.output_filename.clone();
if !self.filelist.is_empty() {
wav_file_name.push_str(".wav");
let final_wav_file = Path::join(output_folder_path, wav_file_name);
if final_wav_file.is_file() {
println!("Removing existing wav file: {}", final_wav_file.display());
fs::remove_file(final_wav_file).unwrap();
}
self.filelist.reverse();
for _ in 0..self.filelist.len() {
let file_path: std::path::PathBuf = self.filelist.pop().unwrap();
self.process_file(file_path, evenly_spaced).unwrap();
}
}
self.data_buffer = vec![
0x46, 0x4F, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x44, 0x50, 0x53, 0x31, 0x53, 0x4D,
0x50, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
];
let tempo: u32 = self.tempo * 6 * 4;
let total_samples: u32 = self.slices.iter().map(|x| x.length).sum();
println!("Total samples: {}", total_samples);
let bars_mult: f32 = (124.0 * total_samples as f32) / (self.sample_rate * 60) as f32 + 0.5;
let bars: u32 = bars_mult as u32 * 25;
self.push_u32(tempo); self.push_u32(bars.clone()); self.push_u32(bars.clone()); self.push_u32(0); self.push_u32(0); self.push_u16(48); self.data_buffer.push(255); self.push_u32(0); self.push_u32(total_samples.clone()); self.push_u32(0);
for i in 0..64 {
if i < self.slices.len() {
let start = self.slices[i].start_point;
let len = self.slices[i].start_point + self.slices[i].length;
println!("Adding slice - Start: {} - Length: {}", start, len);
self.push_u32(start);
self.push_u32(len);
self.push_u32(self.slices[i].loop_point);
} else {
self.push_u32(0);
self.push_u32(0);
self.push_u32(0);
}
}
self.push_u32(self.slices.len() as u32);
println!("Number of slices: {}", self.slices.len());
let mut checksum: u16 = 0;
let len = self.data_buffer.len();
for i in 16..len {
checksum += self.data_buffer[i] as u16;
}
self.push_u16(checksum);
let output_folder_path: &Path = self.output_folder.as_ref();
let mut ot_file_name: String = self.output_filename.clone();
ot_file_name.push_str(".ot");
println!("\nGenerating Octatrack .ot file: {}", ot_file_name);
let ot_file_path = Path::join(output_folder_path, ot_file_name.clone());
if ot_file_path.is_file() {
fs::remove_file(ot_file_path.clone()).unwrap();
};
let mut buffer = fs::File::create(ot_file_path).unwrap();
buffer.write_all(&self.data_buffer).unwrap();
println!("Finished writing Octatrack .ot file.");
self.clear();
Ok("Temporary WAV file renamed succesfully.")
}
fn push_u32(&mut self, num: u32) {
let array = num.to_le_bytes();
for i in 0..4 {
self.data_buffer.push(array[3 - i]);
}
}
fn push_u16(&mut self, num: u16) {
let array = num.to_le_bytes();
self.data_buffer.push(array[1]);
self.data_buffer.push(array[0]);
}
}