mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add resampler
This commit is contained in:
parent
71660a2351
commit
3b64f25286
3 changed files with 556 additions and 4 deletions
|
@ -1,7 +1,7 @@
|
||||||
use std::{mem, str::FromStr, time::Duration};
|
use std::{mem, str::FromStr, time::Duration};
|
||||||
|
|
||||||
pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer};
|
pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer};
|
||||||
use crate::{SAMPLE_RATE, RESAMPLER_INPUT_SIZE, convert::i24, player::duration_to_coefficient};
|
use crate::{convert::i24, player::duration_to_coefficient, RESAMPLER_INPUT_SIZE, SAMPLE_RATE};
|
||||||
|
|
||||||
// Reciprocals allow us to multiply instead of divide during interpolation.
|
// Reciprocals allow us to multiply instead of divide during interpolation.
|
||||||
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
|
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
|
||||||
|
@ -16,7 +16,7 @@ const HZ96000_SAMPLES_PER_SECOND: f64 = 96_000.0 * 2.0;
|
||||||
|
|
||||||
// Given a RESAMPLER_INPUT_SIZE of 147 all of our output sizes work out
|
// Given a RESAMPLER_INPUT_SIZE of 147 all of our output sizes work out
|
||||||
// to be integers, which is a very good thing. That means no fractional samples
|
// to be integers, which is a very good thing. That means no fractional samples
|
||||||
// which translates to much better interpolation.
|
// which translates to much better interpolation.
|
||||||
const HZ48000_INTERPOLATION_OUTPUT_SIZE: usize =
|
const HZ48000_INTERPOLATION_OUTPUT_SIZE: usize =
|
||||||
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ48000_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
|
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ48000_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
|
||||||
const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
|
const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
|
||||||
|
@ -182,8 +182,8 @@ impl std::fmt::Display for SampleRate {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct ResampleSpec {
|
pub struct ResampleSpec {
|
||||||
resample_factor_reciprocal: f64,
|
pub resample_factor_reciprocal: f64,
|
||||||
interpolation_output_size: usize,
|
pub interpolation_output_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SampleRate {
|
impl SampleRate {
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub mod decoder;
|
||||||
pub mod dither;
|
pub mod dither;
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
|
pub mod resampler;
|
||||||
|
|
||||||
pub const RESAMPLER_INPUT_SIZE: usize = 147;
|
pub const RESAMPLER_INPUT_SIZE: usize = 147;
|
||||||
pub const SAMPLE_RATE: u32 = 44100;
|
pub const SAMPLE_RATE: u32 = 44100;
|
||||||
|
|
551
playback/src/resampler.rs
Normal file
551
playback/src/resampler.rs
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
use std::{
|
||||||
|
collections::{vec_deque, VecDeque},
|
||||||
|
marker::Send,
|
||||||
|
process::exit,
|
||||||
|
sync::mpsc,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{InterpolationQuality, SampleRate},
|
||||||
|
RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DelayLine {
|
||||||
|
buffer: VecDeque<f64>,
|
||||||
|
interpolation_coefficients_length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelayLine {
|
||||||
|
fn new(interpolation_coefficients_length: usize) -> DelayLine {
|
||||||
|
Self {
|
||||||
|
buffer: VecDeque::with_capacity(interpolation_coefficients_length),
|
||||||
|
interpolation_coefficients_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, sample: f64) {
|
||||||
|
self.buffer.push_back(sample);
|
||||||
|
|
||||||
|
while self.buffer.len() > self.interpolation_coefficients_length {
|
||||||
|
self.buffer.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a DelayLine {
|
||||||
|
type Item = &'a f64;
|
||||||
|
type IntoIter = vec_deque::Iter<'a, f64>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.buffer.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowedSincInterpolator {
|
||||||
|
interpolation_coefficients: Vec<f64>,
|
||||||
|
interpolation_coefficients_sum: f64,
|
||||||
|
delay_line: DelayLine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowedSincInterpolator {
|
||||||
|
fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self {
|
||||||
|
let interpolation_coefficients =
|
||||||
|
interpolation_quality.get_interpolation_coefficients(resample_factor_reciprocal);
|
||||||
|
|
||||||
|
let interpolation_coefficients_sum = interpolation_coefficients.iter().sum();
|
||||||
|
|
||||||
|
let delay_line = DelayLine::new(interpolation_coefficients.len());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
interpolation_coefficients,
|
||||||
|
interpolation_coefficients_sum,
|
||||||
|
delay_line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate(&mut self, sample: f64) -> f64 {
|
||||||
|
// Since our interpolation coefficients are pre-calculated
|
||||||
|
// we can basically pretend like the Interpolator is a FIR filter.
|
||||||
|
self.delay_line.push(sample);
|
||||||
|
|
||||||
|
// Temporal convolution
|
||||||
|
let mut output_sample = self
|
||||||
|
.interpolation_coefficients
|
||||||
|
.iter()
|
||||||
|
.zip(&self.delay_line)
|
||||||
|
.fold(0.0, |acc, (coefficient, delay_line_sample)| {
|
||||||
|
acc + coefficient * delay_line_sample
|
||||||
|
});
|
||||||
|
|
||||||
|
if output_sample.is_normal() {
|
||||||
|
// Make sure that interpolation does not add any gain.
|
||||||
|
output_sample /= self.interpolation_coefficients_sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_sample
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.delay_line.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MonoResampler {
|
||||||
|
fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
|
fn stop(&mut self);
|
||||||
|
fn get_latency_pcm(&mut self) -> u64;
|
||||||
|
fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonoSincResampler {
|
||||||
|
interpolator: WindowedSincInterpolator,
|
||||||
|
input_buffer: Vec<f64>,
|
||||||
|
resample_factor_reciprocal: f64,
|
||||||
|
delay_line_latency: u64,
|
||||||
|
interpolation_output_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonoResampler for MonoSincResampler {
|
||||||
|
fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self {
|
||||||
|
let spec = sample_rate.get_resample_spec();
|
||||||
|
|
||||||
|
let delay_line_latency = (interpolation_quality.get_interpolation_coefficients_length()
|
||||||
|
as f64
|
||||||
|
* spec.resample_factor_reciprocal)
|
||||||
|
.round() as u64;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
interpolator: WindowedSincInterpolator::new(
|
||||||
|
interpolation_quality,
|
||||||
|
spec.resample_factor_reciprocal,
|
||||||
|
),
|
||||||
|
|
||||||
|
input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
|
||||||
|
resample_factor_reciprocal: spec.resample_factor_reciprocal,
|
||||||
|
delay_line_latency,
|
||||||
|
interpolation_output_size: spec.interpolation_output_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_latency_pcm(&mut self) -> u64 {
|
||||||
|
self.input_buffer.len() as u64 + self.delay_line_latency
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.interpolator.clear();
|
||||||
|
self.input_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>> {
|
||||||
|
self.input_buffer.extend_from_slice(samples);
|
||||||
|
|
||||||
|
let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
|
||||||
|
|
||||||
|
if num_buffer_chunks == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_size = num_buffer_chunks * RESAMPLER_INPUT_SIZE;
|
||||||
|
// The size of the output after interpolation.
|
||||||
|
let output_size = num_buffer_chunks * self.interpolation_output_size;
|
||||||
|
|
||||||
|
let mut output = Vec::with_capacity(output_size);
|
||||||
|
|
||||||
|
output.extend((0..output_size).map(|ouput_index| {
|
||||||
|
// The factional weights are already calculated and factored
|
||||||
|
// into our interpolation coefficients so all we have to
|
||||||
|
// do is pretend we're doing nearest-neighbor interpolation
|
||||||
|
// and push samples though the Interpolator and what comes
|
||||||
|
// out the other side is Sinc Windowed Interpolated samples.
|
||||||
|
let sample_index = (ouput_index as f64 * self.resample_factor_reciprocal) as usize;
|
||||||
|
let sample = self.input_buffer[sample_index];
|
||||||
|
self.interpolator.interpolate(sample)
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.input_buffer.drain(..input_size);
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonoLinearResampler {
|
||||||
|
input_buffer: Vec<f64>,
|
||||||
|
resample_factor_reciprocal: f64,
|
||||||
|
interpolation_output_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonoResampler for MonoLinearResampler {
|
||||||
|
fn new(sample_rate: SampleRate, _: InterpolationQuality) -> Self {
|
||||||
|
let spec = sample_rate.get_resample_spec();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
|
||||||
|
resample_factor_reciprocal: spec.resample_factor_reciprocal,
|
||||||
|
interpolation_output_size: spec.interpolation_output_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_latency_pcm(&mut self) -> u64 {
|
||||||
|
self.input_buffer.len() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.input_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>> {
|
||||||
|
self.input_buffer.extend_from_slice(samples);
|
||||||
|
|
||||||
|
let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
|
||||||
|
|
||||||
|
if num_buffer_chunks == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_size = num_buffer_chunks * RESAMPLER_INPUT_SIZE;
|
||||||
|
// The size of the output after interpolation.
|
||||||
|
// We have to account for the fact that to do effective linear
|
||||||
|
// interpolation we need an extra sample to be able to throw away later.
|
||||||
|
let output_size = num_buffer_chunks * self.interpolation_output_size + 1;
|
||||||
|
|
||||||
|
let mut output = Vec::with_capacity(output_size);
|
||||||
|
|
||||||
|
output.extend((0..output_size).map(|output_index| {
|
||||||
|
let sample_index = output_index as f64 * self.resample_factor_reciprocal;
|
||||||
|
let sample_index_fractional = sample_index.fract();
|
||||||
|
let sample_index = sample_index as usize;
|
||||||
|
let sample = *self.input_buffer.get(sample_index).unwrap_or(&0.0);
|
||||||
|
let next_sample = *self.input_buffer.get(sample_index + 1).unwrap_or(&0.0);
|
||||||
|
let sample_index_fractional_complementary = 1.0 - sample_index_fractional;
|
||||||
|
sample * sample_index_fractional_complementary + next_sample * sample_index_fractional
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Remove the last garbage sample.
|
||||||
|
output.pop();
|
||||||
|
|
||||||
|
self.input_buffer.drain(..input_size);
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ResampleTask {
|
||||||
|
Stop,
|
||||||
|
Terminate,
|
||||||
|
GetLatency,
|
||||||
|
ProcessSamples(Vec<f64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ResampleResult {
|
||||||
|
Latency(u64),
|
||||||
|
ProcessedSamples(Option<Vec<f64>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResampleWorker {
|
||||||
|
task_sender: Option<mpsc::Sender<ResampleTask>>,
|
||||||
|
result_receiver: Option<mpsc::Receiver<ResampleResult>>,
|
||||||
|
handle: Option<thread::JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResampleWorker {
|
||||||
|
fn new(mut resampler: impl MonoResampler + Send + 'static, name: String) -> Self {
|
||||||
|
let (task_sender, task_receiver) = mpsc::channel();
|
||||||
|
let (result_sender, result_receiver) = mpsc::channel();
|
||||||
|
|
||||||
|
let builder = thread::Builder::new().name(name.clone());
|
||||||
|
|
||||||
|
let handle = match builder.spawn(move || loop {
|
||||||
|
match task_receiver.recv() {
|
||||||
|
Err(e) => {
|
||||||
|
match thread::current().name() {
|
||||||
|
Some(name) => error!("Error in <ResampleWorker> [{name}] thread: {e}"),
|
||||||
|
None => error!("Error in <ResampleWorker> thread: {e}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Ok(task) => match task {
|
||||||
|
ResampleTask::Stop => resampler.stop(),
|
||||||
|
ResampleTask::GetLatency => {
|
||||||
|
let latency = resampler.get_latency_pcm();
|
||||||
|
|
||||||
|
result_sender.send(ResampleResult::Latency(latency)).ok();
|
||||||
|
}
|
||||||
|
ResampleTask::ProcessSamples(samples) => {
|
||||||
|
let samples = resampler.resample(&samples);
|
||||||
|
|
||||||
|
result_sender
|
||||||
|
.send(ResampleResult::ProcessedSamples(samples))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
ResampleTask::Terminate => {
|
||||||
|
match thread::current().name() {
|
||||||
|
Some(name) => debug!("drop <ResampleWorker> [{name}] thread"),
|
||||||
|
None => debug!("drop <ResampleWorker> thread"),
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Ok(handle) => {
|
||||||
|
debug!("Created <ResampleWorker> [{name}] thread");
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error creating <ResampleWorker> [{name}] thread: {e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
task_sender: Some(task_sender),
|
||||||
|
result_receiver: Some(result_receiver),
|
||||||
|
handle: Some(handle),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_latency_pcm(&mut self) -> u64 {
|
||||||
|
self.task_sender
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|sender| sender.send(ResampleTask::GetLatency).ok());
|
||||||
|
|
||||||
|
self.result_receiver
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|result_receiver| result_receiver.recv().ok())
|
||||||
|
.and_then(|result| match result {
|
||||||
|
ResampleResult::Latency(latency) => Some(latency),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.task_sender
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|sender| sender.send(ResampleTask::Stop).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(&mut self, samples: Vec<f64>) {
|
||||||
|
self.task_sender
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|sender| sender.send(ResampleTask::ProcessSamples(samples)).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_result(&mut self) -> Option<Vec<f64>> {
|
||||||
|
self.result_receiver
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|result_receiver| result_receiver.recv().ok())
|
||||||
|
.and_then(|result| match result {
|
||||||
|
ResampleResult::ProcessedSamples(samples) => samples,
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ResampleWorker {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.task_sender
|
||||||
|
.take()
|
||||||
|
.and_then(|sender| sender.send(ResampleTask::Terminate).ok());
|
||||||
|
|
||||||
|
self.result_receiver
|
||||||
|
.take()
|
||||||
|
.and_then(|result_receiver| loop {
|
||||||
|
let drained = result_receiver.recv().ok();
|
||||||
|
|
||||||
|
if drained.is_none() {
|
||||||
|
break drained;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.handle.take().and_then(|handle| handle.join().ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Resampler {
|
||||||
|
Bypass,
|
||||||
|
Worker {
|
||||||
|
left_resampler: ResampleWorker,
|
||||||
|
right_resampler: ResampleWorker,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StereoInterleavedResampler {
|
||||||
|
resampler: Resampler,
|
||||||
|
latency_flag: bool,
|
||||||
|
process_flag: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StereoInterleavedResampler {
|
||||||
|
pub fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self {
|
||||||
|
debug!("Sample Rate: {sample_rate}");
|
||||||
|
|
||||||
|
let resampler = match sample_rate {
|
||||||
|
SampleRate::Hz44100 => {
|
||||||
|
debug!("Interpolation Type: Bypass");
|
||||||
|
debug!("No <ResampleWorker> threads required");
|
||||||
|
|
||||||
|
Resampler::Bypass
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
debug!("Interpolation Quality: {interpolation_quality}");
|
||||||
|
|
||||||
|
let left_thread_name = "resampler:left".to_string();
|
||||||
|
let right_thread_name = "resampler:right".to_string();
|
||||||
|
|
||||||
|
match interpolation_quality {
|
||||||
|
InterpolationQuality::Low => {
|
||||||
|
debug!("Interpolation Type: Linear");
|
||||||
|
|
||||||
|
let left = MonoLinearResampler::new(sample_rate, interpolation_quality);
|
||||||
|
let right = MonoLinearResampler::new(sample_rate, interpolation_quality);
|
||||||
|
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler: ResampleWorker::new(left, left_thread_name),
|
||||||
|
right_resampler: ResampleWorker::new(right, right_thread_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
debug!("Interpolation Type: Windowed Sinc");
|
||||||
|
|
||||||
|
let left = MonoSincResampler::new(sample_rate, interpolation_quality);
|
||||||
|
let right = MonoSincResampler::new(sample_rate, interpolation_quality);
|
||||||
|
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler: ResampleWorker::new(left, left_thread_name),
|
||||||
|
right_resampler: ResampleWorker::new(right, right_thread_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
resampler,
|
||||||
|
latency_flag: true,
|
||||||
|
process_flag: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latency_pcm(&mut self) -> u64 {
|
||||||
|
let alternate_latency_flag = self.alternate_latency_flag();
|
||||||
|
|
||||||
|
match &mut self.resampler {
|
||||||
|
Resampler::Bypass => 0,
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler,
|
||||||
|
right_resampler,
|
||||||
|
} => {
|
||||||
|
if alternate_latency_flag {
|
||||||
|
left_resampler.get_latency_pcm()
|
||||||
|
} else {
|
||||||
|
right_resampler.get_latency_pcm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alternate_latency_flag(&mut self) -> bool {
|
||||||
|
// We only actually need the latency
|
||||||
|
// from one channel for PCM frame latency
|
||||||
|
// to balance the load we alternate.
|
||||||
|
let current_flag = self.latency_flag;
|
||||||
|
self.latency_flag = !self.latency_flag;
|
||||||
|
current_flag
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alternate_process_flag(&mut self) -> bool {
|
||||||
|
// This along with the latency_flag makes
|
||||||
|
// sure that all worker calls alternate
|
||||||
|
// for load balancing.
|
||||||
|
let current_flag = self.process_flag;
|
||||||
|
self.process_flag = !self.process_flag;
|
||||||
|
current_flag
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process(&mut self, input_samples: &[f64]) -> Option<Vec<f64>> {
|
||||||
|
let alternate_process_flag = self.alternate_process_flag();
|
||||||
|
|
||||||
|
match &mut self.resampler {
|
||||||
|
// Bypass is basically a no-op.
|
||||||
|
Resampler::Bypass => Some(input_samples.to_vec()),
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler,
|
||||||
|
right_resampler,
|
||||||
|
} => {
|
||||||
|
let (left_samples, right_samples) = Self::deinterleave_samples(input_samples);
|
||||||
|
|
||||||
|
let (processed_left_samples, processed_right_samples) = if alternate_process_flag {
|
||||||
|
left_resampler.process(left_samples);
|
||||||
|
right_resampler.process(right_samples);
|
||||||
|
|
||||||
|
let processed_left_samples = left_resampler.receive_result();
|
||||||
|
let processed_right_samples = right_resampler.receive_result();
|
||||||
|
|
||||||
|
(processed_left_samples, processed_right_samples)
|
||||||
|
} else {
|
||||||
|
right_resampler.process(right_samples);
|
||||||
|
left_resampler.process(left_samples);
|
||||||
|
|
||||||
|
let processed_right_samples = right_resampler.receive_result();
|
||||||
|
let processed_left_samples = left_resampler.receive_result();
|
||||||
|
|
||||||
|
(processed_left_samples, processed_right_samples)
|
||||||
|
};
|
||||||
|
|
||||||
|
processed_left_samples.and_then(|left_samples| {
|
||||||
|
processed_right_samples.map(|right_samples| {
|
||||||
|
Self::interleave_samples(&left_samples, &right_samples)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
match &mut self.resampler {
|
||||||
|
// Stop does nothing
|
||||||
|
// if we're bypassed.
|
||||||
|
Resampler::Bypass => (),
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler,
|
||||||
|
right_resampler,
|
||||||
|
} => {
|
||||||
|
left_resampler.stop();
|
||||||
|
right_resampler.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interleave_samples(left_samples: &[f64], right_samples: &[f64]) -> Vec<f64> {
|
||||||
|
// Re-interleave the resampled channels.
|
||||||
|
left_samples
|
||||||
|
.iter()
|
||||||
|
.zip(right_samples.iter())
|
||||||
|
.flat_map(|(&x, &y)| vec![x, y])
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinterleave_samples(samples: &[f64]) -> (Vec<f64>, Vec<f64>) {
|
||||||
|
// Split the stereo interleaved samples into left and right channels.
|
||||||
|
let (left_samples, right_samples): (Vec<f64>, Vec<f64>) = samples
|
||||||
|
.chunks(2)
|
||||||
|
.map(|chunk| {
|
||||||
|
let [left_sample, right_sample] = [chunk[0], chunk[1]];
|
||||||
|
(left_sample, right_sample)
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
(left_samples, right_samples)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue