use std::path::Path;
use std::fs;
use std::io::Write;
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
}
impl Slicer {
pub fn new () -> 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
}
}
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, &'static str> {
println!("Processing file: {}", filepath.display());
let spec = hound::WavSpec {
channels: 1,
sample_rate: self.sample_rate.clone(),
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
let result = {
if self.slices.len() < 65 {
let mut reader = hound::WavReader::open(filepath).unwrap();
let samples: Vec<i16> = reader.samples().map(|s| s.unwrap()).collect();
let output_folder_path : &Path = self.output_folder.as_ref();
let mut wav_file_name : String = self.output_filename.clone();
wav_file_name.push_str(".wav");
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).unwrap();
self.fill_wav_file(temp_wav_file, samples, evenly_spaced)
},
false => {
let temp_wav_file = hound::WavWriter::create(temp_file_path, spec).unwrap();
self.fill_wav_file(temp_wav_file, samples, evenly_spaced)
}
};
let new_ot_slice = OTSlice{start_point: self.start_offset, length: slice_len as u32, loop_point: slice_len as u32};
self.slices.push(new_ot_slice);
self.start_offset += slice_len;
Ok("File succesfully parsed.")
} else {
Err("No more slice slots available.")
}
};
result
}
fn fill_wav_file(&mut self, mut writer : hound::WavWriter<std::io::BufWriter<std::fs::File>>, samples: Vec<i16>, evenly_spaced : bool) -> u32 {
match evenly_spaced {
true => {
for i in 0..self.max_file_length {
let mut sample_value : i16 = 0;
if i < samples.len() {
sample_value = samples[i].clone()
}
writer.write_sample(sample_value).unwrap();
};
},
false => {
for i in 0..samples.len() {
writer.write_sample(samples[i].clone()).unwrap();
};
}
}
writer.finalize().unwrap();
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();
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();
}
let mut file_data : Vec<u8> = 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 mut total_samples : u32 = 0;
for i in 0..self.slices.len() {
total_samples += self.slices[i].length;
}
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;
file_data = self.push_u32(file_data, tempo);
file_data = self.push_u32(file_data, bars.clone());
file_data = self.push_u32(file_data, bars.clone());
file_data = self.push_u32(file_data, 0);
file_data = self.push_u32(file_data, 0);
file_data = self.push_u16(file_data, 48);
file_data.push(255);
file_data = self.push_u32(file_data, 0);
file_data = self.push_u32(file_data, total_samples.clone());
file_data = self.push_u32(file_data, 0);
for i in 0..64 {
if i < self.slices.len() {
file_data = self.push_u32(file_data, self.slices[i].start_point);
file_data = self.push_u32(file_data, self.slices[i].start_point + self.slices[i].length);
file_data = self.push_u32(file_data, self.slices[i].loop_point);
} else {
file_data = self.push_u32(file_data, 0);
file_data = self.push_u32(file_data, 0);
file_data = self.push_u32(file_data, 0);
}
}
file_data = self.push_u32(file_data, self.slices.len() as u32);
println!("Number of slices: {}", self.slices.len());
let mut checksum : u16 = 0;
let len = file_data.len();
for i in 16..len {
checksum += file_data[i] as u16;
}
file_data = self.push_u16(file_data, 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(&file_data).unwrap();
println!("Finished writing Octatrack .ot file.");
self.clear();
Ok("Temporary WAV file renamed succesfully.")
}
fn push_u32(&mut self, mut vector : Vec<u8>, num : u32) -> Vec<u8> {
let array = num.to_le_bytes();
for i in 0..4 {
vector.push(array[3-i]);
}
vector
}
fn push_u16(&mut self, mut vector : Vec<u8>, num : u16) -> Vec<u8> {
let array = num.to_le_bytes();
vector.push(array[1]);
vector.push(array[0]);
vector
}
}