Prepare for XA encoding

This commit is contained in:
Jaby 2024-11-09 22:21:32 +00:00
parent cb8f8eb11f
commit b74c2cb578
4 changed files with 90 additions and 24 deletions

View File

@ -1,9 +1,39 @@
mod raw_audio;
mod xa_audio;
use clap::{Args, ValueEnum};
use std::io::Write;
use tool_helper::{Error, Input};
pub fn convert(input: Input, _output: &mut dyn Write) -> Result<(), Error> {
let _prepared_xa_audio = raw_audio::load_for_xa_audio(input)?;
Err(Error::not_implemented("XA conversion"))
#[derive(Args)]
pub struct Arguments {
#[clap(value_enum, value_parser, default_value_t=Frequency::High)]
frequency: Frequency,
#[clap(value_enum, value_parser, default_value_t=Channels::Stereo)]
channels: Channels,
#[clap(value_enum, value_parser, default_value_t=SampleDepth::Normal)]
sample_depth: SampleDepth,
}
#[derive(Copy, Clone, ValueEnum)]
pub enum Frequency {
High,
Low,
}
#[derive(Copy, Clone, ValueEnum)]
pub enum Channels {
Stereo,
Mono,
}
#[derive(Copy, Clone, ValueEnum)]
pub enum SampleDepth {
Normal,
High
}
pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> {
let prepared_xa_audio = raw_audio::load_as_i16_audio(input, xa_audio::audio_conversion_settings(&args))?;
xa_audio::convert(prepared_xa_audio, output, &args)
}

View File

@ -1,17 +1,28 @@
mod error;
use symphonia::core::audio::{RawSampleBuffer, SignalSpec};
use symphonia::core::codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL};
use symphonia::core::errors::Error as SymError;
use symphonia::core::formats::{FormatReader, FormatOptions};
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;
use symphonia::core::{
audio::{Layout, SampleBuffer, SignalSpec},
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
errors::Error as SymError,
formats::{FormatReader, FormatOptions},
io::MediaSourceStream,
meta::MetadataOptions,
probe::Hint
};
use tool_helper::{Error, Input};
type XASamples = RawSampleBuffer::<i16>;
pub type I16Samples = SampleBuffer::<i16>;
pub struct Settings {
frequency: u32,
layout: Layout
}
pub fn load_for_xa_audio(input: Input) -> Result<XASamples, Error> {
impl Settings {
pub fn new(frequency: u32, layout: Layout) -> Settings {
Settings{frequency, layout}
}
}
pub fn load_as_i16_audio(input: Input, settings: Settings) -> Result<I16Samples, Error> {
let media_stream = MediaSourceStream::new(Box::new(load_to_ram(input)?), Default::default());
let format = symphonia::default::get_probe().format(&Hint::new(), media_stream, &FormatOptions::default(), &MetadataOptions::default()).map_err(error::probe)?.format;
let track = format.tracks().iter().find(|t| t.codec_params.codec != CODEC_TYPE_NULL).ok_or_else(error::find_track)?;
@ -20,11 +31,11 @@ pub fn load_for_xa_audio(input: Input) -> Result<XASamples, Error> {
let decoder = symphonia::default::get_codecs().make(&track.codec_params, &DecoderOptions::default()).map_err(error::decoder)?;
let track_id = track.id;
decode(format, decoder, track_id)
decode(format, decoder, settings, track_id)
}
fn decode(mut format: Box<dyn FormatReader>, mut decoder: Box<dyn Decoder>, track_id: u32) -> Result<XASamples, Error> {
let mut xa_audio = None;
fn decode(mut format: Box<dyn FormatReader>, mut decoder: Box<dyn Decoder>, settings: Settings, track_id: u32) -> Result<I16Samples, Error> {
let mut i16_audio = None;
loop {
// Get the next packet from the media format.
@ -33,7 +44,7 @@ fn decode(mut format: Box<dyn FormatReader>, mut decoder: Box<dyn Decoder>, trac
Err(err) => {
if let SymError::IoError(io_err) = &err {
if io_err.kind() == std::io::ErrorKind::UnexpectedEof {
return xa_audio.ok_or_else(error::no_buffer_created);
return i16_audio.ok_or_else(error::no_buffer_created);
}
}
return Err(error::next_packet(err));
@ -50,14 +61,14 @@ fn decode(mut format: Box<dyn FormatReader>, mut decoder: Box<dyn Decoder>, trac
// Decode the packet into audio samples.
let packet = decoder.decode(&packet).map_err(error::decode)?;
if xa_audio.is_none() {
if i16_audio.is_none() {
let duration = packet.capacity() as u64;
xa_audio = Some(XASamples::new(duration, SignalSpec::new_with_layout(44_100, symphonia::core::audio::Layout::Stereo)));
i16_audio = Some(I16Samples::new(duration, SignalSpec::new_with_layout(settings.frequency, settings.layout)));
}
if let Some(xa_audio) = &mut xa_audio {
xa_audio.copy_interleaved_ref(packet);
if let Some(i16_audio) = &mut i16_audio {
i16_audio.copy_interleaved_ref(packet);
}
}
}

View File

@ -0,0 +1,25 @@
use super::Arguments;
use super::raw_audio::{I16Samples, Settings};
use std::io::Write;
use symphonia::core::audio::Layout;
use tool_helper::Error;
const HIGH_FREQUENCY:u32 = 37_800;
const LOW_FREQUENCY:u32 = 18_900;
pub fn audio_conversion_settings(arguments: &Arguments) -> Settings {
let frequency = match arguments.frequency {
super::Frequency::High => HIGH_FREQUENCY,
super::Frequency::Low => LOW_FREQUENCY,
};
let layout = match arguments.channels {
super::Channels::Stereo => Layout::Stereo,
super::Channels::Mono => Layout::Mono,
};
Settings::new(frequency, layout)
}
pub fn convert(_input: I16Samples, _output: &mut dyn Write, _arguments: &Arguments) -> Result<(), Error> {
Err(Error::not_implemented("XA conversion"))
}

View File

@ -25,7 +25,7 @@ enum SubCommands {
Nothing,
SimpleTIM(reduced_tim::Arguments),
VAG(vag::Arguments),
MyXA,
MyXA(my_xa::Arguments),
// === External Commands ===
XA(xa::Arguments),
@ -37,7 +37,7 @@ impl SubCommands {
SubCommands::Nothing => false,
SubCommands::SimpleTIM(_) => false,
SubCommands::VAG(_) => false,
SubCommands::MyXA => false,
SubCommands::MyXA(_) => false,
SubCommands::XA(_) => true
}
@ -63,7 +63,7 @@ fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
SubCommands::VAG(args) => audio::vag::convert(args, &cmd.output_file, input, dst_buffer),
SubCommands::MyXA => audio::my_xa::convert(input, dst_buffer),
SubCommands::MyXA(args) => audio::my_xa::convert(args, input, dst_buffer),
_ => Err(Error::from_str("External functions can not be called for internal conversion"))
}
};