More touch ups

This commit is contained in:
JasonLG1979 2023-09-03 17:32:55 -05:00
parent a331729f0e
commit 8aaab0a210
5 changed files with 1498 additions and 1464 deletions

View file

@ -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

View file

@ -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,

View file

@ -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());

View file

@ -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(())