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