Resampling and interleaving supported

This commit is contained in:
Jaby 2024-12-01 11:02:51 +00:00
parent c83e27a1c9
commit f905bf6013
4 changed files with 54 additions and 41 deletions

View File

@ -4,6 +4,7 @@ mod xa_audio;
use clap::{Args, ValueEnum}; use clap::{Args, ValueEnum};
use std::io::Write; use std::io::Write;
use tool_helper::{Error, Input}; use tool_helper::{Error, Input};
use xa_audio::{LOW_FREQUENCY, HIGH_FREQUENCY};
#[derive(Args, Clone)] #[derive(Args, Clone)]
pub struct Arguments { pub struct Arguments {
@ -26,7 +27,14 @@ pub enum SampleDepth {
} }
pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> { pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> {
let prepared_xa_audio = raw_audio::load_as_i16_audio(input, args.frequency)?; let prepared_xa_audio = raw_audio::load_as_i16_audio(input, frequency_to_value(args.frequency))?;
xa_audio::encode(prepared_xa_audio, output, &args) xa_audio::encode(prepared_xa_audio, output, &args)
}
fn frequency_to_value(requested_freq: Frequency) -> u32 {
match requested_freq {
Frequency::High => HIGH_FREQUENCY,
Frequency::Low => LOW_FREQUENCY,
}
} }

View File

@ -1,15 +1,14 @@
mod error; mod error;
use super::Frequency as RequestedFrequencyType;
use rubato::{FftFixedInOut, Resampler}; use rubato::{FftFixedInOut, Resampler};
use symphonia::core::{ use symphonia::core::{
audio::{Layout, SampleBuffer}, audio::{AudioBuffer, Layout, SampleBuffer, Signal, SignalSpec},
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL}, codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
errors::Error as SymError, errors::Error as SymError,
formats::{FormatOptions, FormatReader}, formats::{FormatOptions, FormatReader},
io::MediaSourceStream, io::MediaSourceStream,
meta::MetadataOptions, meta::MetadataOptions,
probe::Hint, sample probe::Hint
}; };
use tool_helper::{Error, Input}; use tool_helper::{Error, Input};
@ -19,30 +18,21 @@ pub enum Orality {
Mono, Mono,
} }
impl Orality { pub struct CDAudioSamples {
fn as_channels(&self) -> usize {
match self {
Orality::Mono => 1,
Orality::Stereo => 2,
}
}
}
pub struct PreparedAudioSamples {
samples: Vec::<i16>, samples: Vec::<i16>,
orality: Orality, orality: Orality,
frequency: u32, frequency: u32,
} }
impl PreparedAudioSamples { impl CDAudioSamples {
pub fn new(samples: Vec<i16>, channels: usize, frequency: u32) -> Result<PreparedAudioSamples, Error> { pub fn new(samples: Vec<i16>, channels: usize, frequency: u32) -> Result<CDAudioSamples, Error> {
let orality = match channels { let orality = match channels {
0 => return Err(Error::from_str("Input file has no audio channels")), 0 => return Err(Error::from_str("Input file has no audio channels")),
1 => Orality::Mono, 1 => Orality::Mono,
2 => Orality::Stereo, 2 => Orality::Stereo,
_ => return Err(Error::from_str("Only Mono and Stereo input are supported")), _ => return Err(Error::from_str("Only Mono and Stereo input are supported")),
}; };
Ok(PreparedAudioSamples{samples, orality, frequency}) Ok(CDAudioSamples{samples, orality, frequency})
} }
pub fn samples(&self) -> &Vec::<i16> { pub fn samples(&self) -> &Vec::<i16> {
@ -61,9 +51,8 @@ struct InternalAudioSamples {
impl InternalAudioSamples { impl InternalAudioSamples {
pub fn new(planar_samples: Vec<Vec<f32>>, frequency: u32) -> Result<InternalAudioSamples, Error> { pub fn new(planar_samples: Vec<Vec<f32>>, frequency: u32) -> Result<InternalAudioSamples, Error> {
if planar_samples.len() < 1 { if planar_samples.len() < 1 || planar_samples.len() > 2{
// TODO: Make better text Err(Error::from_str("Audio samples need to be either mono or stereo"))
Err(Error::from_str("Audio samples can not be empty"))
} }
else { else {
@ -71,6 +60,20 @@ impl InternalAudioSamples {
} }
} }
pub fn into_audio_buffer(self) -> AudioBuffer<f32> {
let duration = self.sample_len() as u64;
let mut new_audio_buffer = AudioBuffer::new(duration, SignalSpec::new_with_layout(self.frequency, if self.planar_samples.len() == 1 {Layout::Mono} else {Layout::Stereo}));
new_audio_buffer.render_silence(None);
for (channel_idx, channel) in self.planar_samples.into_iter().enumerate() {
let dst_channel = new_audio_buffer.chan_mut(channel_idx);
for (sample_idx, sample) in channel.into_iter().enumerate() {
dst_channel[sample_idx] = sample;
}
}
new_audio_buffer
}
pub fn sample_len(&self) -> usize { pub fn sample_len(&self) -> usize {
self.planar_samples[0].len() self.planar_samples[0].len()
} }
@ -89,18 +92,20 @@ impl InternalAudioSamples {
} }
} }
pub fn load_as_i16_audio(input: Input, freq_type: RequestedFrequencyType) -> Result<PreparedAudioSamples, Error> { pub fn load_as_i16_audio(input: Input, target_frequency: u32) -> Result<CDAudioSamples, Error> {
let raw_audio = load_raw_audio(input)?; let raw_audio = load_raw_audio(input)?;
let raw_audio = resample(raw_audio, 37_800)?; let raw_audio = resample(raw_audio, target_frequency)?;
test_write_wav(down_sample_interleave(raw_audio)?)?; down_sample_interleave(raw_audio)
Err(Error::not_implemented("Resampling not implemented"))
} }
fn test_write_wav(audio_samples: PreparedAudioSamples) -> Result<(), Error> { fn _test_write_wav(audio_samples: CDAudioSamples) -> Result<(), Error> {
let spec = hound::WavSpec { let spec = hound::WavSpec {
channels: 2, channels: match audio_samples.orality {
sample_rate: 37_800, Orality::Mono => 1,
Orality::Stereo => 2,
},
sample_rate: audio_samples.frequency,
bits_per_sample: 16, bits_per_sample: 16,
sample_format: hound::SampleFormat::Int, sample_format: hound::SampleFormat::Int,
}; };
@ -251,8 +256,14 @@ fn resample(input: InternalAudioSamples, target_frequency: u32) -> Result<Intern
InternalAudioSamples::new(planar_output, target_frequency) InternalAudioSamples::new(planar_output, target_frequency)
} }
fn down_sample_interleave(input: InternalAudioSamples) -> Result<PreparedAudioSamples, Error> { fn down_sample_interleave(input: InternalAudioSamples) -> Result<CDAudioSamples, Error> {
Err(Error::not_implemented("down_sample_interleave")) let channels = input.channels();
let frequency = input.frequency;
let audio_buffer = input.into_audio_buffer();
let mut sample_buffer = SampleBuffer::<i16>::new(audio_buffer.capacity() as u64, audio_buffer.spec().clone());
sample_buffer.copy_interleaved_typed::<f32>(&audio_buffer);
CDAudioSamples::new(sample_buffer.samples().to_vec(), channels, frequency)
} }
fn load_to_ram(mut input: Input) -> Result<std::io::Cursor<Vec<u8>>, Error> { fn load_to_ram(mut input: Input) -> Result<std::io::Cursor<Vec<u8>>, Error> {

View File

@ -1,24 +1,18 @@
mod xapcm; mod xapcm;
use super::Arguments; use super::Arguments;
use super::raw_audio::PreparedAudioSamples; use super::raw_audio::CDAudioSamples;
use std::io::Write; use std::io::Write;
use tool_helper::Error; use tool_helper::Error;
const HIGH_FREQUENCY:u32 = 37_800; pub const HIGH_FREQUENCY:u32 = 37_800;
const LOW_FREQUENCY:u32 = 18_900; pub const LOW_FREQUENCY:u32 = 18_900;
pub fn encode(input: PreparedAudioSamples, output: &mut dyn Write, arguments: &Arguments) -> Result<(), Error> { pub fn encode(input: CDAudioSamples, output: &mut dyn Write, arguments: &Arguments) -> Result<(), Error> {
let mut encoder = xapcm::Encoder::new(&input.samples(), arguments.clone(), input.orality()); let mut encoder = xapcm::Encoder::new(&input.samples(), arguments.clone(), input.orality());
let mut sector_count = 0;
while let Some(xa_sector) = encoder.encode_next_xa_sector()? { while let Some(xa_sector) = encoder.encode_next_xa_sector()? {
output.write(&xa_sector)?; output.write(&xa_sector)?;
sector_count += 1;
print!("\rSector: {} written", sector_count);
} }
println!();
//Err(Error::not_implemented("XA conversion"))
Ok(()) Ok(())
} }

View File

@ -13,7 +13,7 @@ pub struct Encoder<'a> {
impl<'a> Encoder<'a> { impl<'a> Encoder<'a> {
const BLOCKS_PER_SECTOR:usize = 18; const BLOCKS_PER_SECTOR:usize = 18;
const XA_ADPCM_FILTER_COUNT: i32 = 4; const XA_ADPCM_FILTER_COUNT: i32 = 3;
const FILTER_K1: [i16; 5] = [0, 60, 115, 98, 122]; const FILTER_K1: [i16; 5] = [0, 60, 115, 98, 122];
const FILTER_K2: [i16; 5] = [0, 0, -52, -55, -60]; const FILTER_K2: [i16; 5] = [0, 0, -52, -55, -60];