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,
|
||||
};
|
||||
|
||||
// Reciprocals allow us to multiply instead of divide during interpolation.
|
||||
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
|
||||
const HZ88200_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 88_200.0;
|
||||
const HZ96000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 96_000.0;
|
||||
const HZ48000_RESAMPLE_FACTOR: f64 = 48_000.0 / (SAMPLE_RATE as f64);
|
||||
const HZ88200_RESAMPLE_FACTOR: f64 = 88_200.0 / (SAMPLE_RATE as f64);
|
||||
const HZ96000_RESAMPLE_FACTOR: f64 = 96_000.0 / (SAMPLE_RATE as f64);
|
||||
|
||||
// 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
|
||||
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
|
||||
// which translates to much better interpolation.
|
||||
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 =
|
||||
(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 =
|
||||
(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)]
|
||||
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> {
|
||||
use SampleRate::*;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,32 +7,6 @@ use crate::{
|
|||
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)]
|
||||
struct DynamicNormalisation {
|
||||
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::*;
|
||||
|
||||
match self {
|
||||
None => NoNormalisation::normalise(samples, volume),
|
||||
Basic => BasicNormalisation::normalise(samples, volume, factor),
|
||||
None => {
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +219,7 @@ impl Normaliser {
|
|||
normalisation_type: config.normalisation_type,
|
||||
pregain_db: config.normalisation_pregain_db,
|
||||
threshold_dbfs: config.normalisation_threshold_dbfs,
|
||||
factor: 1.0,
|
||||
factor: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +230,7 @@ impl Normaliser {
|
|||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.factor = 0.0;
|
||||
self.normalisation.stop();
|
||||
}
|
||||
|
||||
|
@ -241,6 +239,8 @@ impl Normaliser {
|
|||
auto_normalise_as_album: bool,
|
||||
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 {
|
||||
self.factor = self.get_factor(auto_normalise_as_album, data);
|
||||
}
|
||||
|
@ -326,6 +326,7 @@ impl Normaliser {
|
|||
};
|
||||
|
||||
debug!("Normalisation Data: {:?}", data);
|
||||
debug!("Normalisation Type: {:?}", self.normalisation_type);
|
||||
debug!(
|
||||
"Calculated Normalisation Factor for {:?}: {:.2}%",
|
||||
norm_type,
|
||||
|
|
|
@ -1,76 +1,60 @@
|
|||
use std::{
|
||||
collections::{vec_deque, VecDeque},
|
||||
process::exit,
|
||||
sync::atomic::Ordering,
|
||||
sync::mpsc,
|
||||
thread,
|
||||
collections::VecDeque, process::exit, sync::atomic::Ordering::SeqCst, sync::mpsc, thread,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
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()
|
||||
}
|
||||
}
|
||||
use crate::{config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE};
|
||||
|
||||
struct ConvolutionFilter {
|
||||
coefficients: Vec<f64>,
|
||||
delay_line: DelayLine,
|
||||
coefficients_length: usize,
|
||||
delay_line: VecDeque<f64>,
|
||||
}
|
||||
|
||||
impl ConvolutionFilter {
|
||||
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 {
|
||||
coefficients,
|
||||
coefficients_length,
|
||||
delay_line,
|
||||
}
|
||||
}
|
||||
|
||||
fn convolute(&mut self, sample: f64) -> f64 {
|
||||
self.delay_line.push(sample);
|
||||
|
||||
// Temporal convolution
|
||||
self.coefficients
|
||||
fn get_convoluted_sample(&mut self) -> f64 {
|
||||
let output_sample = self
|
||||
.coefficients
|
||||
.iter()
|
||||
.zip(&self.delay_line)
|
||||
.fold(0.0, |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) {
|
||||
|
@ -81,6 +65,7 @@ impl ConvolutionFilter {
|
|||
struct MonoSincResampler {
|
||||
interpolator: ConvolutionFilter,
|
||||
input_buffer: Vec<f64>,
|
||||
resample_factor: f64,
|
||||
resample_factor_reciprocal: f64,
|
||||
delay_line_latency: u64,
|
||||
interpolation_output_size: usize,
|
||||
|
@ -92,6 +77,8 @@ impl MonoSincResampler {
|
|||
.get_interpolation_coefficients()
|
||||
.unwrap_or_default();
|
||||
|
||||
let resample_factor = sample_rate.get_resample_factor().unwrap_or_default();
|
||||
|
||||
let resample_factor_reciprocal = sample_rate
|
||||
.get_resample_factor_reciprocal()
|
||||
.unwrap_or_default();
|
||||
|
@ -104,7 +91,8 @@ impl MonoSincResampler {
|
|||
|
||||
Self {
|
||||
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,
|
||||
delay_line_latency,
|
||||
interpolation_output_size,
|
||||
|
@ -120,40 +108,76 @@ impl MonoSincResampler {
|
|||
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);
|
||||
|
||||
let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
|
||||
|
||||
if num_buffer_chunks == 0 {
|
||||
return None;
|
||||
return (None, self.get_latency_pcm());
|
||||
}
|
||||
|
||||
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.convolute(sample)
|
||||
// Since the interpolation coefficients are pre-calculated we can pretend like
|
||||
// we're doing nearest neighbor interpolation and then push the samples though
|
||||
// the interpolator as if it were a simple FIR filter (which it actually also is).
|
||||
// What comes out the other side is anti-aliased windowed sinc interpolated samples.
|
||||
self.interpolator.convolute(
|
||||
*self
|
||||
.input_buffer
|
||||
.get((ouput_index as f64 * self.resample_factor_reciprocal) as usize)
|
||||
.unwrap_or(&0.0),
|
||||
)
|
||||
}));
|
||||
|
||||
self.input_buffer.drain(..input_size);
|
||||
|
||||
Some(output)
|
||||
(Some(output), self.get_latency_pcm())
|
||||
}
|
||||
}
|
||||
|
||||
enum ResampleTask {
|
||||
Stop,
|
||||
Drain,
|
||||
Terminate,
|
||||
Resample(Vec<f64>),
|
||||
}
|
||||
|
@ -165,12 +189,14 @@ struct 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 (result_sender, result_receiver) = mpsc::channel();
|
||||
|
||||
let builder = thread::Builder::new().name(name.clone());
|
||||
|
||||
let mut resampler = MonoSincResampler::new(sample_rate);
|
||||
|
||||
let handle = match builder.spawn(move || loop {
|
||||
match task_receiver.recv() {
|
||||
Err(e) => {
|
||||
|
@ -183,11 +209,11 @@ impl ResampleWorker {
|
|||
}
|
||||
Ok(task) => match task {
|
||||
ResampleTask::Stop => resampler.stop(),
|
||||
ResampleTask::Drain => {
|
||||
result_sender.send(resampler.drain()).ok();
|
||||
}
|
||||
ResampleTask::Resample(samples) => {
|
||||
let resampled = resampler.resample(&samples);
|
||||
let latency = resampler.get_latency_pcm();
|
||||
|
||||
result_sender.send((resampled, latency)).ok();
|
||||
result_sender.send(resampler.resample(&samples)).ok();
|
||||
}
|
||||
ResampleTask::Terminate => {
|
||||
loop {
|
||||
|
@ -231,6 +257,12 @@ impl ResampleWorker {
|
|||
.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>) {
|
||||
self.task_sender
|
||||
.as_mut()
|
||||
|
@ -294,16 +326,16 @@ impl StereoInterleavedResampler {
|
|||
debug!("Interpolation Type: Windowed Sinc");
|
||||
|
||||
// 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 {
|
||||
left_resampler: ResampleWorker::new(
|
||||
MonoSincResampler::new(sample_rate),
|
||||
format!("resampler:{player_id}:left"),
|
||||
sample_rate,
|
||||
format!("resampler:L:{player_id}"),
|
||||
),
|
||||
right_resampler: ResampleWorker::new(
|
||||
MonoSincResampler::new(sample_rate),
|
||||
format!("resampler:{player_id}:right"),
|
||||
sample_rate,
|
||||
format!("resampler:R:{player_id}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -319,6 +351,26 @@ impl StereoInterleavedResampler {
|
|||
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>> {
|
||||
match &mut self.resampler {
|
||||
// Bypass is basically a no-op.
|
||||
|
@ -332,17 +384,11 @@ impl StereoInterleavedResampler {
|
|||
left_resampler.resample(left_samples);
|
||||
right_resampler.resample(right_samples);
|
||||
|
||||
let (left_resampled, left_latency_pcm) = left_resampler.get_resampled();
|
||||
let (right_resampled, right_latency_pcm) = right_resampler.get_resampled();
|
||||
let (resampled, latency_pcm) = Self::get_resampled(left_resampler, right_resampler);
|
||||
|
||||
// They should always be equal
|
||||
self.latency_pcm = left_latency_pcm.max(right_latency_pcm);
|
||||
self.latency_pcm = latency_pcm;
|
||||
|
||||
left_resampled.and_then(|left_samples| {
|
||||
right_resampled.map(|right_samples| {
|
||||
Self::interleave_samples(&left_samples, &right_samples)
|
||||
})
|
||||
})
|
||||
resampled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
// Re-interleave the resampled channels.
|
||||
let mut output = Vec::with_capacity(left_samples.len() + right_samples.len());
|
||||
|
|
|
@ -10,15 +10,120 @@ use crate::{
|
|||
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,
|
||||
normaliser: Normaliser,
|
||||
converter: Converter,
|
||||
sink: Box<dyn Sink>,
|
||||
}
|
||||
|
||||
impl SamplePipeline {
|
||||
pub fn new(
|
||||
impl Pipeline {
|
||||
fn new(
|
||||
config: &PlayerConfig,
|
||||
sink: Box<dyn Sink>,
|
||||
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();
|
||||
|
||||
(total_latency_pcm as f64 * MS_PER_PAGE) as u32
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> SinkResult<()> {
|
||||
fn start(&mut self) -> SinkResult<()> {
|
||||
self.sink.start()?;
|
||||
|
||||
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.normaliser.stop();
|
||||
|
||||
self.sink.stop()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_normalisation_data(
|
||||
fn update_normalisation_data(
|
||||
&mut self,
|
||||
auto_normalise_as_album: bool,
|
||||
data: NormalisationData,
|
||||
|
@ -65,15 +177,13 @@ impl SamplePipeline {
|
|||
.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 {
|
||||
self.resampler
|
||||
.resample(samples)
|
||||
.map(|processed_samples| self.normaliser.normalise(processed_samples))
|
||||
.map(|new_packet| self.sink.write(new_packet, &mut self.converter))
|
||||
.transpose()?;
|
||||
} else {
|
||||
self.sink.write(packet, &mut self.converter)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue