Resampling and interleaving supported
This commit is contained in:
parent
c83e27a1c9
commit
f905bf6013
|
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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> {
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
|
@ -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];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue