Basic conversion working

This commit is contained in:
Jaby 2024-10-17 11:56:11 +02:00
parent c861af7f11
commit 72841b1a37
2 changed files with 150 additions and 8 deletions

View File

@ -2,20 +2,31 @@ pub mod types;
use std::io::Write;
use tool_helper::{Error, Input};
use types::MonoADPCMIterator;
use types::{LPC, MonoADPCMIterator, VAGADPCM, VAGHeader};
pub fn convert(input: Input, _output: &mut dyn Write) -> Result<(), Error> {
pub fn convert(input: Input, output: &mut dyn Write) -> Result<(), Error> {
let mut wav_file = hound::WavReader::new(input)?;
let wav_header = wav_file.spec();
validate(&wav_header)?;
let mut samples = Vec::new();
let mut sample_count = 0;
for _adpcm_samples in MonoADPCMIterator::create(wav_file.samples::<i16>()) {
let mut lpc = LPC::empty();
for adpcm_sample in MonoADPCMIterator::create(wav_file.samples::<i16>()) {
let (vagadpcm, new_lpc) = VAGADPCM::create(adpcm_sample?, lpc);
samples.push(vagadpcm);
lpc = new_lpc;
sample_count += 1;
}
println!("Parsed {} vag samples", sample_count);
Err(Error::not_implemented("my vag convert"))
// TODO: Restructure
tool_helper::raw::write_raw(output, &VAGHeader::create(sample_count, wav_header.sample_rate, "Planschi"))?;
for sample in samples {
tool_helper::raw::write_raw(output, &sample)?;
}
Ok(())
}
fn validate(wav_header: &hound::WavSpec) -> Result<(), Error> {

View File

@ -1,17 +1,148 @@
use tool_helper::Error;
use tool_helper::{raw::RawConversion, Error};
#[repr(packed)]
// TODO: Move logic of fixed values into the raw function, including the BE/LE stuff?
pub struct VAGHeader {
_id: [u8; 4],
_version: u32,
_reserved: u32,
_data_size: u32,
_sampling_frequency: u32,
_reserved2: [u8; 12],
_name: [u8; 16]
}
impl VAGHeader {
const SIZE: usize = std::mem::size_of::<VAGHeader>();
const ID: [u8; 4] = ['V' as u8, 'A' as u8, 'G' as u8, 'p' as u8];
const VERSION: u32 = 0x02;
const RESERVED: u32 = 0;
const RESERVED2: [u8; 12] = [0; 12];
// TODO: Ensure big endianes
pub fn create(vagpcm_samples: usize, sampling_frequency: u32, _name: &str) -> VAGHeader {
// TODO: Support naming feature
let data_size = (vagpcm_samples*VAGADPCM::SIZE) as u32;
let name = ['A' as u8; 16];
println!("Got {} samples; Writing {} bytes", vagpcm_samples, data_size);
VAGHeader{
_id: VAGHeader::ID,
_version: VAGHeader::VERSION.to_be(),
_reserved: VAGHeader::RESERVED,
_data_size: data_size.to_be(),
_sampling_frequency: sampling_frequency.to_be(),
_reserved2: VAGHeader::RESERVED2,
_name: name
}
}
}
impl RawConversion<{VAGHeader::SIZE}> for VAGHeader {
fn convert_to_raw(&self) -> [u8; VAGHeader::SIZE] {
unsafe {
let data: [u8; VAGHeader::SIZE] = std::mem::transmute_copy(&self as &VAGHeader);
data
}
}
}
pub struct VAGADPCM {
data: [u32; 4]
}
impl VAGADPCM {
const SIZE: usize = std::mem::size_of::<VAGADPCM>();
const ADPCM_SAMPLES_PER_VAGADPCM:usize = 28;
pub fn create() -> VAGADPCM {
VAGADPCM{data: [0; 4]}
pub fn empty() -> VAGADPCM {
VAGADPCM{data: [0; 4]}
}
pub fn create_for_filter_shift(filter: u32, shift: u32) -> VAGADPCM {
VAGADPCM{data: [(12 - shift) | filter << 4, 0, 0, 0]}
}
pub fn create(samples: ADPCMSampleForVag, lpc_tap: LPC) -> (VAGADPCM, LPC) {
fn cap_value(value: i32, min: i32, max: i32) -> i32 {
if value < min {
min
}
else if value > max {
max
}
else {
value
}
}
let mut best_frame = VAGADPCM::empty();
let mut best_tap = LPC::empty();
let mut best_error = std::u64::MAX as u64;
for (filter_id, filter) in LPC::FILTERS.iter().enumerate() {
for shift in 0..=12 {
let mut this_frame = VAGADPCM::create_for_filter_shift(filter_id as u32, shift);
let this_tap = lpc_tap.clone();
let mut this_error = 0;
for (sample_id, sample) in samples.iter().enumerate() {
let x = *sample as i32;
let p = (this_tap.first*filter.first + this_tap.second*filter.second + 32) >> 6;
let r = x - p;
let q = cap_value((r + (((1 << shift) - (r < 0) as i32) >> 1)) >> shift, -8, 7);
let y = cap_value(p + (q << shift), std::i16::MIN as i32, std::i16::MAX as i32);
let e = y - x;
this_frame.data[(sample_id+4)/8] |= ((q&0xF) << (((sample_id + 4)%8)*4)) as u32;
this_error += (e*e) as u64;
}
if this_error < best_error {
best_tap = this_tap;
best_error = this_error;
best_frame = this_frame;
}
}
}
(best_frame, best_tap)
}
}
impl RawConversion<{VAGADPCM::SIZE}> for VAGADPCM {
fn convert_to_raw(&self) -> [u8; VAGADPCM::SIZE] {
unsafe {
let data: [u8; VAGADPCM::SIZE] = std::mem::transmute_copy(&self as &VAGADPCM);
data
}
}
}
#[derive(Clone)]
pub struct LPC {
first: i32,
second: i32
}
impl LPC {
const FILTERS: [LPC; 5] = [
LPC{first: 0, second: 0},
LPC{first: 60, second: 0},
LPC{first: 115, second: -52},
LPC{first: 98, second: -55},
LPC{first: 122, second: -60}
];
pub fn empty() -> LPC {
LPC{first: 0, second: 0}
}
}
pub type ADPCMSampleForVag = [i16; VAGADPCM::ADPCM_SAMPLES_PER_VAGADPCM];
pub struct MonoADPCMIterator<I: std::iter::Iterator<Item=Result<i16, hound::Error>>> {
iter: I
}