mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
More touch ups
This commit is contained in:
parent
a331729f0e
commit
8aaab0a210
5 changed files with 1498 additions and 1464 deletions
|
@ -8,10 +8,14 @@ use crate::{
|
||||||
RESAMPLER_INPUT_SIZE, SAMPLE_RATE,
|
RESAMPLER_INPUT_SIZE, SAMPLE_RATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reciprocals allow us to multiply instead of divide during interpolation.
|
const HZ48000_RESAMPLE_FACTOR: f64 = 48_000.0 / (SAMPLE_RATE as f64);
|
||||||
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
|
const HZ88200_RESAMPLE_FACTOR: f64 = 88_200.0 / (SAMPLE_RATE as f64);
|
||||||
const HZ88200_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 88_200.0;
|
const HZ96000_RESAMPLE_FACTOR: f64 = 96_000.0 / (SAMPLE_RATE as f64);
|
||||||
const HZ96000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 96_000.0;
|
|
||||||
|
// Reciprocals allow us to multiply instead of divide during normal interpolation.
|
||||||
|
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = 1.0 / HZ48000_RESAMPLE_FACTOR;
|
||||||
|
const HZ88200_RESAMPLE_FACTOR_RECIPROCAL: f64 = 1.0 / HZ88200_RESAMPLE_FACTOR;
|
||||||
|
const HZ96000_RESAMPLE_FACTOR_RECIPROCAL: f64 = 1.0 / HZ96000_RESAMPLE_FACTOR;
|
||||||
|
|
||||||
// sample rate * channels
|
// sample rate * channels
|
||||||
const HZ44100_SAMPLES_PER_SECOND: f64 = 44_100.0 * 2.0;
|
const HZ44100_SAMPLES_PER_SECOND: f64 = 44_100.0 * 2.0;
|
||||||
|
@ -23,13 +27,13 @@ const HZ96000_SAMPLES_PER_SECOND: f64 = 96_000.0 * 2.0;
|
||||||
// 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 * HZ48000_RESAMPLE_FACTOR) as usize;
|
||||||
|
|
||||||
const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
|
const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
|
||||||
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ88200_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
|
(RESAMPLER_INPUT_SIZE as f64 * HZ88200_RESAMPLE_FACTOR) as usize;
|
||||||
|
|
||||||
const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize =
|
const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize =
|
||||||
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
|
(RESAMPLER_INPUT_SIZE as f64 * HZ96000_RESAMPLE_FACTOR) as usize;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub enum SampleRate {
|
pub enum SampleRate {
|
||||||
|
@ -134,6 +138,17 @@ impl SampleRate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_resample_factor(&self) -> Option<f64> {
|
||||||
|
use SampleRate::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Hz44100 => None,
|
||||||
|
Hz48000 => Some(HZ48000_RESAMPLE_FACTOR),
|
||||||
|
Hz88200 => Some(HZ88200_RESAMPLE_FACTOR),
|
||||||
|
Hz96000 => Some(HZ96000_RESAMPLE_FACTOR),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_resample_factor_reciprocal(&self) -> Option<f64> {
|
pub fn get_resample_factor_reciprocal(&self) -> Option<f64> {
|
||||||
use SampleRate::*;
|
use SampleRate::*;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,32 +7,6 @@ use crate::{
|
||||||
ratio_to_db, PCM_AT_0DBFS,
|
ratio_to_db, PCM_AT_0DBFS,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NoNormalisation;
|
|
||||||
|
|
||||||
impl NoNormalisation {
|
|
||||||
fn normalise(mut samples: Vec<f64>, volume: f64) -> Vec<f64> {
|
|
||||||
if volume < 1.0 {
|
|
||||||
samples.iter_mut().for_each(|sample| *sample *= volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
samples
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BasicNormalisation;
|
|
||||||
|
|
||||||
impl BasicNormalisation {
|
|
||||||
fn normalise(mut samples: Vec<f64>, volume: f64, factor: f64) -> Vec<f64> {
|
|
||||||
if volume < 1.0 || factor < 1.0 {
|
|
||||||
samples
|
|
||||||
.iter_mut()
|
|
||||||
.for_each(|sample| *sample *= factor * volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
samples
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct DynamicNormalisation {
|
struct DynamicNormalisation {
|
||||||
threshold_db: f64,
|
threshold_db: f64,
|
||||||
|
@ -194,12 +168,35 @@ impl Normalisation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalise(&mut self, samples: Vec<f64>, volume: f64, factor: f64) -> Vec<f64> {
|
fn normalise(&mut self, mut samples: Vec<f64>, volume: f64, factor: f64) -> Vec<f64> {
|
||||||
use Normalisation::*;
|
use Normalisation::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
None => NoNormalisation::normalise(samples, volume),
|
None => {
|
||||||
Basic => BasicNormalisation::normalise(samples, volume, factor),
|
// We only care about volume.
|
||||||
|
// We don't care about factor.
|
||||||
|
// volume: 0.0 - 1.0
|
||||||
|
if volume < 1.0 {
|
||||||
|
// for each sample: sample = sample * volume
|
||||||
|
samples.iter_mut().for_each(|sample| *sample *= volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples
|
||||||
|
}
|
||||||
|
Basic => {
|
||||||
|
// We care about both volume and factor.
|
||||||
|
// volume: 0.0 - 1.0
|
||||||
|
// factor: 0.0 - 1.0
|
||||||
|
if volume < 1.0 || factor < 1.0 {
|
||||||
|
// for each sample: sample = sample * volume * factor
|
||||||
|
samples
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|sample| *sample *= volume * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples
|
||||||
|
}
|
||||||
|
// We don't care about anything, DynamicNormalisation does that for us.
|
||||||
Dynamic(ref mut d) => d.normalise(samples, volume, factor),
|
Dynamic(ref mut d) => d.normalise(samples, volume, factor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +219,7 @@ impl Normaliser {
|
||||||
normalisation_type: config.normalisation_type,
|
normalisation_type: config.normalisation_type,
|
||||||
pregain_db: config.normalisation_pregain_db,
|
pregain_db: config.normalisation_pregain_db,
|
||||||
threshold_dbfs: config.normalisation_threshold_dbfs,
|
threshold_dbfs: config.normalisation_threshold_dbfs,
|
||||||
factor: 1.0,
|
factor: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +230,7 @@ impl Normaliser {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
|
self.factor = 0.0;
|
||||||
self.normalisation.stop();
|
self.normalisation.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +239,8 @@ impl Normaliser {
|
||||||
auto_normalise_as_album: bool,
|
auto_normalise_as_album: bool,
|
||||||
data: NormalisationData,
|
data: NormalisationData,
|
||||||
) {
|
) {
|
||||||
|
// Normalisation::None doesn't use the factor,
|
||||||
|
// so there is no need to waste the time calculating it.
|
||||||
if self.normalisation != Normalisation::None {
|
if self.normalisation != Normalisation::None {
|
||||||
self.factor = self.get_factor(auto_normalise_as_album, data);
|
self.factor = self.get_factor(auto_normalise_as_album, data);
|
||||||
}
|
}
|
||||||
|
@ -326,6 +326,7 @@ impl Normaliser {
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Normalisation Data: {:?}", data);
|
debug!("Normalisation Data: {:?}", data);
|
||||||
|
debug!("Normalisation Type: {:?}", self.normalisation_type);
|
||||||
debug!(
|
debug!(
|
||||||
"Calculated Normalisation Factor for {:?}: {:.2}%",
|
"Calculated Normalisation Factor for {:?}: {:.2}%",
|
||||||
norm_type,
|
norm_type,
|
||||||
|
|
|
@ -1,76 +1,60 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{vec_deque, VecDeque},
|
collections::VecDeque, process::exit, sync::atomic::Ordering::SeqCst, sync::mpsc, thread,
|
||||||
process::exit,
|
|
||||||
sync::atomic::Ordering,
|
|
||||||
sync::mpsc,
|
|
||||||
thread,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE};
|
||||||
config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE,
|
|
||||||
SAMPLE_RATE as SOURCE_SAMPLE_RATE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DelayLine {
|
|
||||||
buffer: VecDeque<f64>,
|
|
||||||
coefficients_length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DelayLine {
|
|
||||||
fn new(coefficients_length: usize) -> DelayLine {
|
|
||||||
Self {
|
|
||||||
buffer: VecDeque::with_capacity(coefficients_length),
|
|
||||||
coefficients_length,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self, sample: f64) {
|
|
||||||
self.buffer.push_back(sample);
|
|
||||||
|
|
||||||
while self.buffer.len() > self.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 ConvolutionFilter {
|
struct ConvolutionFilter {
|
||||||
coefficients: Vec<f64>,
|
coefficients: Vec<f64>,
|
||||||
delay_line: DelayLine,
|
coefficients_length: usize,
|
||||||
|
delay_line: VecDeque<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConvolutionFilter {
|
impl ConvolutionFilter {
|
||||||
fn new(coefficients: Vec<f64>) -> Self {
|
fn new(coefficients: Vec<f64>) -> Self {
|
||||||
let delay_line = DelayLine::new(coefficients.len());
|
let coefficients_length = coefficients.len();
|
||||||
|
let delay_line = VecDeque::with_capacity(coefficients_length);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
coefficients,
|
coefficients,
|
||||||
|
coefficients_length,
|
||||||
delay_line,
|
delay_line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convolute(&mut self, sample: f64) -> f64 {
|
fn get_convoluted_sample(&mut self) -> f64 {
|
||||||
self.delay_line.push(sample);
|
let output_sample = self
|
||||||
|
.coefficients
|
||||||
// Temporal convolution
|
|
||||||
self.coefficients
|
|
||||||
.iter()
|
.iter()
|
||||||
.zip(&self.delay_line)
|
.zip(&self.delay_line)
|
||||||
.fold(0.0, |acc, (coefficient, delay_line_sample)| {
|
.fold(0.0, |acc, (coefficient, delay_line_sample)| {
|
||||||
acc + coefficient * delay_line_sample
|
acc + coefficient * delay_line_sample
|
||||||
})
|
});
|
||||||
|
|
||||||
|
self.delay_line.pop_front();
|
||||||
|
|
||||||
|
output_sample
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convolute(&mut self, sample: f64) -> f64 {
|
||||||
|
self.delay_line.push_back(sample);
|
||||||
|
|
||||||
|
if self.delay_line.len() == self.coefficients_length {
|
||||||
|
self.get_convoluted_sample()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain(&mut self) -> Vec<f64> {
|
||||||
|
let delay_line_len = self.delay_line.len();
|
||||||
|
let mut output = Vec::with_capacity(delay_line_len);
|
||||||
|
|
||||||
|
for _ in 0..delay_line_len {
|
||||||
|
output.push(self.get_convoluted_sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
@ -81,6 +65,7 @@ impl ConvolutionFilter {
|
||||||
struct MonoSincResampler {
|
struct MonoSincResampler {
|
||||||
interpolator: ConvolutionFilter,
|
interpolator: ConvolutionFilter,
|
||||||
input_buffer: Vec<f64>,
|
input_buffer: Vec<f64>,
|
||||||
|
resample_factor: f64,
|
||||||
resample_factor_reciprocal: f64,
|
resample_factor_reciprocal: f64,
|
||||||
delay_line_latency: u64,
|
delay_line_latency: u64,
|
||||||
interpolation_output_size: usize,
|
interpolation_output_size: usize,
|
||||||
|
@ -92,6 +77,8 @@ impl MonoSincResampler {
|
||||||
.get_interpolation_coefficients()
|
.get_interpolation_coefficients()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let resample_factor = sample_rate.get_resample_factor().unwrap_or_default();
|
||||||
|
|
||||||
let resample_factor_reciprocal = sample_rate
|
let resample_factor_reciprocal = sample_rate
|
||||||
.get_resample_factor_reciprocal()
|
.get_resample_factor_reciprocal()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
@ -104,7 +91,8 @@ impl MonoSincResampler {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
interpolator: ConvolutionFilter::new(coefficients),
|
interpolator: ConvolutionFilter::new(coefficients),
|
||||||
input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
|
input_buffer: Vec::with_capacity(RESAMPLER_INPUT_SIZE),
|
||||||
|
resample_factor,
|
||||||
resample_factor_reciprocal,
|
resample_factor_reciprocal,
|
||||||
delay_line_latency,
|
delay_line_latency,
|
||||||
interpolation_output_size,
|
interpolation_output_size,
|
||||||
|
@ -120,40 +108,76 @@ impl MonoSincResampler {
|
||||||
self.input_buffer.clear();
|
self.input_buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>> {
|
fn drain(&mut self) -> (Option<Vec<f64>>, u64) {
|
||||||
|
// On drain the interpolation isn't perfect for a couple reasons:
|
||||||
|
// 1. buffer len * resample_factor more than likely isn't an integer.
|
||||||
|
// 2. As you drain the delay line there are less and less samples to use for interpolation.
|
||||||
|
let output_len = (self.input_buffer.len() as f64 * self.resample_factor) as usize;
|
||||||
|
let mut output = Vec::with_capacity(output_len);
|
||||||
|
|
||||||
|
output.extend((0..output_len).map(|ouput_index| {
|
||||||
|
self.interpolator.convolute(
|
||||||
|
*self
|
||||||
|
.input_buffer
|
||||||
|
.get((ouput_index as f64 * self.resample_factor_reciprocal) as usize)
|
||||||
|
.unwrap_or(&0.0),
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
|
let interpolator_drainage = self.interpolator.drain();
|
||||||
|
|
||||||
|
output.reserve_exact(interpolator_drainage.len());
|
||||||
|
|
||||||
|
output.extend(interpolator_drainage.iter());
|
||||||
|
|
||||||
|
let output_len = output.len() as f64;
|
||||||
|
|
||||||
|
// Do a simple linear fade out of the drainage (about 5ms) to hide/prevent audible artifacts.
|
||||||
|
for (index, sample) in output.iter_mut().enumerate() {
|
||||||
|
let fade_factor = 1.0 - (index as f64) / output_len;
|
||||||
|
*sample *= fade_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
(Some(output), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resample(&mut self, samples: &[f64]) -> (Option<Vec<f64>>, u64) {
|
||||||
self.input_buffer.extend_from_slice(samples);
|
self.input_buffer.extend_from_slice(samples);
|
||||||
|
|
||||||
let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
|
let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
|
||||||
|
|
||||||
if num_buffer_chunks == 0 {
|
if num_buffer_chunks == 0 {
|
||||||
return None;
|
return (None, self.get_latency_pcm());
|
||||||
}
|
}
|
||||||
|
|
||||||
let input_size = num_buffer_chunks * RESAMPLER_INPUT_SIZE;
|
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 output_size = num_buffer_chunks * self.interpolation_output_size;
|
||||||
|
|
||||||
let mut output = Vec::with_capacity(output_size);
|
let mut output = Vec::with_capacity(output_size);
|
||||||
|
|
||||||
output.extend((0..output_size).map(|ouput_index| {
|
output.extend((0..output_size).map(|ouput_index| {
|
||||||
// The factional weights are already calculated and factored
|
// Since the interpolation coefficients are pre-calculated we can pretend like
|
||||||
// into our interpolation coefficients so all we have to
|
// we're doing nearest neighbor interpolation and then push the samples though
|
||||||
// do is pretend we're doing nearest-neighbor interpolation
|
// the interpolator as if it were a simple FIR filter (which it actually also is).
|
||||||
// and push samples though the Interpolator and what comes
|
// What comes out the other side is anti-aliased windowed sinc interpolated samples.
|
||||||
// out the other side is Sinc Windowed Interpolated samples.
|
self.interpolator.convolute(
|
||||||
let sample_index = (ouput_index as f64 * self.resample_factor_reciprocal) as usize;
|
*self
|
||||||
let sample = self.input_buffer[sample_index];
|
.input_buffer
|
||||||
self.interpolator.convolute(sample)
|
.get((ouput_index as f64 * self.resample_factor_reciprocal) as usize)
|
||||||
|
.unwrap_or(&0.0),
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
self.input_buffer.drain(..input_size);
|
self.input_buffer.drain(..input_size);
|
||||||
|
|
||||||
Some(output)
|
(Some(output), self.get_latency_pcm())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResampleTask {
|
enum ResampleTask {
|
||||||
Stop,
|
Stop,
|
||||||
|
Drain,
|
||||||
Terminate,
|
Terminate,
|
||||||
Resample(Vec<f64>),
|
Resample(Vec<f64>),
|
||||||
}
|
}
|
||||||
|
@ -165,12 +189,14 @@ struct ResampleWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResampleWorker {
|
impl ResampleWorker {
|
||||||
fn new(mut resampler: MonoSincResampler, name: String) -> Self {
|
fn new(sample_rate: SampleRate, name: String) -> Self {
|
||||||
let (task_sender, task_receiver) = mpsc::channel();
|
let (task_sender, task_receiver) = mpsc::channel();
|
||||||
let (result_sender, result_receiver) = mpsc::channel();
|
let (result_sender, result_receiver) = mpsc::channel();
|
||||||
|
|
||||||
let builder = thread::Builder::new().name(name.clone());
|
let builder = thread::Builder::new().name(name.clone());
|
||||||
|
|
||||||
|
let mut resampler = MonoSincResampler::new(sample_rate);
|
||||||
|
|
||||||
let handle = match builder.spawn(move || loop {
|
let handle = match builder.spawn(move || loop {
|
||||||
match task_receiver.recv() {
|
match task_receiver.recv() {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -183,11 +209,11 @@ impl ResampleWorker {
|
||||||
}
|
}
|
||||||
Ok(task) => match task {
|
Ok(task) => match task {
|
||||||
ResampleTask::Stop => resampler.stop(),
|
ResampleTask::Stop => resampler.stop(),
|
||||||
|
ResampleTask::Drain => {
|
||||||
|
result_sender.send(resampler.drain()).ok();
|
||||||
|
}
|
||||||
ResampleTask::Resample(samples) => {
|
ResampleTask::Resample(samples) => {
|
||||||
let resampled = resampler.resample(&samples);
|
result_sender.send(resampler.resample(&samples)).ok();
|
||||||
let latency = resampler.get_latency_pcm();
|
|
||||||
|
|
||||||
result_sender.send((resampled, latency)).ok();
|
|
||||||
}
|
}
|
||||||
ResampleTask::Terminate => {
|
ResampleTask::Terminate => {
|
||||||
loop {
|
loop {
|
||||||
|
@ -231,6 +257,12 @@ impl ResampleWorker {
|
||||||
.and_then(|sender| sender.send(ResampleTask::Stop).ok());
|
.and_then(|sender| sender.send(ResampleTask::Stop).ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(&mut self) {
|
||||||
|
self.task_sender
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|sender| sender.send(ResampleTask::Drain).ok());
|
||||||
|
}
|
||||||
|
|
||||||
fn resample(&mut self, samples: Vec<f64>) {
|
fn resample(&mut self, samples: Vec<f64>) {
|
||||||
self.task_sender
|
self.task_sender
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
@ -294,16 +326,16 @@ impl StereoInterleavedResampler {
|
||||||
debug!("Interpolation Type: Windowed Sinc");
|
debug!("Interpolation Type: Windowed Sinc");
|
||||||
|
|
||||||
// The player increments the player id when it gets it...
|
// The player increments the player id when it gets it...
|
||||||
let player_id = PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1);
|
let player_id = PLAYER_COUNTER.load(SeqCst).saturating_sub(1);
|
||||||
|
|
||||||
Resampler::Worker {
|
Resampler::Worker {
|
||||||
left_resampler: ResampleWorker::new(
|
left_resampler: ResampleWorker::new(
|
||||||
MonoSincResampler::new(sample_rate),
|
sample_rate,
|
||||||
format!("resampler:{player_id}:left"),
|
format!("resampler:L:{player_id}"),
|
||||||
),
|
),
|
||||||
right_resampler: ResampleWorker::new(
|
right_resampler: ResampleWorker::new(
|
||||||
MonoSincResampler::new(sample_rate),
|
sample_rate,
|
||||||
format!("resampler:{player_id}:right"),
|
format!("resampler:R:{player_id}"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +351,26 @@ impl StereoInterleavedResampler {
|
||||||
self.latency_pcm
|
self.latency_pcm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn drain(&mut self) -> Option<Vec<f64>> {
|
||||||
|
match &mut self.resampler {
|
||||||
|
// Bypass is basically a no-op.
|
||||||
|
Resampler::Bypass => None,
|
||||||
|
Resampler::Worker {
|
||||||
|
left_resampler,
|
||||||
|
right_resampler,
|
||||||
|
} => {
|
||||||
|
left_resampler.drain();
|
||||||
|
right_resampler.drain();
|
||||||
|
|
||||||
|
let (resampled, latency_pcm) = Self::get_resampled(left_resampler, right_resampler);
|
||||||
|
|
||||||
|
self.latency_pcm = latency_pcm;
|
||||||
|
|
||||||
|
resampled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resample(&mut self, input_samples: Vec<f64>) -> Option<Vec<f64>> {
|
pub fn resample(&mut self, input_samples: Vec<f64>) -> Option<Vec<f64>> {
|
||||||
match &mut self.resampler {
|
match &mut self.resampler {
|
||||||
// Bypass is basically a no-op.
|
// Bypass is basically a no-op.
|
||||||
|
@ -332,17 +384,11 @@ impl StereoInterleavedResampler {
|
||||||
left_resampler.resample(left_samples);
|
left_resampler.resample(left_samples);
|
||||||
right_resampler.resample(right_samples);
|
right_resampler.resample(right_samples);
|
||||||
|
|
||||||
let (left_resampled, left_latency_pcm) = left_resampler.get_resampled();
|
let (resampled, latency_pcm) = Self::get_resampled(left_resampler, right_resampler);
|
||||||
let (right_resampled, right_latency_pcm) = right_resampler.get_resampled();
|
|
||||||
|
|
||||||
// They should always be equal
|
self.latency_pcm = latency_pcm;
|
||||||
self.latency_pcm = left_latency_pcm.max(right_latency_pcm);
|
|
||||||
|
|
||||||
left_resampled.and_then(|left_samples| {
|
resampled
|
||||||
right_resampled.map(|right_samples| {
|
|
||||||
Self::interleave_samples(&left_samples, &right_samples)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,6 +410,24 @@ impl StereoInterleavedResampler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_resampled(
|
||||||
|
left_resampler: &mut ResampleWorker,
|
||||||
|
right_resampler: &mut ResampleWorker,
|
||||||
|
) -> (Option<Vec<f64>>, u64) {
|
||||||
|
let (left_resampled, left_latency_pcm) = left_resampler.get_resampled();
|
||||||
|
let (right_resampled, right_latency_pcm) = right_resampler.get_resampled();
|
||||||
|
|
||||||
|
let resampled = left_resampled.and_then(|left_samples| {
|
||||||
|
right_resampled
|
||||||
|
.map(|right_samples| Self::interleave_samples(&left_samples, &right_samples))
|
||||||
|
});
|
||||||
|
|
||||||
|
// They should always be equal
|
||||||
|
let latency_pcm = left_latency_pcm.max(right_latency_pcm);
|
||||||
|
|
||||||
|
(resampled, latency_pcm)
|
||||||
|
}
|
||||||
|
|
||||||
fn interleave_samples(left_samples: &[f64], right_samples: &[f64]) -> Vec<f64> {
|
fn interleave_samples(left_samples: &[f64], right_samples: &[f64]) -> Vec<f64> {
|
||||||
// Re-interleave the resampled channels.
|
// Re-interleave the resampled channels.
|
||||||
let mut output = Vec::with_capacity(left_samples.len() + right_samples.len());
|
let mut output = Vec::with_capacity(left_samples.len() + right_samples.len());
|
||||||
|
|
|
@ -10,15 +10,120 @@ use crate::{
|
||||||
MS_PER_PAGE,
|
MS_PER_PAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SamplePipeline {
|
pub enum SamplePipeline {
|
||||||
|
PassThrough(Bypass),
|
||||||
|
Process(Pipeline),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SamplePipeline {
|
||||||
|
pub fn new(
|
||||||
|
config: &PlayerConfig,
|
||||||
|
sink: Box<dyn Sink>,
|
||||||
|
volume_getter: Box<dyn VolumeGetter>,
|
||||||
|
) -> Self {
|
||||||
|
if config.passthrough {
|
||||||
|
SamplePipeline::PassThrough(Bypass::new(config, sink))
|
||||||
|
} else {
|
||||||
|
SamplePipeline::Process(Pipeline::new(config, sink, volume_getter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latency_ms(&mut self) -> u32 {
|
||||||
|
use SamplePipeline::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PassThrough(_) => 0,
|
||||||
|
Process(ref mut p) => p.get_latency_ms(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> SinkResult<()> {
|
||||||
|
use SamplePipeline::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PassThrough(ref mut p) => p.start()?,
|
||||||
|
Process(ref mut p) => p.start()?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) -> SinkResult<()> {
|
||||||
|
use SamplePipeline::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PassThrough(ref mut p) => p.stop()?,
|
||||||
|
Process(ref mut p) => p.stop()?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_normalisation_data(
|
||||||
|
&mut self,
|
||||||
|
auto_normalise_as_album: bool,
|
||||||
|
data: NormalisationData,
|
||||||
|
) {
|
||||||
|
use SamplePipeline::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PassThrough(_) => (),
|
||||||
|
Process(ref mut p) => p.update_normalisation_data(auto_normalise_as_album, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> {
|
||||||
|
use SamplePipeline::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PassThrough(ref mut p) => p.write(packet)?,
|
||||||
|
Process(ref mut p) => p.write(packet)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bypass {
|
||||||
|
converter: Converter,
|
||||||
|
sink: Box<dyn Sink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bypass {
|
||||||
|
fn new(config: &PlayerConfig, sink: Box<dyn Sink>) -> Self {
|
||||||
|
let converter = Converter::new(config.ditherer);
|
||||||
|
|
||||||
|
Self { converter, sink }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&mut self) -> SinkResult<()> {
|
||||||
|
self.sink.start()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) -> SinkResult<()> {
|
||||||
|
self.sink.stop()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, packet: AudioPacket) -> SinkResult<()> {
|
||||||
|
self.sink.write(packet, &mut self.converter)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pipeline {
|
||||||
resampler: StereoInterleavedResampler,
|
resampler: StereoInterleavedResampler,
|
||||||
normaliser: Normaliser,
|
normaliser: Normaliser,
|
||||||
converter: Converter,
|
converter: Converter,
|
||||||
sink: Box<dyn Sink>,
|
sink: Box<dyn Sink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SamplePipeline {
|
impl Pipeline {
|
||||||
pub fn new(
|
fn new(
|
||||||
config: &PlayerConfig,
|
config: &PlayerConfig,
|
||||||
sink: Box<dyn Sink>,
|
sink: Box<dyn Sink>,
|
||||||
volume_getter: Box<dyn VolumeGetter>,
|
volume_getter: Box<dyn VolumeGetter>,
|
||||||
|
@ -36,27 +141,34 @@ impl SamplePipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_latency_ms(&mut self) -> u32 {
|
fn get_latency_ms(&mut self) -> u32 {
|
||||||
let total_latency_pcm = self.sink.get_latency_pcm() + self.resampler.get_latency_pcm();
|
let total_latency_pcm = self.sink.get_latency_pcm() + self.resampler.get_latency_pcm();
|
||||||
|
|
||||||
(total_latency_pcm as f64 * MS_PER_PAGE) as u32
|
(total_latency_pcm as f64 * MS_PER_PAGE) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) -> SinkResult<()> {
|
fn start(&mut self) -> SinkResult<()> {
|
||||||
self.sink.start()?;
|
self.sink.start()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) -> SinkResult<()> {
|
fn stop(&mut self) -> SinkResult<()> {
|
||||||
|
self.resampler
|
||||||
|
.drain()
|
||||||
|
.map(|processed_samples| self.normaliser.normalise(processed_samples))
|
||||||
|
.map(|new_packet| self.sink.write(new_packet, &mut self.converter))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
self.resampler.stop();
|
self.resampler.stop();
|
||||||
self.normaliser.stop();
|
self.normaliser.stop();
|
||||||
|
|
||||||
self.sink.stop()?;
|
self.sink.stop()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_normalisation_data(
|
fn update_normalisation_data(
|
||||||
&mut self,
|
&mut self,
|
||||||
auto_normalise_as_album: bool,
|
auto_normalise_as_album: bool,
|
||||||
data: NormalisationData,
|
data: NormalisationData,
|
||||||
|
@ -65,15 +177,13 @@ impl SamplePipeline {
|
||||||
.update_normalisation_data(auto_normalise_as_album, data);
|
.update_normalisation_data(auto_normalise_as_album, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> {
|
fn write(&mut self, packet: AudioPacket) -> SinkResult<()> {
|
||||||
if let AudioPacket::Samples(samples) = packet {
|
if let AudioPacket::Samples(samples) = packet {
|
||||||
self.resampler
|
self.resampler
|
||||||
.resample(samples)
|
.resample(samples)
|
||||||
.map(|processed_samples| self.normaliser.normalise(processed_samples))
|
.map(|processed_samples| self.normaliser.normalise(processed_samples))
|
||||||
.map(|new_packet| self.sink.write(new_packet, &mut self.converter))
|
.map(|new_packet| self.sink.write(new_packet, &mut self.converter))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
} else {
|
|
||||||
self.sink.write(packet, &mut self.converter)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue