Integrate all the progress into master #6
|
@ -4,6 +4,7 @@ mod xa_audio;
|
|||
use clap::{Args, ValueEnum};
|
||||
use std::io::Write;
|
||||
use tool_helper::{Error, Input};
|
||||
use xa_audio::{LOW_FREQUENCY, HIGH_FREQUENCY};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct Arguments {
|
||||
|
@ -26,7 +27,14 @@ pub enum SampleDepth {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
use super::Frequency as RequestedFrequencyType;
|
||||
use rubato::{FftFixedInOut, Resampler};
|
||||
use symphonia::core::{
|
||||
audio::{Layout, SampleBuffer},
|
||||
audio::{AudioBuffer, Layout, SampleBuffer, Signal, SignalSpec},
|
||||
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
|
||||
errors::Error as SymError,
|
||||
formats::{FormatOptions, FormatReader},
|
||||
io::MediaSourceStream,
|
||||
meta::MetadataOptions,
|
||||
probe::Hint, sample
|
||||
probe::Hint
|
||||
};
|
||||
use tool_helper::{Error, Input};
|
||||
|
||||
|
@ -19,30 +18,21 @@ pub enum Orality {
|
|||
Mono,
|
||||
}
|
||||
|
||||
impl Orality {
|
||||
fn as_channels(&self) -> usize {
|
||||
match self {
|
||||
Orality::Mono => 1,
|
||||
Orality::Stereo => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PreparedAudioSamples {
|
||||
pub struct CDAudioSamples {
|
||||
samples: Vec::<i16>,
|
||||
orality: Orality,
|
||||
frequency: u32,
|
||||
}
|
||||
|
||||
impl PreparedAudioSamples {
|
||||
pub fn new(samples: Vec<i16>, channels: usize, frequency: u32) -> Result<PreparedAudioSamples, Error> {
|
||||
impl CDAudioSamples {
|
||||
pub fn new(samples: Vec<i16>, channels: usize, frequency: u32) -> Result<CDAudioSamples, Error> {
|
||||
let orality = match channels {
|
||||
0 => return Err(Error::from_str("Input file has no audio channels")),
|
||||
1 => Orality::Mono,
|
||||
2 => Orality::Stereo,
|
||||
_ => 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> {
|
||||
|
@ -61,9 +51,8 @@ struct InternalAudioSamples {
|
|||
|
||||
impl InternalAudioSamples {
|
||||
pub fn new(planar_samples: Vec<Vec<f32>>, frequency: u32) -> Result<InternalAudioSamples, Error> {
|
||||
if planar_samples.len() < 1 {
|
||||
// TODO: Make better text
|
||||
Err(Error::from_str("Audio samples can not be empty"))
|
||||
if planar_samples.len() < 1 || planar_samples.len() > 2{
|
||||
Err(Error::from_str("Audio samples need to be either mono or stereo"))
|
||||
}
|
||||
|
||||
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 {
|
||||
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 = resample(raw_audio, 37_800)?;
|
||||
let raw_audio = resample(raw_audio, target_frequency)?;
|
||||
|
||||
test_write_wav(down_sample_interleave(raw_audio)?)?;
|
||||
Err(Error::not_implemented("Resampling not implemented"))
|
||||
down_sample_interleave(raw_audio)
|
||||
}
|
||||
|
||||
fn test_write_wav(audio_samples: PreparedAudioSamples) -> Result<(), Error> {
|
||||
fn _test_write_wav(audio_samples: CDAudioSamples) -> Result<(), Error> {
|
||||
let spec = hound::WavSpec {
|
||||
channels: 2,
|
||||
sample_rate: 37_800,
|
||||
channels: match audio_samples.orality {
|
||||
Orality::Mono => 1,
|
||||
Orality::Stereo => 2,
|
||||
},
|
||||
sample_rate: audio_samples.frequency,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
|
@ -251,8 +256,14 @@ fn resample(input: InternalAudioSamples, target_frequency: u32) -> Result<Intern
|
|||
InternalAudioSamples::new(planar_output, target_frequency)
|
||||
}
|
||||
|
||||
fn down_sample_interleave(input: InternalAudioSamples) -> Result<PreparedAudioSamples, Error> {
|
||||
Err(Error::not_implemented("down_sample_interleave"))
|
||||
fn down_sample_interleave(input: InternalAudioSamples) -> Result<CDAudioSamples, Error> {
|
||||
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> {
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
mod xapcm;
|
||||
|
||||
use super::Arguments;
|
||||
use super::raw_audio::PreparedAudioSamples;
|
||||
use super::raw_audio::CDAudioSamples;
|
||||
use std::io::Write;
|
||||
use tool_helper::Error;
|
||||
|
||||
const HIGH_FREQUENCY:u32 = 37_800;
|
||||
const LOW_FREQUENCY:u32 = 18_900;
|
||||
pub const HIGH_FREQUENCY:u32 = 37_800;
|
||||
pub const LOW_FREQUENCY:u32 = 18_900;
|
||||
|
||||
pub fn encode(input: PreparedAudioSamples, output: &mut dyn Write, arguments: &Arguments) -> Result<(), Error> {
|
||||
let mut encoder = xapcm::Encoder::new(&input.samples(), arguments.clone(), input.orality());
|
||||
let mut sector_count = 0;
|
||||
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());
|
||||
|
||||
while let Some(xa_sector) = encoder.encode_next_xa_sector()? {
|
||||
output.write(&xa_sector)?;
|
||||
|
||||
sector_count += 1;
|
||||
print!("\rSector: {} written", sector_count);
|
||||
}
|
||||
println!();
|
||||
//Err(Error::not_implemented("XA conversion"))
|
||||
Ok(())
|
||||
}
|
|
@ -13,7 +13,7 @@ pub struct Encoder<'a> {
|
|||
|
||||
impl<'a> Encoder<'a> {
|
||||
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_K2: [i16; 5] = [0, 0, -52, -55, -60];
|
||||
|
||||
|
|
Loading…
Reference in New Issue