Implement resampling
This commit is contained in:
parent
850c2d23e4
commit
3cea8d99ff
|
@ -1,7 +1,7 @@
|
|||
mod error;
|
||||
|
||||
use super::Frequency as RequestedFrequencyType;
|
||||
use rubato::{FftFixedIn, Resampler};
|
||||
use rubato::{FftFixedInOut, Resampler};
|
||||
use symphonia::core::{
|
||||
audio::{Layout, SampleBuffer},
|
||||
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
|
||||
|
@ -28,55 +28,24 @@ impl Orality {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct AudioSamples<T> {
|
||||
samples: Vec::<T>,
|
||||
pub struct PreparedAudioSamples {
|
||||
samples: Vec::<i16>,
|
||||
orality: Orality,
|
||||
frequency: u32,
|
||||
}
|
||||
|
||||
impl<T> AudioSamples<T> {
|
||||
pub fn new(samples: Vec<T>, channels: usize, frequency: u32) -> Result<AudioSamples<T>, Error> {
|
||||
impl PreparedAudioSamples {
|
||||
pub fn new(samples: Vec<i16>, channels: usize, frequency: u32) -> Result<PreparedAudioSamples, 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(AudioSamples{samples, orality, frequency})
|
||||
Ok(PreparedAudioSamples{samples, orality, frequency})
|
||||
}
|
||||
|
||||
// TODO: Reconsider passing zero_maker
|
||||
pub fn to_planar(self, chunk_size: usize, zero_maker: fn()->T) -> Vec<Vec::<T>> {
|
||||
let (_, chunk_remainder) = self.calculate_chunks(chunk_size);
|
||||
let num_channel = self.orality.as_channels();
|
||||
let mut result = Vec::new();
|
||||
|
||||
for _ in 0..num_channel {
|
||||
result.push(Vec::<T>::new());
|
||||
}
|
||||
|
||||
for (idx, sample) in self.samples.into_iter().enumerate() {
|
||||
result[idx%num_channel].push(sample);
|
||||
}
|
||||
|
||||
for channel in &mut result {
|
||||
println!("({} - {} = {})", chunk_size, chunk_remainder, chunk_size - chunk_remainder);
|
||||
for _ in 0..(chunk_size - chunk_remainder) {
|
||||
channel.push(zero_maker());
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn calculate_chunks(&self, chunk_size: usize) -> (usize, usize) {
|
||||
let sample_len = self.samples.len()/self.orality.as_channels();
|
||||
let div_value = chunk_size;
|
||||
|
||||
(sample_len/div_value, sample_len%div_value)
|
||||
}
|
||||
|
||||
pub fn samples(&self) -> &Vec::<T> {
|
||||
pub fn samples(&self) -> &Vec::<i16> {
|
||||
&self.samples
|
||||
}
|
||||
|
||||
|
@ -91,21 +60,21 @@ struct InternalAudioSamples {
|
|||
}
|
||||
|
||||
impl InternalAudioSamples {
|
||||
pub fn new(planar_samples: Vec<Vec<f32>>, frequency: u32) -> InternalAudioSamples {
|
||||
InternalAudioSamples{planar_samples, frequency}
|
||||
}
|
||||
|
||||
pub fn calculate_chunks(&self, chunk_size: usize) -> (usize, usize) {
|
||||
if self.planar_samples.len() > 0 {
|
||||
let sample_len = self.planar_samples[0].len();
|
||||
(sample_len/chunk_size, sample_len%chunk_size)
|
||||
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"))
|
||||
}
|
||||
|
||||
else {
|
||||
(0, 0)
|
||||
Ok(InternalAudioSamples{planar_samples, frequency})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sample_len(&self) -> usize {
|
||||
self.planar_samples[0].len()
|
||||
}
|
||||
|
||||
pub fn channels(&self) -> usize {
|
||||
self.planar_samples.len()
|
||||
}
|
||||
|
@ -120,29 +89,27 @@ impl InternalAudioSamples {
|
|||
}
|
||||
}
|
||||
|
||||
pub type PreparedAudioSamples = AudioSamples<i16>;
|
||||
|
||||
pub fn load_as_i16_audio(input: Input, freq_type: RequestedFrequencyType) -> Result<PreparedAudioSamples, Error> {
|
||||
let raw_audio = load_raw_audio(input)?;
|
||||
let raw_audio = resample(raw_audio, 37_800)?;
|
||||
|
||||
test_write_wav(raw_audio)?;
|
||||
test_write_wav(down_sample_interleave(raw_audio)?)?;
|
||||
Err(Error::not_implemented("Resampling not implemented"))
|
||||
}
|
||||
|
||||
fn test_write_wav(audio_samples: InternalAudioSamples) -> Result<(), Error> {
|
||||
/*let spec = hound::WavSpec {
|
||||
fn test_write_wav(audio_samples: PreparedAudioSamples) -> Result<(), Error> {
|
||||
let spec = hound::WavSpec {
|
||||
channels: 2,
|
||||
sample_rate: 37_800,
|
||||
bits_per_sample: 32,
|
||||
sample_format: hound::SampleFormat::Float,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
|
||||
let mut file = hound::WavWriter::create("planschi.wav", spec).unwrap();
|
||||
for sample in audio_samples.samples {
|
||||
file.write_sample(sample)?;
|
||||
}
|
||||
file.finalize()?;*/
|
||||
file.finalize()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -171,7 +138,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 Ok(InternalAudioSamples::new(samples, frequency));
|
||||
return InternalAudioSamples::new(samples, frequency);
|
||||
}
|
||||
}
|
||||
return Err(error::next_packet(err));
|
||||
|
@ -215,12 +182,20 @@ fn decode(mut format: Box<dyn FormatReader>, mut decoder: Box<dyn Decoder>, trac
|
|||
|
||||
fn resample(input: InternalAudioSamples, target_frequency: u32) -> Result<InternalAudioSamples, Error> {
|
||||
const HIGH_QUALITY_CHUNKS:usize = (1024*10)*100;
|
||||
const SUB_CHUNKS:usize = 20;
|
||||
fn process_partial(input_option: Option<&[&[f32]]>, resampler: &mut FftFixedInOut<f32>, planar_output: &mut Vec<Vec<f32>>) -> Result<(), Error> {
|
||||
let new_samples = resampler.process_partial(input_option, None).map_err(error::resample)?;
|
||||
for (channel, channel_samples) in new_samples.into_iter().enumerate() {
|
||||
planar_output[channel].extend(channel_samples.iter());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let (chunk_size, sub_chunks) = (HIGH_QUALITY_CHUNKS, SUB_CHUNKS);
|
||||
let (chunk_count, chunk_remainder) = input.calculate_chunks(chunk_size);
|
||||
let chunk_size = HIGH_QUALITY_CHUNKS;
|
||||
let mut planar_input = input.planar_slices();
|
||||
let mut resampler = FftFixedIn::<f32>::new(input.frequency as usize, target_frequency as usize, chunk_size, sub_chunks, input.channels()).map_err(error::resampler_construction)?;
|
||||
let mut resampler = FftFixedInOut::<f32>::new(input.frequency as usize, target_frequency as usize, chunk_size, input.channels()).map_err(error::resampler_construction)?;
|
||||
let delay = resampler.output_delay();
|
||||
let mut sample_len = input.sample_len();
|
||||
let new_sample_len = (sample_len as f64*(target_frequency as f64/input.frequency as f64)) as usize;
|
||||
let mut planar_output = {
|
||||
let mut planar_output = Vec::new();
|
||||
|
||||
|
@ -230,27 +205,34 @@ fn resample(input: InternalAudioSamples, target_frequency: u32) -> Result<Intern
|
|||
planar_output
|
||||
};
|
||||
|
||||
for _chunk in 0..chunk_count {
|
||||
loop {
|
||||
let next_input_frames = resampler.input_frames_next();
|
||||
if next_input_frames > sample_len {
|
||||
if sample_len > 0 {
|
||||
// Still frames left
|
||||
process_partial(Some(&planar_input), &mut resampler, &mut planar_output)?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let new_samples = resampler.process(&planar_input, None).map_err(error::resample)?;
|
||||
for (channel, slice) in planar_input.iter_mut().enumerate() {
|
||||
*slice = &slice[chunk_size..];
|
||||
*slice = &slice[next_input_frames..];
|
||||
planar_output[channel].extend(new_samples[channel].iter());
|
||||
}
|
||||
sample_len -= next_input_frames;
|
||||
}
|
||||
|
||||
let mut process_partial = |input_option: Option<&[&[f32]]>| -> Result<(), Error> {
|
||||
let new_samples = resampler.process_partial(input_option, None).map_err(error::resample)?;
|
||||
for (channel, channel_samples) in new_samples.into_iter().enumerate() {
|
||||
planar_output[channel].extend(channel_samples.iter());
|
||||
if planar_output[0].len() < delay + new_sample_len {
|
||||
// Flush
|
||||
process_partial(None, &mut resampler, &mut planar_output)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if chunk_remainder > 0 {
|
||||
process_partial(Some(&planar_input))?;
|
||||
for channel in &mut planar_output {
|
||||
let start = delay;
|
||||
let end = start + new_sample_len;
|
||||
*channel = channel[start..end].into();
|
||||
}
|
||||
process_partial(None)?;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
let spec = hound::WavSpec {
|
||||
|
@ -266,8 +248,11 @@ fn resample(input: InternalAudioSamples, target_frequency: u32) -> Result<Intern
|
|||
}
|
||||
file.finalize()?;
|
||||
|
||||
InternalAudioSamples::new(planar_output, target_frequency)
|
||||
}
|
||||
|
||||
Err(Error::not_implemented("resample"))
|
||||
fn down_sample_interleave(input: InternalAudioSamples) -> Result<PreparedAudioSamples, Error> {
|
||||
Err(Error::not_implemented("down_sample_interleave"))
|
||||
}
|
||||
|
||||
fn load_to_ram(mut input: Input) -> Result<std::io::Cursor<Vec<u8>>, Error> {
|
||||
|
|
Loading…
Reference in New Issue