From 0cefd0ea668ab1cdba28ec364ac92703eefdeb17 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 19:09:21 -0500 Subject: [PATCH 01/53] Better thread handling in player --- playback/src/player.rs | 61 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index 0790b40ab..96bd70817 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -448,10 +448,13 @@ impl Player { } } - let handle = thread::spawn(move || { - let player_id = PLAYER_COUNTER.fetch_add(1, Ordering::AcqRel); - debug!("new Player [{}]", player_id); + let player_id = PLAYER_COUNTER.fetch_add(1, Ordering::AcqRel); + let thread_name = format!("player:{}", player_id); + + let builder = thread::Builder::new().name(thread_name.clone()); + + let handle = match builder.spawn(move || { let converter = Converter::new(config.ditherer); let internal = PlayerInternal { @@ -479,11 +482,34 @@ impl Player { // While PlayerInternal is written as a future, it still contains blocking code. // It must be run by using block_on() in a dedicated thread. - let runtime = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); + let runtime = match tokio::runtime::Runtime::new() { + Ok(runtime) => runtime, + Err(e) => { + match thread::current().name() { + Some(name) => debug!("Error creating [{name}] thread, Failed to create Tokio runtime: {e}"), + None => debug!("Error creating thread, Failed to create Tokio runtime: {e}"), + } + + exit(1); + } + }; + runtime.block_on(internal); - debug!("PlayerInternal thread finished."); - }); + match thread::current().name() { + Some(name) => debug!(" [{name}] thread finished"), + None => debug!(" thread finished"), + } + }) { + Ok(handle) => { + debug!("Created [{thread_name}] thread"); + handle + } + Err(e) => { + error!("Error creating [{thread_name}] thread: {e}"); + exit(1); + } + }; Self { commands: Some(cmd_tx), @@ -2191,7 +2217,12 @@ impl PlayerInternal { let load_handles_clone = self.load_handles.clone(); let handle = tokio::runtime::Handle::current(); - let load_handle = thread::spawn(move || { + + let thread_name = format!("loader:{}", spotify_id.to_uri().unwrap_or_default()); + + let builder = thread::Builder::new().name(thread_name.clone()); + + let load_handle = match builder.spawn(move || { let data = handle.block_on(loader.load_track(spotify_id, position_ms)); if let Some(data) = data { let _ = result_tx.send(data); @@ -2199,7 +2230,21 @@ impl PlayerInternal { let mut load_handles = load_handles_clone.lock(); load_handles.remove(&thread::current().id()); - }); + + match thread::current().name() { + Some(name) => debug!(" [{name}] thread finished"), + None => debug!(" [loader] thread finished"), + } + }) { + Ok(handle) => { + debug!("Created [{thread_name}] thread"); + handle + } + Err(e) => { + error!("Error creating [{thread_name}] thread: {e}"); + exit(1); + } + }; let mut load_handles = self.load_handles.lock(); load_handles.insert(load_handle.thread().id(), load_handle); From 35b94bdc9467f312dac0e2244f8b5806f98132e7 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 19:17:23 -0500 Subject: [PATCH 02/53] Add the ability to get backend latency --- playback/src/audio_backend/alsa.rs | 17 +++++++++++++++++ playback/src/audio_backend/mod.rs | 3 +++ playback/src/audio_backend/pulseaudio.rs | 11 +++++++++++ 3 files changed, 31 insertions(+) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index fada25807..b647b1e4b 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -456,6 +456,23 @@ impl Sink for AlsaSink { Ok(()) } + fn get_latency_pcm(&mut self) -> u64 { + let buffer_len = self.period_buffer.len(); + + self.pcm + .as_mut() + .and_then(|pcm| { + pcm.status().ok().map(|status| { + let delay_frames = status.get_delay(); + + let frames_in_buffer = pcm.bytes_to_frames(buffer_len as isize); + + (delay_frames + frames_in_buffer) as u64 + }) + }) + .unwrap_or(0) + } + sink_as_bytes!(); } diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 058223956..05916fa65 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -30,6 +30,9 @@ pub trait Sink { fn stop(&mut self) -> SinkResult<()> { Ok(()) } + fn get_latency_pcm(&mut self) -> u64 { + 0 + } fn write(&mut self, packet: AudioPacket, converter: &mut Converter) -> SinkResult<()>; } diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 43d7ec079..b1b93822b 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -131,6 +131,17 @@ impl Sink for PulseAudioSink { Ok(()) } + fn get_latency_pcm(&mut self) -> u64 { + self.sink + .as_mut() + .and_then(|sink| { + sink.get_latency() + .ok() + .map(|micro_sec| (micro_sec.as_secs_f64() * SAMPLE_RATE as f64).round() as u64) + }) + .unwrap_or(0) + } + sink_as_bytes!(); } From 7dae33c4f0c6eaaab32c3ce1d05fb0ac50110b2c Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 19:24:40 -0500 Subject: [PATCH 03/53] Use default macro --- playback/src/config.rs | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index cdb455cee..8c64191f5 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -3,9 +3,10 @@ use std::{mem, str::FromStr, time::Duration}; pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer}; use crate::{convert::i24, player::duration_to_coefficient}; -#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum Bitrate { Bitrate96, + #[default] Bitrate160, Bitrate320, } @@ -22,19 +23,14 @@ impl FromStr for Bitrate { } } -impl Default for Bitrate { - fn default() -> Self { - Self::Bitrate160 - } -} - -#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum AudioFormat { F64, F32, S32, S24, S24_3, + #[default] S16, } @@ -53,12 +49,6 @@ impl FromStr for AudioFormat { } } -impl Default for AudioFormat { - fn default() -> Self { - Self::S16 - } -} - impl AudioFormat { // not used by all backends #[allow(dead_code)] @@ -73,10 +63,11 @@ impl AudioFormat { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum NormalisationType { Album, Track, + #[default] Auto, } @@ -92,15 +83,10 @@ impl FromStr for NormalisationType { } } -impl Default for NormalisationType { - fn default() -> Self { - Self::Auto - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum NormalisationMethod { Basic, + #[default] Dynamic, } @@ -115,12 +101,6 @@ impl FromStr for NormalisationMethod { } } -impl Default for NormalisationMethod { - fn default() -> Self { - Self::Dynamic - } -} - #[derive(Clone)] pub struct PlayerConfig { pub bitrate: Bitrate, From 71660a23515967ad973873c538aff9fe4cfc9056 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 19:51:14 -0500 Subject: [PATCH 04/53] Add InterpolationQuality and SampleRate enums --- playback/src/config.rs | 243 ++++++++++++++++++++++++++++++++++++++++- playback/src/lib.rs | 1 + 2 files changed, 243 insertions(+), 1 deletion(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 8c64191f5..a9a47e7dc 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,7 +1,248 @@ use std::{mem, str::FromStr, time::Duration}; pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer}; -use crate::{convert::i24, player::duration_to_coefficient}; +use crate::{SAMPLE_RATE, RESAMPLER_INPUT_SIZE, convert::i24, player::duration_to_coefficient}; + +// 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; + +// sample rate * channels +const HZ44100_SAMPLES_PER_SECOND: f64 = 44_100.0 * 2.0; +const HZ48000_SAMPLES_PER_SECOND: f64 = 48_000.0 * 2.0; +const HZ88200_SAMPLES_PER_SECOND: f64 = 88_200.0 * 2.0; +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 +// 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; +const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = + (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ88200_RESAMPLE_FACTOR_RECIPROCAL)) as usize; +const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = + (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; + +// Blackman Window coefficients +const BLACKMAN_A0: f64 = 0.42; +const BLACKMAN_A1: f64 = 0.5; +const BLACKMAN_A2: f64 = 0.08; + +// Constants for calculations +const TWO_TIMES_PI: f64 = 2.0 * std::f64::consts::PI; +const FOUR_TIMES_PI: f64 = 4.0 * std::f64::consts::PI; + +#[derive(Clone, Copy, Debug, Default)] +pub enum InterpolationQuality { + #[default] + Low, + Medium, + High, +} + +impl FromStr for InterpolationQuality { + type Err = (); + + fn from_str(s: &str) -> Result { + use InterpolationQuality::*; + + match s.to_lowercase().as_ref() { + "low" => Ok(Low), + "medium" => Ok(Medium), + "high" => Ok(High), + _ => Err(()), + } + } +} + +impl std::fmt::Display for InterpolationQuality { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InterpolationQuality::*; + + match self { + Low => write!(f, "Low"), + Medium => write!(f, "Medium"), + High => write!(f, "High"), + } + } +} + +impl InterpolationQuality { + pub fn get_interpolation_coefficients(&self, resample_factor_reciprocal: f64) -> Vec { + let interpolation_coefficients_length = self.get_interpolation_coefficients_length(); + + let mut coefficients = Vec::with_capacity(interpolation_coefficients_length); + + let last_index = interpolation_coefficients_length as f64 - 1.0; + + let sinc_center = last_index * 0.5; + + let mut coefficient_sum = 0.0; + + coefficients.extend((0..interpolation_coefficients_length).map( + |interpolation_coefficient_index| { + let index_float = interpolation_coefficient_index as f64; + let sample_index_fractional = (index_float * resample_factor_reciprocal).fract(); + let sinc_center_offset = index_float - sinc_center; + + let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional); + + let sinc_value = Self::sinc(sinc_center_offset); + // Calculate the Blackman window function for the given center offset + // w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)), + // where n is the center offset, N is the window size, + // and A0, A1, A2 are precalculated coefficients + + let two_pi_n = TWO_TIMES_PI * index_float; + let four_pi_n = FOUR_TIMES_PI * index_float; + + let blackman_window_value = BLACKMAN_A0 + - BLACKMAN_A1 * (two_pi_n / last_index).cos() + + BLACKMAN_A2 * (four_pi_n / last_index).cos(); + + let sinc_window = sinc_value * blackman_window_value; + + let coefficient = sinc_window * sample_index_fractional_sinc_weight; + + coefficient_sum += coefficient; + + coefficient + }, + )); + + coefficients + .iter_mut() + .for_each(|coefficient| *coefficient /= coefficient_sum); + + coefficients + } + + pub fn get_interpolation_coefficients_length(&self) -> usize { + use InterpolationQuality::*; + match self { + Low => 0, + Medium => 129, + High => 257, + } + } + + fn sinc(x: f64) -> f64 { + if x.abs() < f64::EPSILON { + 1.0 + } else { + let pi_x = std::f64::consts::PI * x; + pi_x.sin() / pi_x + } + } +} + +#[derive(Clone, Copy, Debug, Default)] +pub enum SampleRate { + #[default] + Hz44100, + Hz48000, + Hz88200, + Hz96000, +} + +impl FromStr for SampleRate { + type Err = (); + + fn from_str(s: &str) -> Result { + use SampleRate::*; + + // Match against both the actual + // stringified value and how most + // humans would write a sample rate. + match s.to_uppercase().as_ref() { + "hz44100" | "44100hz" | "44100" | "44.1khz" => Ok(Hz44100), + "hz48000" | "48000hz" | "48000" | "48khz" => Ok(Hz48000), + "hz88200" | "88200hz" | "88200" | "88.2khz" => Ok(Hz88200), + "hz96000" | "96000hz" | "96000" | "96khz" => Ok(Hz96000), + _ => Err(()), + } + } +} + +impl std::fmt::Display for SampleRate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use SampleRate::*; + + match self { + // Let's make these more human readable. + // "Hz44100" is just awkward. + Hz44100 => write!(f, "44.1kHz"), + Hz48000 => write!(f, "48kHz"), + Hz88200 => write!(f, "88.2kHz"), + Hz96000 => write!(f, "96kHz"), + } + } +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct ResampleSpec { + resample_factor_reciprocal: f64, + interpolation_output_size: usize, +} + +impl SampleRate { + pub fn as_u32(&self) -> u32 { + use SampleRate::*; + + match self { + Hz44100 => 44100, + Hz48000 => 48000, + Hz88200 => 88200, + Hz96000 => 96000, + } + } + + pub fn duration_to_normalisation_coefficient(&self, duration: Duration) -> f64 { + (-1.0 / (duration.as_secs_f64() * self.samples_per_second())).exp() + } + + pub fn normalisation_coefficient_to_duration(&self, coefficient: f64) -> Duration { + Duration::from_secs_f64(-1.0 / coefficient.ln() / self.samples_per_second()) + } + + fn samples_per_second(&self) -> f64 { + use SampleRate::*; + + match self { + Hz44100 => HZ44100_SAMPLES_PER_SECOND, + Hz48000 => HZ48000_SAMPLES_PER_SECOND, + Hz88200 => HZ88200_SAMPLES_PER_SECOND, + Hz96000 => HZ96000_SAMPLES_PER_SECOND, + } + } + + pub fn get_resample_spec(&self) -> ResampleSpec { + use SampleRate::*; + + match self { + // Dummy values to satisfy + // the match statement. + // 44.1kHz will be bypassed. + Hz44100 => ResampleSpec { + resample_factor_reciprocal: 1.0, + interpolation_output_size: RESAMPLER_INPUT_SIZE, + }, + Hz48000 => ResampleSpec { + resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL, + interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE, + }, + Hz88200 => ResampleSpec { + resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL, + interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE, + }, + Hz96000 => ResampleSpec { + resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL, + interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE, + }, + } + } +} #[derive(Clone, Copy, Debug, Default, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum Bitrate { diff --git a/playback/src/lib.rs b/playback/src/lib.rs index 43a5b4f0c..48fdb1057 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -13,6 +13,7 @@ pub mod dither; pub mod mixer; pub mod player; +pub const RESAMPLER_INPUT_SIZE: usize = 147; pub const SAMPLE_RATE: u32 = 44100; pub const NUM_CHANNELS: u8 = 2; pub const SAMPLES_PER_SECOND: u32 = SAMPLE_RATE * NUM_CHANNELS as u32; From 3b64f2528641dae97ef2f25f917c3cb40264dee2 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 20:29:22 -0500 Subject: [PATCH 05/53] Add resampler --- playback/src/config.rs | 8 +- playback/src/lib.rs | 1 + playback/src/resampler.rs | 551 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+), 4 deletions(-) create mode 100644 playback/src/resampler.rs diff --git a/playback/src/config.rs b/playback/src/config.rs index a9a47e7dc..f90cd4cee 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,7 +1,7 @@ use std::{mem, str::FromStr, time::Duration}; 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. 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 // 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 = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ48000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = @@ -182,8 +182,8 @@ impl std::fmt::Display for SampleRate { #[derive(Clone, Copy, Debug, Default)] pub struct ResampleSpec { - resample_factor_reciprocal: f64, - interpolation_output_size: usize, + pub resample_factor_reciprocal: f64, + pub interpolation_output_size: usize, } impl SampleRate { diff --git a/playback/src/lib.rs b/playback/src/lib.rs index 48fdb1057..2e67796c5 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -12,6 +12,7 @@ pub mod decoder; pub mod dither; pub mod mixer; pub mod player; +pub mod resampler; pub const RESAMPLER_INPUT_SIZE: usize = 147; pub const SAMPLE_RATE: u32 = 44100; diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs new file mode 100644 index 000000000..3c1392b35 --- /dev/null +++ b/playback/src/resampler.rs @@ -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, + 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, + 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>; +} + +struct MonoSincResampler { + interpolator: WindowedSincInterpolator, + input_buffer: Vec, + 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> { + 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, + 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> { + 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), +} + +enum ResampleResult { + Latency(u64), + ProcessedSamples(Option>), +} + +struct ResampleWorker { + task_sender: Option>, + result_receiver: Option>, + handle: Option>, +} + +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 [{name}] thread: {e}"), + None => error!("Error in 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 [{name}] thread"), + None => debug!("drop thread"), + } + + break; + } + }, + } + }) { + Ok(handle) => { + debug!("Created [{name}] thread"); + handle + } + Err(e) => { + error!("Error creating [{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) { + self.task_sender + .as_mut() + .and_then(|sender| sender.send(ResampleTask::ProcessSamples(samples)).ok()); + } + + fn receive_result(&mut self) -> Option> { + 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 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> { + 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 { + // 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, Vec) { + // Split the stereo interleaved samples into left and right channels. + let (left_samples, right_samples): (Vec, Vec) = samples + .chunks(2) + .map(|chunk| { + let [left_sample, right_sample] = [chunk[0], chunk[1]]; + (left_sample, right_sample) + }) + .unzip(); + + (left_samples, right_samples) + } +} From 375f83797a5211d11b3342cbb2e36a086d6e5ef8 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 20:48:02 -0500 Subject: [PATCH 06/53] Add normaliser --- playback/src/lib.rs | 11 ++ playback/src/normaliser.rs | 343 +++++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 playback/src/normaliser.rs diff --git a/playback/src/lib.rs b/playback/src/lib.rs index 2e67796c5..d6b7ea3f3 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -11,12 +11,23 @@ pub mod convert; pub mod decoder; pub mod dither; pub mod mixer; +pub mod normaliser; pub mod player; pub mod resampler; +pub const DB_VOLTAGE_RATIO: f64 = 20.0; +pub const PCM_AT_0DBFS: f64 = 1.0; pub const RESAMPLER_INPUT_SIZE: usize = 147; pub const SAMPLE_RATE: u32 = 44100; pub const NUM_CHANNELS: u8 = 2; pub const SAMPLES_PER_SECOND: u32 = SAMPLE_RATE * NUM_CHANNELS as u32; pub const PAGES_PER_MS: f64 = SAMPLE_RATE as f64 / 1000.0; pub const MS_PER_PAGE: f64 = 1000.0 / SAMPLE_RATE as f64; + +pub fn db_to_ratio(db: f64) -> f64 { + f64::powf(10.0, db / DB_VOLTAGE_RATIO) +} + +pub fn ratio_to_db(ratio: f64) -> f64 { + ratio.log10() * DB_VOLTAGE_RATIO +} diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs new file mode 100644 index 000000000..d08577383 --- /dev/null +++ b/playback/src/normaliser.rs @@ -0,0 +1,343 @@ +use crate::{ + config::{NormalisationMethod, NormalisationType, PlayerConfig}, + db_to_ratio, + decoder::AudioPacket, + mixer::VolumeGetter, + player::NormalisationData, + ratio_to_db, PCM_AT_0DBFS, +}; + +struct NoNormalisation; + +impl NoNormalisation { + fn normalise(samples: &[f64], volume: f64) -> Vec { + if volume < 1.0 { + let mut output = Vec::with_capacity(samples.len()); + + output.extend(samples.iter().map(|sample| sample * volume)); + + output + } else { + samples.to_vec() + } + } +} + +struct BasicNormalisation; + +impl BasicNormalisation { + fn normalise(samples: &[f64], volume: f64, factor: f64) -> Vec { + if volume < 1.0 || factor < 1.0 { + let mut output = Vec::with_capacity(samples.len()); + + output.extend(samples.iter().map(|sample| sample * factor * volume)); + + output + } else { + samples.to_vec() + } + } +} + +#[derive(PartialEq)] +struct DynamicNormalisation { + threshold_db: f64, + attack_cf: f64, + release_cf: f64, + knee_db: f64, + integrator: f64, + peak: f64, +} + +impl DynamicNormalisation { + fn new(config: &PlayerConfig) -> Self { + // as_millis() has rounding errors (truncates) + debug!( + "Normalisation Attack: {:.0} ms", + config + .sample_rate + .normalisation_coefficient_to_duration(config.normalisation_attack_cf) + .as_secs_f64() + * 1000. + ); + + debug!( + "Normalisation Release: {:.0} ms", + config + .sample_rate + .normalisation_coefficient_to_duration(config.normalisation_release_cf) + .as_secs_f64() + * 1000. + ); + + Self { + threshold_db: config.normalisation_threshold_dbfs, + attack_cf: config.normalisation_attack_cf, + release_cf: config.normalisation_release_cf, + knee_db: config.normalisation_knee_db, + integrator: 0.0, + peak: 0.0, + } + } + + fn stop(&mut self) { + self.integrator = 0.0; + self.peak = 0.0; + } + + fn normalise(&mut self, samples: &[f64], volume: f64, factor: f64) -> Vec { + let mut output = Vec::with_capacity(samples.len()); + + output.extend(samples.iter().map(|sample| { + let mut sample = sample * factor; + + // Feedforward limiter in the log domain + // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic + // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio + // Engineering Society, 60, 399-408. + + // Some tracks have samples that are precisely 0.0. That's silence + // and we know we don't need to limit that, in which we can spare + // the CPU cycles. + // + // Also, calling `ratio_to_db(0.0)` returns `inf` and would get the + // peak detector stuck. Also catch the unlikely case where a sample + // is decoded as `NaN` or some other non-normal value. + let limiter_db = if sample.is_normal() { + // step 1-4: half-wave rectification and conversion into dB + // and gain computer with soft knee and subtractor + let bias_db = ratio_to_db(sample.abs()) - self.threshold_db; + let knee_boundary_db = bias_db * 2.0; + + if knee_boundary_db < -self.knee_db { + 0.0 + } else if knee_boundary_db.abs() <= self.knee_db { + // The textbook equation: + // ratio_to_db(sample.abs()) - (ratio_to_db(sample.abs()) - (bias_db + knee_db / 2.0).powi(2) / (2.0 * knee_db)) + // Simplifies to: + // ((2.0 * bias_db) + knee_db).powi(2) / (8.0 * knee_db) + // Which in our case further simplifies to: + // (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) + // because knee_boundary_db is 2.0 * bias_db. + (knee_boundary_db + self.knee_db).powi(2) / (8.0 * self.knee_db) + } else { + // Textbook: + // ratio_to_db(sample.abs()) - threshold_db, which is already our bias_db. + bias_db + } + } else { + 0.0 + }; + + // Spare the CPU unless (1) the limiter is engaged, (2) we + // were in attack or (3) we were in release, and that attack/ + // release wasn't finished yet. + if limiter_db > 0.0 || self.integrator > 0.0 || self.peak > 0.0 { + // step 5: smooth, decoupled peak detector + // Textbook: + // release_cf * integrator + (1.0 - release_cf) * limiter_db + // Simplifies to: + // release_cf * integrator - release_cf * limiter_db + limiter_db + self.integrator = limiter_db.max( + self.release_cf * self.integrator - self.release_cf * limiter_db + limiter_db, + ); + // Textbook: + // attack_cf * peak + (1.0 - attack_cf) * integrator + // Simplifies to: + // attack_cf * peak - attack_cf * integrator + integrator + self.peak = + self.attack_cf * self.peak - self.attack_cf * self.integrator + self.integrator; + + // step 6: make-up gain applied later (volume attenuation) + // Applying the standard normalisation factor here won't work, + // because there are tracks with peaks as high as 6 dB above + // the default threshold, so that would clip. + + // steps 7-8: conversion into level and multiplication into gain stage + sample *= db_to_ratio(-self.peak); + } + + sample * volume + })); + + output + } +} + +#[derive(PartialEq)] +enum Normalisation { + None, + Basic, + Dynamic(DynamicNormalisation), +} + +impl Normalisation { + fn new(config: &PlayerConfig) -> Self { + if !config.normalisation { + Normalisation::None + } else { + debug!("Normalisation Type: {:?}", config.normalisation_type); + debug!( + "Normalisation Pregain: {:.1} dB", + config.normalisation_pregain_db + ); + + debug!( + "Normalisation Threshold: {:.1} dBFS", + config.normalisation_threshold_dbfs + ); + + debug!("Normalisation Method: {:?}", config.normalisation_method); + + match config.normalisation_method { + NormalisationMethod::Dynamic => { + Normalisation::Dynamic(DynamicNormalisation::new(config)) + } + NormalisationMethod::Basic => Normalisation::Basic, + } + } + } + + fn stop(&mut self) { + if let Normalisation::Dynamic(ref mut d) = self { + d.stop() + } + } + + fn normalise(&mut self, samples: &[f64], volume: f64, factor: f64) -> Vec { + use Normalisation::*; + + match self { + None => NoNormalisation::normalise(samples, volume), + Basic => BasicNormalisation::normalise(samples, volume, factor), + Dynamic(ref mut d) => d.normalise(samples, volume, factor), + } + } +} + +pub struct Normaliser { + normalisation: Normalisation, + volume_getter: Box, + normalisation_type: NormalisationType, + pregain_db: f64, + threshold_dbfs: f64, + factor: f64, +} + +impl Normaliser { + pub fn new(config: &PlayerConfig, volume_getter: Box) -> Self { + Self { + normalisation: Normalisation::new(config), + volume_getter, + normalisation_type: config.normalisation_type, + pregain_db: config.normalisation_pregain_db, + threshold_dbfs: config.normalisation_threshold_dbfs, + factor: 1.0, + } + } + + pub fn normalise(&mut self, samples: &[f64]) -> AudioPacket { + let volume = self.volume_getter.attenuation_factor(); + + AudioPacket::Samples(self.normalisation.normalise(samples, volume, self.factor)) + } + + pub fn stop(&mut self) { + self.normalisation.stop(); + } + + pub fn set_factor(&mut self, auto_normalise_as_album: bool, data: NormalisationData) { + if self.normalisation != Normalisation::None { + self.factor = self.get_factor(auto_normalise_as_album, data); + } + } + + fn get_factor(&self, auto_normalise_as_album: bool, data: NormalisationData) -> f64 { + let (gain_db, gain_peak, norm_type) = match self.normalisation_type { + NormalisationType::Album => ( + data.album_gain_db, + data.album_peak, + NormalisationType::Album, + ), + NormalisationType::Track => ( + data.track_gain_db, + data.track_peak, + NormalisationType::Track, + ), + NormalisationType::Auto => { + if auto_normalise_as_album { + ( + data.album_gain_db, + data.album_peak, + NormalisationType::Album, + ) + } else { + ( + data.track_gain_db, + data.track_peak, + NormalisationType::Track, + ) + } + } + }; + + // As per the ReplayGain 1.0 & 2.0 (proposed) spec: + // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Clipping_prevention + // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Clipping_prevention + let normalisation_factor = if self.normalisation == Normalisation::Basic { + // For Basic Normalisation, factor = min(ratio of (ReplayGain + PreGain), 1.0 / peak level). + // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Peak_amplitude + // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Peak_amplitude + // We then limit that to 1.0 as not to exceed dBFS (0.0 dB). + let factor = f64::min( + db_to_ratio(gain_db + self.pregain_db), + PCM_AT_0DBFS / gain_peak, + ); + + if factor > PCM_AT_0DBFS { + info!( + "Lowering gain by {:.2} dB for the duration of this track to avoid potentially exceeding dBFS.", + ratio_to_db(factor) + ); + + PCM_AT_0DBFS + } else { + factor + } + } else { + // For Dynamic Normalisation it's up to the player to decide, + // factor = ratio of (ReplayGain + PreGain). + // We then let the dynamic limiter handle gain reduction. + let factor = db_to_ratio(gain_db + self.pregain_db); + let threshold_ratio = db_to_ratio(self.threshold_dbfs); + + if factor > PCM_AT_0DBFS { + let factor_db = gain_db + self.pregain_db; + let limiting_db = factor_db + self.threshold_dbfs.abs(); + + warn!( + "This track may exceed dBFS by {:.2} dB and be subject to {:.2} dB of dynamic limiting at it's peak.", + factor_db, limiting_db + ); + } else if factor > threshold_ratio { + let limiting_db = gain_db + self.pregain_db + self.threshold_dbfs.abs(); + + info!( + "This track may be subject to {:.2} dB of dynamic limiting at it's peak.", + limiting_db + ); + } + + factor + }; + + debug!("Normalisation Data: {:?}", data); + debug!( + "Calculated Normalisation Factor for {:?}: {:.2}%", + norm_type, + normalisation_factor * 100.0 + ); + + normalisation_factor + } +} From 9861a582a6d3710f2e2eb3cd1c8ad6b066b091e2 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 20:53:01 -0500 Subject: [PATCH 07/53] Add sample_pipeline --- playback/src/lib.rs | 1 + playback/src/sample_pipeline.rs | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 playback/src/sample_pipeline.rs diff --git a/playback/src/lib.rs b/playback/src/lib.rs index d6b7ea3f3..e56b2cae5 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -14,6 +14,7 @@ pub mod mixer; pub mod normaliser; pub mod player; pub mod resampler; +pub mod sample_pipeline; pub const DB_VOLTAGE_RATIO: f64 = 20.0; pub const PCM_AT_0DBFS: f64 = 1.0; diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs new file mode 100644 index 000000000..4be796cee --- /dev/null +++ b/playback/src/sample_pipeline.rs @@ -0,0 +1,78 @@ +use crate::{ + audio_backend::{Sink, SinkResult}, + config::PlayerConfig, + convert::Converter, + decoder::AudioPacket, + mixer::VolumeGetter, + normaliser::Normaliser, + player::NormalisationData, + resampler::StereoInterleavedResampler, +}; + +pub struct SamplePipeline { + resampler: StereoInterleavedResampler, + normaliser: Normaliser, + converter: Converter, + sink: Box, +} + +impl SamplePipeline { + pub fn new( + config: &PlayerConfig, + sink: Box, + volume_getter: Box, + ) -> Self { + let resampler = + StereoInterleavedResampler::new(config.sample_rate, config.interpolation_quality); + + let normaliser = Normaliser::new(config, volume_getter); + let converter = Converter::new(config.ditherer); + + Self { + resampler, + normaliser, + converter, + sink, + } + } + + pub fn get_latency_pcm(&mut self) -> u64 { + self.sink.get_latency_pcm() + self.resampler.get_latency_pcm() + } + + pub fn start(&mut self) -> SinkResult<()> { + self.sink.start()?; + + Ok(()) + } + + pub fn stop(&mut self) -> SinkResult<()> { + self.resampler.stop(); + self.normaliser.stop(); + self.sink.stop()?; + + Ok(()) + } + + pub fn set_normalisation_factor( + &mut self, + auto_normalise_as_album: bool, + data: NormalisationData, + ) { + self.normaliser.set_factor(auto_normalise_as_album, data); + } + + pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> { + if let AudioPacket::Samples(samples) = packet { + self.resampler + .process(&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(()) + } +} From 3bcf5498d2267880f7a857c37965b5759561d8d4 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 21:11:37 -0500 Subject: [PATCH 08/53] Convert get_latency_pcm to get_latency_ms in sample_pipeline --- playback/src/audio_backend/pulseaudio.rs | 2 +- playback/src/resampler.rs | 3 +-- playback/src/sample_pipeline.rs | 7 +++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index b1b93822b..35d25f38d 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -137,7 +137,7 @@ impl Sink for PulseAudioSink { .and_then(|sink| { sink.get_latency() .ok() - .map(|micro_sec| (micro_sec.as_secs_f64() * SAMPLE_RATE as f64).round() as u64) + .map(|micro_sec| (micro_sec.as_secs_f64() * SAMPLE_RATE as f64) as u64) }) .unwrap_or(0) } diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 3c1392b35..f9da63b0f 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -119,8 +119,7 @@ impl MonoResampler for MonoSincResampler { let delay_line_latency = (interpolation_quality.get_interpolation_coefficients_length() as f64 - * spec.resample_factor_reciprocal) - .round() as u64; + * spec.resample_factor_reciprocal) as u64; Self { interpolator: WindowedSincInterpolator::new( diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index 4be796cee..34a71fc15 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -1,4 +1,5 @@ use crate::{ + MS_PER_PAGE, audio_backend::{Sink, SinkResult}, config::PlayerConfig, convert::Converter, @@ -36,8 +37,10 @@ impl SamplePipeline { } } - pub fn get_latency_pcm(&mut self) -> u64 { - self.sink.get_latency_pcm() + self.resampler.get_latency_pcm() + pub 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<()> { From e1ea400220e1ad3d0f859ab5d51292ada625cfaf Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 22:17:41 -0500 Subject: [PATCH 09/53] Change the backends so that they support the diffrent sample rates --- playback/src/audio_backend/alsa.rs | 623 +++++++++++------------ playback/src/audio_backend/gstreamer.rs | 8 +- playback/src/audio_backend/jackaudio.rs | 4 +- playback/src/audio_backend/mod.rs | 12 +- playback/src/audio_backend/pipe.rs | 4 +- playback/src/audio_backend/portaudio.rs | 25 +- playback/src/audio_backend/pulseaudio.rs | 89 ++-- playback/src/audio_backend/rodio.rs | 17 +- playback/src/audio_backend/sdl.rs | 6 +- playback/src/audio_backend/subprocess.rs | 4 +- 10 files changed, 391 insertions(+), 401 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index b647b1e4b..9a8721e09 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -2,41 +2,56 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE}; +use crate::{NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; use alsa::device_name::HintIter; use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, ValueOr}; use std::process::exit; use thiserror::Error; -const MAX_BUFFER: Frames = (SAMPLE_RATE / 2) as Frames; -const MIN_BUFFER: Frames = (SAMPLE_RATE / 10) as Frames; -const ZERO_FRAMES: Frames = 0; +const OPTIMAL_NUM_PERIODS: Frames = 5; +const MIN_NUM_PERIODS: Frames = 2; -const MAX_PERIOD_DIVISOR: Frames = 4; -const MIN_PERIOD_DIVISOR: Frames = 10; +const COMMON_SAMPLE_RATES: [u32; 14] = [ + 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, + 768000, +]; + +const SUPPORTED_SAMPLE_RATES: [u32; 4] = [44100, 48000, 88200, 96000]; + +const FORMATS: [AudioFormat; 6] = [ + AudioFormat::S16, + AudioFormat::S24, + AudioFormat::S24_3, + AudioFormat::S32, + AudioFormat::F32, + AudioFormat::F64, +]; #[derive(Debug, Error)] enum AlsaError { - #[error(" Device {device} Unsupported Format {alsa_format:?} ({format:?}), {e}")] + #[error(" Device {device} Unsupported Format {alsa_format} ({format:?}), {e}, Supported Format(s): {supported_formats:?}")] UnsupportedFormat { device: String, alsa_format: Format, format: AudioFormat, + supported_formats: Vec, e: alsa::Error, }, - #[error(" Device {device} Unsupported Channel Count {channel_count}, {e}")] + #[error(" Device {device} Unsupported Channel Count {channel_count}, {e}, Supported Channel Count(s): {supported_channel_counts:?}")] UnsupportedChannelCount { device: String, channel_count: u8, + supported_channel_counts: Vec, e: alsa::Error, }, - #[error(" Device {device} Unsupported Sample Rate {samplerate}, {e}")] + #[error(" Device {device} Unsupported Sample Rate {samplerate}, {e}, Supported Sample Rate(s): {supported_rates:?}")] UnsupportedSampleRate { device: String, samplerate: u32, + supported_rates: Vec, e: alsa::Error, }, @@ -63,9 +78,6 @@ enum AlsaError { #[error(" Could Not Parse Output Name(s) and/or Description(s), {0}")] Parsing(alsa::Error), - - #[error("")] - NotConnected, } impl From for SinkError { @@ -75,7 +87,6 @@ impl From for SinkError { match e { DrainFailure(_) | OnWrite(_) => SinkError::OnWrite(es), PcmSetUp { .. } => SinkError::ConnectionRefused(es), - NotConnected => SinkError::NotConnected(es), _ => SinkError::InvalidParams(es), } } @@ -98,6 +109,8 @@ impl From for Format { pub struct AlsaSink { pcm: Option, format: AudioFormat, + sample_rate: u32, + latency_scale_factor: f64, device: String, period_buffer: Vec, } @@ -106,302 +119,100 @@ fn list_compatible_devices() -> SinkResult<()> { let i = HintIter::new_str(None, "pcm").map_err(AlsaError::Parsing)?; println!("\n\n\tCompatible alsa device(s):\n"); - println!("\t------------------------------------------------------\n"); + println!("\t--------------------------------------------------------------------\n"); for a in i { if let Some(Direction::Playback) = a.direction { if let Some(name) = a.name { - if let Ok(pcm) = PCM::new(&name, Direction::Playback, false) { - if let Ok(hwp) = HwParams::any(&pcm) { - // Only show devices that support - // 2 ch 44.1 Interleaved. - - if hwp.set_access(Access::RWInterleaved).is_ok() - && hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest).is_ok() - && hwp.set_channels(NUM_CHANNELS as u32).is_ok() - { - let mut supported_formats = vec![]; - - for f in &[ - AudioFormat::S16, - AudioFormat::S24, - AudioFormat::S24_3, - AudioFormat::S32, - AudioFormat::F32, - AudioFormat::F64, - ] { - if hwp.test_format(Format::from(*f)).is_ok() { - supported_formats.push(format!("{f:?}")); + // surround* outputs throw: + // ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map + if name.contains(':') && !name.starts_with("surround") { + if let Ok(pcm) = PCM::new(&name, Direction::Playback, false) { + if let Ok(hwp) = HwParams::any(&pcm) { + if hwp.set_access(Access::RWInterleaved).is_ok() + && hwp.set_channels(NUM_CHANNELS as u32).is_ok() + { + let mut supported_formats_and_samplerates = String::new(); + + for format in FORMATS.iter() { + let hwp = hwp.clone(); + + if hwp.set_format((*format).into()).is_ok() { + let sample_rates: Vec = SUPPORTED_SAMPLE_RATES + .iter() + .filter_map(|sample_rate| { + let hwp = hwp.clone(); + if hwp + .set_rate(*sample_rate, ValueOr::Nearest) + .is_ok() + { + match *sample_rate { + 44100 => Some("44.1kHz".to_string()), + 48000 => Some("48kHz".to_string()), + 88200 => Some("88.2kHz".to_string()), + 96000 => Some("96kHz".to_string()), + _ => None, + } + } else { + None + } + }) + .collect(); + + if !sample_rates.is_empty() { + let format_and_sample_rates = + if *format == AudioFormat::S24_3 { + format!( + "\n\t\tFormat: {format:?} Sample Rate(s): {}", + sample_rates.join(", ") + ) + } else { + format!( + "\n\t\tFormat: {format:?} Sample Rate(s): {}", + sample_rates.join(", ") + ) + }; + + supported_formats_and_samplerates + .push_str(&format_and_sample_rates); + } + } } - } - - if !supported_formats.is_empty() { - println!("\tDevice:\n\n\t\t{name}\n"); - - println!( - "\tDescription:\n\n\t\t{}\n", - a.desc.unwrap_or_default().replace('\n', "\n\t\t") - ); - - println!( - "\tSupported Format(s):\n\n\t\t{}\n", - supported_formats.join(" ") - ); - - println!( - "\t------------------------------------------------------\n" - ); - } - } - }; - } - } - } - } - - Ok(()) -} - -fn open_device(dev_name: &str, format: AudioFormat) -> SinkResult<(PCM, usize)> { - let pcm = PCM::new(dev_name, Direction::Playback, false).map_err(|e| AlsaError::PcmSetUp { - device: dev_name.to_string(), - e, - })?; - - let bytes_per_period = { - let hwp = HwParams::any(&pcm).map_err(AlsaError::HwParams)?; - - hwp.set_access(Access::RWInterleaved) - .map_err(|e| AlsaError::UnsupportedAccessType { - device: dev_name.to_string(), - e, - })?; - - let alsa_format = Format::from(format); - - hwp.set_format(alsa_format) - .map_err(|e| AlsaError::UnsupportedFormat { - device: dev_name.to_string(), - alsa_format, - format, - e, - })?; - - hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest).map_err(|e| { - AlsaError::UnsupportedSampleRate { - device: dev_name.to_string(), - samplerate: SAMPLE_RATE, - e, - } - })?; - - hwp.set_channels(NUM_CHANNELS as u32) - .map_err(|e| AlsaError::UnsupportedChannelCount { - device: dev_name.to_string(), - channel_count: NUM_CHANNELS, - e, - })?; - - // Clone the hwp while it's in - // a good working state so that - // in the event of an error setting - // the buffer and period sizes - // we can use the good working clone - // instead of the hwp that's in an - // error state. - let hwp_clone = hwp.clone(); - - // At a sampling rate of 44100: - // The largest buffer is 22050 Frames (500ms) with 5512 Frame periods (125ms). - // The smallest buffer is 4410 Frames (100ms) with 441 Frame periods (10ms). - // Actual values may vary. - // - // Larger buffer and period sizes are preferred as extremely small values - // will cause high CPU useage. - // - // If no buffer or period size is in those ranges or an error happens - // trying to set the buffer or period size use the device's defaults - // which may not be ideal but are *hopefully* serviceable. - - let buffer_size = { - let max = match hwp.get_buffer_size_max() { - Err(e) => { - trace!("Error getting the device's max Buffer size: {}", e); - ZERO_FRAMES - } - Ok(s) => s, - }; - - let min = match hwp.get_buffer_size_min() { - Err(e) => { - trace!("Error getting the device's min Buffer size: {}", e); - ZERO_FRAMES - } - Ok(s) => s, - }; - - let buffer_size = if min < max { - match (MIN_BUFFER..=MAX_BUFFER) - .rev() - .find(|f| (min..=max).contains(f)) - { - Some(size) => { - trace!("Desired Frames per Buffer: {:?}", size); - - match hwp.set_buffer_size_near(size) { - Err(e) => { - trace!("Error setting the device's Buffer size: {}", e); - ZERO_FRAMES - } - Ok(s) => s, - } - } - None => { - trace!("No Desired Buffer size in range reported by the device."); - ZERO_FRAMES - } - } - } else { - trace!("The device's min reported Buffer size was greater than or equal to it's max reported Buffer size."); - ZERO_FRAMES - }; - if buffer_size == ZERO_FRAMES { - trace!( - "Desired Buffer Frame range: {:?} - {:?}", - MIN_BUFFER, - MAX_BUFFER - ); - - trace!( - "Actual Buffer Frame range as reported by the device: {:?} - {:?}", - min, - max - ); - } + if !supported_formats_and_samplerates.is_empty() { + println!("\tDevice:\n\n\t\t{name}\n"); - buffer_size - }; + println!( + "\tDescription:\n\n\t\t{}\n", + a.desc.unwrap_or_default().replace('\n', "\n\t\t") + ); - let period_size = { - if buffer_size == ZERO_FRAMES { - ZERO_FRAMES - } else { - let max = match hwp.get_period_size_max() { - Err(e) => { - trace!("Error getting the device's max Period size: {}", e); - ZERO_FRAMES - } - Ok(s) => s, - }; + println!("\tSupported Format & Sample Rate Combinations:\n{supported_formats_and_samplerates}\n"); - let min = match hwp.get_period_size_min() { - Err(e) => { - trace!("Error getting the device's min Period size: {}", e); - ZERO_FRAMES - } - Ok(s) => s, - }; - - let max_period = buffer_size / MAX_PERIOD_DIVISOR; - let min_period = buffer_size / MIN_PERIOD_DIVISOR; - - let period_size = if min < max && min_period < max_period { - match (min_period..=max_period) - .rev() - .find(|f| (min..=max).contains(f)) - { - Some(size) => { - trace!("Desired Frames per Period: {:?}", size); - - match hwp.set_period_size_near(size, ValueOr::Nearest) { - Err(e) => { - trace!("Error setting the device's Period size: {}", e); - ZERO_FRAMES + println!( + "\t--------------------------------------------------------------------\n" + ); } - Ok(s) => s, } - } - None => { - trace!("No Desired Period size in range reported by the device."); - ZERO_FRAMES - } + }; } - } else { - trace!("The device's min reported Period size was greater than or equal to it's max reported Period size,"); - trace!("or the desired min Period size was greater than or equal to the desired max Period size."); - ZERO_FRAMES - }; - - if period_size == ZERO_FRAMES { - trace!("Buffer size: {:?}", buffer_size); - - trace!( - "Desired Period Frame range: {:?} (Buffer size / {:?}) - {:?} (Buffer size / {:?})", - min_period, - MIN_PERIOD_DIVISOR, - max_period, - MAX_PERIOD_DIVISOR, - ); - - trace!( - "Actual Period Frame range as reported by the device: {:?} - {:?}", - min, - max - ); } - - period_size } - }; - - if buffer_size == ZERO_FRAMES || period_size == ZERO_FRAMES { - trace!( - "Failed to set Buffer and/or Period size, falling back to the device's defaults." - ); - - trace!("You may experience higher than normal CPU usage and/or audio issues."); - - pcm.hw_params(&hwp_clone).map_err(AlsaError::Pcm)?; - } else { - pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?; } + } - let hwp = pcm.hw_params_current().map_err(AlsaError::Pcm)?; - - // Don't assume we got what we wanted. Ask to make sure. - let frames_per_period = hwp.get_period_size().map_err(AlsaError::HwParams)?; - - let frames_per_buffer = hwp.get_buffer_size().map_err(AlsaError::HwParams)?; - - let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?; - - swp.set_start_threshold(frames_per_buffer - frames_per_period) - .map_err(AlsaError::SwParams)?; - - pcm.sw_params(&swp).map_err(AlsaError::Pcm)?; - - trace!("Actual Frames per Buffer: {:?}", frames_per_buffer); - trace!("Actual Frames per Period: {:?}", frames_per_period); - - // Let ALSA do the math for us. - pcm.frames_to_bytes(frames_per_period) as usize - }; - - trace!("Period Buffer size in bytes: {:?}", bytes_per_period); - - Ok((pcm, bytes_per_period)) + Ok(()) } impl Open for AlsaSink { - fn open(device: Option, format: AudioFormat) -> Self { + fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { let name = match device.as_deref() { Some("?") => match list_compatible_devices() { Ok(_) => { exit(0); } Err(e) => { - error!("{}", e); + error!("{e}"); exit(1); } }, @@ -410,11 +221,15 @@ impl Open for AlsaSink { } .to_string(); - info!("Using AlsaSink with format: {:?}", format); + let latency_scale_factor = DECODER_SAMPLE_RATE as f64 / sample_rate as f64; + + info!("Using AlsaSink with format: {format:?}, sample rate: {sample_rate}"); Self { pcm: None, format, + sample_rate, + latency_scale_factor, device: name, period_buffer: vec![], } @@ -424,32 +239,19 @@ impl Open for AlsaSink { impl Sink for AlsaSink { fn start(&mut self) -> SinkResult<()> { if self.pcm.is_none() { - let (pcm, bytes_per_period) = open_device(&self.device, self.format)?; - self.pcm = Some(pcm); - - if self.period_buffer.capacity() != bytes_per_period { - self.period_buffer = Vec::with_capacity(bytes_per_period); - } - - // Should always match the "Period Buffer size in bytes: " trace! message. - trace!( - "Period Buffer capacity: {:?}", - self.period_buffer.capacity() - ); + self.open_device()?; } Ok(()) } fn stop(&mut self) -> SinkResult<()> { - if self.pcm.is_some() { - // Zero fill the remainder of the period buffer and - // write any leftover data before draining the actual PCM buffer. - self.period_buffer.resize(self.period_buffer.capacity(), 0); - self.write_buf()?; - - let pcm = self.pcm.take().ok_or(AlsaError::NotConnected)?; + // Zero fill the remainder of the period buffer and + // write any leftover data before draining the actual PCM buffer. + self.period_buffer.resize(self.period_buffer.capacity(), 0); + self.write_buf()?; + if let Some(pcm) = self.pcm.take() { pcm.drain().map_err(AlsaError::DrainFailure)?; } @@ -458,6 +260,7 @@ impl Sink for AlsaSink { fn get_latency_pcm(&mut self) -> u64 { let buffer_len = self.period_buffer.len(); + let latency_scale_factor = self.latency_scale_factor; self.pcm .as_mut() @@ -467,7 +270,9 @@ impl Sink for AlsaSink { let frames_in_buffer = pcm.bytes_to_frames(buffer_len as isize); - (delay_frames + frames_in_buffer) as u64 + let total_frames = (delay_frames + frames_in_buffer) as f64; + + (total_frames * latency_scale_factor) as u64 }) }) .unwrap_or(0) @@ -507,33 +312,203 @@ impl SinkAsBytes for AlsaSink { impl AlsaSink { pub const NAME: &'static str = "alsa"; - fn write_buf(&mut self) -> SinkResult<()> { - if self.pcm.is_some() { - let write_result = { - let pcm = self.pcm.as_mut().ok_or(AlsaError::NotConnected)?; - - match pcm.io_bytes().writei(&self.period_buffer) { - Ok(_) => Ok(()), - Err(e) => { - // Capture and log the original error as a warning, and then try to recover. - // If recovery fails then forward that error back to player. - warn!( - "Error writing from AlsaSink buffer to PCM, trying to recover, {}", - e - ); - - pcm.try_recover(e, false).map_err(AlsaError::OnWrite) + fn set_period_and_buffer_size( + hwp: &HwParams, + optimal_buffer_size: Frames, + optimal_period_size: Frames, + ) -> bool { + let period_size = match hwp.set_period_size_near(optimal_period_size, ValueOr::Nearest) { + Ok(period_size) => { + if period_size > 0 { + trace!("Closest Supported Period Size to Optimal ({optimal_period_size}): {period_size}"); + period_size + } else { + trace!("Error getting Period Size, Period Size must be greater than 0, falling back to the device's default Buffer parameters"); + 0 + } + } + Err(e) => { + trace!("Error getting Period Size: {e}, falling back to the device's default Buffer parameters"); + 0 + } + }; + + if period_size > 0 { + let buffer_size = match hwp + .set_buffer_size_near((period_size * OPTIMAL_NUM_PERIODS).max(optimal_buffer_size)) + { + Ok(buffer_size) => { + if buffer_size >= period_size * MIN_NUM_PERIODS { + trace!("Closest Supported Buffer Size to Optimal ({optimal_buffer_size}): {buffer_size}"); + buffer_size + } else { + trace!("Error getting Buffer Size, Buffer Size must be at least {period_size} * {MIN_NUM_PERIODS}, falling back to the device's default Buffer parameters"); + 0 } } + Err(e) => { + trace!("Error getting Buffer Size: {e}, falling back to the device's default Buffer parameters"); + 0 + } }; - if let Err(e) = write_result { - self.pcm = None; - return Err(e.into()); + return buffer_size > 0; + } + + false + } + + fn open_device(&mut self) -> SinkResult<()> { + let optimal_buffer_size: Frames = self.sample_rate as Frames / 2; + let optimal_period_size: Frames = self.sample_rate as Frames / 10; + + let pcm = PCM::new(&self.device, Direction::Playback, false).map_err(|e| { + AlsaError::PcmSetUp { + device: self.device.clone(), + e, + } + })?; + + { + let hwp = HwParams::any(&pcm).map_err(AlsaError::HwParams)?; + + hwp.set_access(Access::RWInterleaved).map_err(|e| { + AlsaError::UnsupportedAccessType { + device: self.device.clone(), + e, + } + })?; + + let alsa_format = self.format.into(); + + hwp.set_format(alsa_format).map_err(|e| { + let supported_formats = FORMATS + .iter() + .filter_map(|f| { + if hwp.test_format((*f).into()).is_ok() { + Some(format!("{f:?}")) + } else { + None + } + }) + .collect(); + + AlsaError::UnsupportedFormat { + device: self.device.clone(), + alsa_format, + format: self.format, + supported_formats, + e, + } + })?; + + hwp.set_rate(self.sample_rate, ValueOr::Nearest) + .map_err(|e| { + let supported_rates = (hwp.get_rate_min().unwrap_or_default() + ..=hwp.get_rate_max().unwrap_or_default()) + .filter(|r| COMMON_SAMPLE_RATES.contains(r) && hwp.test_rate(*r).is_ok()) + .collect(); + + AlsaError::UnsupportedSampleRate { + device: self.device.clone(), + samplerate: self.sample_rate, + supported_rates, + e, + } + })?; + + hwp.set_channels(NUM_CHANNELS as u32).map_err(|e| { + let supported_channel_counts = (hwp.get_channels_min().unwrap_or_default() + ..=hwp.get_channels_max().unwrap_or_default()) + .filter(|c| hwp.test_channels(*c).is_ok()) + .collect(); + + AlsaError::UnsupportedChannelCount { + device: self.device.clone(), + channel_count: NUM_CHANNELS, + supported_channel_counts, + e, + } + })?; + + // Calculate a buffer and period size as close + // to optimal as possible. + + // hwp continuity is very important. + let hwp_clone = hwp.clone(); + + if Self::set_period_and_buffer_size( + &hwp_clone, + optimal_buffer_size, + optimal_period_size, + ) { + pcm.hw_params(&hwp_clone).map_err(AlsaError::Pcm)?; + } else { + pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?; + } + + let hwp = pcm.hw_params_current().map_err(AlsaError::Pcm)?; + + // Don't assume we got what we wanted. Ask to make sure. + let buffer_size = hwp.get_buffer_size().map_err(AlsaError::HwParams)?; + + let period_size = hwp.get_period_size().map_err(AlsaError::HwParams)?; + + let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?; + + swp.set_start_threshold(buffer_size - period_size) + .map_err(AlsaError::SwParams)?; + + pcm.sw_params(&swp).map_err(AlsaError::Pcm)?; + + if buffer_size != optimal_buffer_size { + trace!("A Buffer Size of {buffer_size} Frames is Suboptimal"); + + if buffer_size < optimal_buffer_size { + trace!("A smaller than necessary Buffer Size can lead to Buffer underruns (audio glitches) and high CPU usage."); + } else { + trace!("A larger than necessary Buffer Size can lead to perceivable latency (lag)."); + } + } + + let optimal_period_size = buffer_size / OPTIMAL_NUM_PERIODS; + + if period_size != optimal_period_size { + trace!("A Period Size of {period_size} Frames is Suboptimal"); + + if period_size < optimal_period_size { + trace!("A smaller than necessary Period Size relative to Buffer Size can lead to high CPU usage."); + } else { + trace!("A larger than necessary Period Size relative to Buffer Size can lessen Buffer underrun (audio glitch) protection."); + } + } + + // Let ALSA do the math for us. + let bytes_per_period = pcm.frames_to_bytes(period_size) as usize; + + trace!("Period Buffer size in bytes: {bytes_per_period}"); + + self.period_buffer = Vec::with_capacity(bytes_per_period); + } + + self.pcm = Some(pcm); + + Ok(()) + } + + fn write_buf(&mut self) -> SinkResult<()> { + if let Some(pcm) = self.pcm.as_mut() { + if let Err(e) = pcm.io_bytes().writei(&self.period_buffer) { + // Capture and log the original error as a warning, and then try to recover. + // If recovery fails then forward that error back to player. + warn!("Error writing from AlsaSink Buffer to PCM, trying to recover, {e}"); + + pcm.try_recover(e, false).map_err(AlsaError::OnWrite)?; } } self.period_buffer.clear(); + Ok(()) } } diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index e3cc78cf4..5651211e4 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -14,7 +14,7 @@ use std::sync::Arc; use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::{ - config::AudioFormat, convert::Converter, decoder::AudioPacket, NUM_CHANNELS, SAMPLE_RATE, + config::AudioFormat, convert::Converter, decoder::AudioPacket, NUM_CHANNELS, }; pub struct GstreamerSink { @@ -26,8 +26,8 @@ pub struct GstreamerSink { } impl Open for GstreamerSink { - fn open(device: Option, format: AudioFormat) -> Self { - info!("Using GStreamer sink with format: {format:?}"); + fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { + info!("Using GStreamer sink with format: {format:?}, sample rate: {sample_rate}"); gst::init().expect("failed to init GStreamer!"); let gst_format = match format { @@ -39,7 +39,7 @@ impl Open for GstreamerSink { AudioFormat::S16 => gst_audio::AUDIO_FORMAT_S16, }; - let gst_info = gst_audio::AudioInfo::builder(gst_format, SAMPLE_RATE, NUM_CHANNELS as u32) + let gst_info = gst_audio::AudioInfo::builder(gst_format, sample_rate, NUM_CHANNELS as u32) .build() .expect("Failed to create GStreamer audio format"); let gst_caps = gst_info.to_caps().expect("Failed to create GStreamer caps"); diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 9d40ee82c..180040e66 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -38,11 +38,11 @@ impl ProcessHandler for JackData { } impl Open for JackSink { - fn open(client_name: Option, format: AudioFormat) -> Self { + fn open(client_name: Option, format: AudioFormat, sample_rate: u32) -> Self { if format != AudioFormat::F32 { warn!("JACK currently does not support {format:?} output"); } - info!("Using JACK sink with format {:?}", AudioFormat::F32); + info!("Using JACK sink with format {:?}, sample rate: {sample_rate}", AudioFormat::F32); let client_name = client_name.unwrap_or_else(|| "librespot".to_string()); let (client, _status) = diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 05916fa65..fc39162fa 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -20,7 +20,7 @@ pub enum SinkError { pub type SinkResult = Result; pub trait Open { - fn open(_: Option, format: AudioFormat) -> Self; + fn open(_: Option, format: AudioFormat, sample_rate: u32) -> Self; } pub trait Sink { @@ -36,14 +36,18 @@ pub trait Sink { fn write(&mut self, packet: AudioPacket, converter: &mut Converter) -> SinkResult<()>; } -pub type SinkBuilder = fn(Option, AudioFormat) -> Box; +pub type SinkBuilder = fn(Option, AudioFormat, u32) -> Box; pub trait SinkAsBytes { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()>; } -fn mk_sink(device: Option, format: AudioFormat) -> Box { - Box::new(S::open(device, format)) +fn mk_sink( + device: Option, + format: AudioFormat, + sample_rate: u32, +) -> Box { + Box::new(S::open(device, format, sample_rate)) } // reuse code for various backends diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index e0e8a77c3..05db49534 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -42,13 +42,13 @@ pub struct StdoutSink { } impl Open for StdoutSink { - fn open(file: Option, format: AudioFormat) -> Self { + fn open(file: Option, format: AudioFormat, sample_rate: u32) -> Self { if let Some("?") = file.as_deref() { println!("\nUsage:\n\nOutput to stdout:\n\n\t--backend pipe\n\nOutput to file:\n\n\t--backend pipe --device {{filename}}\n"); exit(0); } - info!("Using StdoutSink (pipe) with format: {:?}", format); + info!("Using StdoutSink (pipe) with format: {format:?}, sample rate: {sample_rate}"); Self { output: None, diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index c44245cfe..eb455b0fc 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -12,14 +12,17 @@ pub enum PortAudioSink<'a> { F32( Option>, StreamParameters, + f64, ), S32( Option>, StreamParameters, + f64, ), S16( Option>, StreamParameters, + f64, ), } @@ -51,8 +54,8 @@ fn find_output(device: &str) -> Option { } impl<'a> Open for PortAudioSink<'a> { - fn open(device: Option, format: AudioFormat) -> PortAudioSink<'a> { - info!("Using PortAudio sink with format: {format:?}"); + fn open(device: Option, format: AudioFormat, sample_rate: u32) -> PortAudioSink<'a> { + info!("Using PortAudio sink with format: {format:?}, sample rate: {sample_rate}"); portaudio_rs::initialize().unwrap(); @@ -80,13 +83,13 @@ impl<'a> Open for PortAudioSink<'a> { suggested_latency: latency, data: 0.0 as $type, }; - $sink(None, params) + $sink(None, params, sample_rate) }}; } match format { - AudioFormat::F32 => open_sink!(Self::F32, f32), - AudioFormat::S32 => open_sink!(Self::S32, i32), - AudioFormat::S16 => open_sink!(Self::S16, i16), + AudioFormat::F32 => open_sink!(Self::F32, f32, sample_rate as f64), + AudioFormat::S32 => open_sink!(Self::S32, i32, sample_rate as f64), + AudioFormat::S16 => open_sink!(Self::S16, i16, sample_rate as f64), _ => { unimplemented!("PortAudio currently does not support {format:?} output") } @@ -97,13 +100,13 @@ impl<'a> Open for PortAudioSink<'a> { impl<'a> Sink for PortAudioSink<'a> { fn start(&mut self) -> SinkResult<()> { macro_rules! start_sink { - (ref mut $stream: ident, ref $parameters: ident) => {{ + (ref mut $stream: ident, ref $parameters: ident, ref $sample_rate: ident ) => {{ if $stream.is_none() { *$stream = Some( Stream::open( None, Some(*$parameters), - SAMPLE_RATE as f64, + *$sample_rate, FRAMES_PER_BUFFER_UNSPECIFIED, StreamFlags::DITHER_OFF, // no need to dither twice; use librespot dithering instead None, @@ -116,9 +119,9 @@ impl<'a> Sink for PortAudioSink<'a> { } match self { - Self::F32(stream, parameters) => start_sink!(ref mut stream, ref parameters), - Self::S32(stream, parameters) => start_sink!(ref mut stream, ref parameters), - Self::S16(stream, parameters) => start_sink!(ref mut stream, ref parameters), + Self::F32(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), + Self::S32(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), + Self::S16(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), }; Ok(()) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 35d25f38d..d60351e92 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -2,7 +2,7 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE}; +use crate::{NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; use libpulse_binding::{self as pulse, error::PAErr, stream::Direction}; use libpulse_simple_binding::Simple; use std::env; @@ -24,9 +24,6 @@ enum PulseError { #[error(" Failed to Drain Pulseaudio Buffer, {0}")] DrainFailure(PAErr), - #[error("")] - NotConnected, - #[error(" {0}")] OnWrite(PAErr), } @@ -38,40 +35,63 @@ impl From for SinkError { match e { DrainFailure(_) | OnWrite(_) => SinkError::OnWrite(es), ConnectionRefused(_) => SinkError::ConnectionRefused(es), - NotConnected => SinkError::NotConnected(es), InvalidSampleSpec { .. } => SinkError::InvalidParams(es), } } } +impl From for pulse::sample::Format { + fn from(f: AudioFormat) -> pulse::sample::Format { + use AudioFormat::*; + match f { + F64 | F32 => pulse::sample::Format::FLOAT32NE, + S32 => pulse::sample::Format::S32NE, + S24 => pulse::sample::Format::S24_32NE, + S24_3 => pulse::sample::Format::S24NE, + S16 => pulse::sample::Format::S16NE, + } + } +} + pub struct PulseAudioSink { sink: Option, device: Option, app_name: String, stream_desc: String, format: AudioFormat, + sample_rate: u32, + + sample_spec: pulse::sample::Spec, } impl Open for PulseAudioSink { - fn open(device: Option, format: AudioFormat) -> Self { + fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { let app_name = env::var("PULSE_PROP_application.name").unwrap_or_default(); let stream_desc = env::var("PULSE_PROP_stream.description").unwrap_or_default(); - let mut actual_format = format; - - if actual_format == AudioFormat::F64 { + let format = if format == AudioFormat::F64 { warn!("PulseAudio currently does not support F64 output"); - actual_format = AudioFormat::F32; - } + AudioFormat::F32 + } else { + format + }; + + info!("Using PulseAudioSink with format: {format:?}, sample rate: {sample_rate}"); - info!("Using PulseAudioSink with format: {actual_format:?}"); + let sample_spec = pulse::sample::Spec { + format: format.into(), + channels: NUM_CHANNELS, + rate: sample_rate, + }; Self { sink: None, device, app_name, stream_desc, - format: actual_format, + format, + sample_rate, + sample_spec, } } } @@ -79,31 +99,15 @@ impl Open for PulseAudioSink { impl Sink for PulseAudioSink { fn start(&mut self) -> SinkResult<()> { if self.sink.is_none() { - // PulseAudio calls S24 and S24_3 different from the rest of the world - let pulse_format = match self.format { - AudioFormat::F32 => pulse::sample::Format::FLOAT32NE, - AudioFormat::S32 => pulse::sample::Format::S32NE, - AudioFormat::S24 => pulse::sample::Format::S24_32NE, - AudioFormat::S24_3 => pulse::sample::Format::S24NE, - AudioFormat::S16 => pulse::sample::Format::S16NE, - _ => unreachable!(), - }; - - let sample_spec = pulse::sample::Spec { - format: pulse_format, - channels: NUM_CHANNELS, - rate: SAMPLE_RATE, - }; - - if !sample_spec.is_valid() { + if !self.sample_spec.is_valid() { let pulse_error = PulseError::InvalidSampleSpec { - pulse_format, + pulse_format: self.sample_spec.format, format: self.format, channels: NUM_CHANNELS, - rate: SAMPLE_RATE, + rate: self.sample_rate, }; - return Err(SinkError::from(pulse_error)); + return Err(pulse_error.into()); } let sink = Simple::new( @@ -112,7 +116,7 @@ impl Sink for PulseAudioSink { Direction::Playback, // Direction. self.device.as_deref(), // Our device (sink) name. &self.stream_desc, // Description of our stream. - &sample_spec, // Our sample format. + &self.sample_spec, // Our sample format. None, // Use default channel map. None, // Use default buffering attributes. ) @@ -125,9 +129,10 @@ impl Sink for PulseAudioSink { } fn stop(&mut self) -> SinkResult<()> { - let sink = self.sink.take().ok_or(PulseError::NotConnected)?; + if let Some(sink) = self.sink.take() { + sink.drain().map_err(PulseError::DrainFailure)?; + } - sink.drain().map_err(PulseError::DrainFailure)?; Ok(()) } @@ -135,9 +140,9 @@ impl Sink for PulseAudioSink { self.sink .as_mut() .and_then(|sink| { - sink.get_latency() - .ok() - .map(|micro_sec| (micro_sec.as_secs_f64() * SAMPLE_RATE as f64) as u64) + sink.get_latency().ok().map(|micro_sec| { + (micro_sec.as_secs_f64() * DECODER_SAMPLE_RATE as f64).round() as u64 + }) }) .unwrap_or(0) } @@ -147,9 +152,9 @@ impl Sink for PulseAudioSink { impl SinkAsBytes for PulseAudioSink { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> { - let sink = self.sink.as_mut().ok_or(PulseError::NotConnected)?; - - sink.write(data).map_err(PulseError::OnWrite)?; + if let Some(sink) = self.sink.as_mut() { + sink.write(data).map_err(PulseError::OnWrite)?; + } Ok(()) } diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 2632f54a5..4b63d3522 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -9,7 +9,7 @@ use super::{Sink, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE}; +use crate::NUM_CHANNELS; #[cfg(all( feature = "rodiojack-backend", @@ -18,16 +18,17 @@ use crate::{NUM_CHANNELS, SAMPLE_RATE}; compile_error!("Rodio JACK backend is currently only supported on linux."); #[cfg(feature = "rodio-backend")] -pub fn mk_rodio(device: Option, format: AudioFormat) -> Box { - Box::new(open(cpal::default_host(), device, format)) +pub fn mk_rodio(device: Option, format: AudioFormat, sample_rate: u32) -> Box { + Box::new(open(cpal::default_host(), device, format, sample_rate)) } #[cfg(feature = "rodiojack-backend")] -pub fn mk_rodiojack(device: Option, format: AudioFormat) -> Box { +pub fn mk_rodiojack(device: Option, format: AudioFormat, sample_rate: u32) -> Box { Box::new(open( cpal::host_from_id(cpal::HostId::Jack).unwrap(), device, format, + sample_rate, )) } @@ -62,6 +63,7 @@ impl From for SinkError { pub struct RodioSink { rodio_sink: rodio::Sink, format: AudioFormat, + sample_rate: u32, _stream: rodio::OutputStream, } @@ -164,7 +166,7 @@ fn create_sink( Ok((sink, stream)) } -pub fn open(host: cpal::Host, device: Option, format: AudioFormat) -> RodioSink { +pub fn open(host: cpal::Host, device: Option, format: AudioFormat, sample_rate: u32) -> RodioSink { info!( "Using Rodio sink with format {format:?} and cpal host: {}", host.id().name() @@ -180,6 +182,7 @@ pub fn open(host: cpal::Host, device: Option, format: AudioFormat) -> Ro RodioSink { rodio_sink: sink, format, + sample_rate, _stream: stream, } } @@ -205,7 +208,7 @@ impl Sink for RodioSink { let samples_f32: &[f32] = &converter.f64_to_f32(samples); let source = rodio::buffer::SamplesBuffer::new( NUM_CHANNELS as u16, - SAMPLE_RATE, + self.sample_rate, samples_f32, ); self.rodio_sink.append(source); @@ -214,7 +217,7 @@ impl Sink for RodioSink { let samples_s16: &[i16] = &converter.f64_to_s16(samples); let source = rodio::buffer::SamplesBuffer::new( NUM_CHANNELS as u16, - SAMPLE_RATE, + self.sample_rate, samples_s16, ); self.rodio_sink.append(source); diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 0d2209282..ce7c146b9 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -14,8 +14,8 @@ pub enum SdlSink { } impl Open for SdlSink { - fn open(device: Option, format: AudioFormat) -> Self { - info!("Using SDL sink with format: {:?}", format); + fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { + info!("Using SDL sink with format: {format:?}, sample rate: {sample_rate}"); if device.is_some() { warn!("SDL sink does not support specifying a device name"); @@ -27,7 +27,7 @@ impl Open for SdlSink { .expect("could not initialize SDL audio subsystem"); let desired_spec = AudioSpecDesired { - freq: Some(SAMPLE_RATE as i32), + freq: Some(sample_rate as i32), channels: Some(NUM_CHANNELS), samples: None, }; diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 6ce545dac..15778dbef 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -66,13 +66,13 @@ pub struct SubprocessSink { } impl Open for SubprocessSink { - fn open(shell_command: Option, format: AudioFormat) -> Self { + fn open(shell_command: Option, format: AudioFormat, sample_rate: u32) -> Self { if let Some("?") = shell_command.as_deref() { println!("\nUsage:\n\nOutput to a Subprocess:\n\n\t--backend subprocess --device {{shell_command}}\n"); exit(0); } - info!("Using SubprocessSink with format: {:?}", format); + info!("Using SubprocessSink with format: {format:?}, sample rate: {sample_rate}"); Self { shell_command, From efec96b9cc2f8dab49b67aeae62c8dcd2a44331f Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 21 Jun 2023 23:32:03 -0500 Subject: [PATCH 10/53] Put it all together --- playback/src/audio_backend/gstreamer.rs | 4 +- playback/src/audio_backend/jackaudio.rs | 5 +- playback/src/audio_backend/portaudio.rs | 12 +- playback/src/audio_backend/rodio.rs | 13 +- playback/src/config.rs | 18 +- playback/src/mixer/alsamixer.rs | 2 +- playback/src/mixer/mappings.rs | 2 +- playback/src/mixer/mod.rs | 4 +- playback/src/mixer/softmixer.rs | 2 +- playback/src/player.rs | 298 ++---------------------- playback/src/sample_pipeline.rs | 2 +- src/main.rs | 104 +++++++-- 12 files changed, 152 insertions(+), 314 deletions(-) diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 5651211e4..ec748a982 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -13,9 +13,7 @@ use std::sync::Arc; use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::{ - config::AudioFormat, convert::Converter, decoder::AudioPacket, NUM_CHANNELS, -}; +use crate::{config::AudioFormat, convert::Converter, decoder::AudioPacket, NUM_CHANNELS}; pub struct GstreamerSink { appsrc: gst_app::AppSrc, diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 180040e66..1122a427b 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -42,7 +42,10 @@ impl Open for JackSink { if format != AudioFormat::F32 { warn!("JACK currently does not support {format:?} output"); } - info!("Using JACK sink with format {:?}, sample rate: {sample_rate}", AudioFormat::F32); + info!( + "Using JACK sink with format {:?}, sample rate: {sample_rate}", + AudioFormat::F32 + ); let client_name = client_name.unwrap_or_else(|| "librespot".to_string()); let (client, _status) = diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index eb455b0fc..f1978a0d6 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -119,9 +119,15 @@ impl<'a> Sink for PortAudioSink<'a> { } match self { - Self::F32(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), - Self::S32(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), - Self::S16(stream, parameters, sample_rate) => start_sink!(ref mut stream, ref parameters, ref sample_rate), + Self::F32(stream, parameters, sample_rate) => { + start_sink!(ref mut stream, ref parameters, ref sample_rate) + } + Self::S32(stream, parameters, sample_rate) => { + start_sink!(ref mut stream, ref parameters, ref sample_rate) + } + Self::S16(stream, parameters, sample_rate) => { + start_sink!(ref mut stream, ref parameters, ref sample_rate) + } }; Ok(()) diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 4b63d3522..05a060cbe 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -23,7 +23,11 @@ pub fn mk_rodio(device: Option, format: AudioFormat, sample_rate: u32) - } #[cfg(feature = "rodiojack-backend")] -pub fn mk_rodiojack(device: Option, format: AudioFormat, sample_rate: u32) -> Box { +pub fn mk_rodiojack( + device: Option, + format: AudioFormat, + sample_rate: u32, +) -> Box { Box::new(open( cpal::host_from_id(cpal::HostId::Jack).unwrap(), device, @@ -166,7 +170,12 @@ fn create_sink( Ok((sink, stream)) } -pub fn open(host: cpal::Host, device: Option, format: AudioFormat, sample_rate: u32) -> RodioSink { +pub fn open( + host: cpal::Host, + device: Option, + format: AudioFormat, + sample_rate: u32, +) -> RodioSink { info!( "Using Rodio sink with format {format:?} and cpal host: {}", host.id().name() diff --git a/playback/src/config.rs b/playback/src/config.rs index f90cd4cee..952e43bfb 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,7 +1,7 @@ use std::{mem, str::FromStr, time::Duration}; pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer}; -use crate::{convert::i24, player::duration_to_coefficient, RESAMPLER_INPUT_SIZE, SAMPLE_RATE}; +use crate::{convert::i24, 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; @@ -152,10 +152,12 @@ impl FromStr for SampleRate { fn from_str(s: &str) -> Result { use SampleRate::*; + let lowercase_input = s.to_lowercase(); + // Match against both the actual // stringified value and how most // humans would write a sample rate. - match s.to_uppercase().as_ref() { + match lowercase_input.as_str() { "hz44100" | "44100hz" | "44100" | "44.1khz" => Ok(Hz44100), "hz48000" | "48000hz" | "48000" | "48khz" => Ok(Hz48000), "hz88200" | "88200hz" | "88200" | "88.2khz" => Ok(Hz88200), @@ -348,6 +350,9 @@ pub struct PlayerConfig { pub gapless: bool, pub passthrough: bool, + pub interpolation_quality: InterpolationQuality, + pub sample_rate: SampleRate, + pub normalisation: bool, pub normalisation_type: NormalisationType, pub normalisation_method: NormalisationMethod, @@ -368,12 +373,17 @@ impl Default for PlayerConfig { bitrate: Bitrate::default(), gapless: true, normalisation: false, + interpolation_quality: InterpolationQuality::default(), + sample_rate: SampleRate::default(), normalisation_type: NormalisationType::default(), normalisation_method: NormalisationMethod::default(), normalisation_pregain_db: 0.0, normalisation_threshold_dbfs: -2.0, - normalisation_attack_cf: duration_to_coefficient(Duration::from_millis(5)), - normalisation_release_cf: duration_to_coefficient(Duration::from_millis(100)), + // Dummy value. We can't use the default because + // no matter what it's dependent on the sample rate. + normalisation_attack_cf: 0.0, + // Same with release. + normalisation_release_cf: 0.0, normalisation_knee_db: 5.0, passthrough: false, ditherer: Some(mk_ditherer::), diff --git a/playback/src/mixer/alsamixer.rs b/playback/src/mixer/alsamixer.rs index 52be10852..17f996d87 100644 --- a/playback/src/mixer/alsamixer.rs +++ b/playback/src/mixer/alsamixer.rs @@ -1,4 +1,4 @@ -use crate::player::{db_to_ratio, ratio_to_db}; +use crate::{db_to_ratio, ratio_to_db}; use super::mappings::{LogMapping, MappedCtrl, VolumeMapping}; use super::{Mixer, MixerConfig, VolumeCtrl}; diff --git a/playback/src/mixer/mappings.rs b/playback/src/mixer/mappings.rs index 736b3c3f7..38290d5ee 100644 --- a/playback/src/mixer/mappings.rs +++ b/playback/src/mixer/mappings.rs @@ -1,5 +1,5 @@ use super::VolumeCtrl; -use crate::player::db_to_ratio; +use crate::db_to_ratio; pub trait MappedCtrl { fn to_mapped(&self, volume: u16) -> f64; diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index 0a8b8d6c1..2d89d30e9 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -13,12 +13,12 @@ pub trait Mixer: Send { fn set_volume(&self, volume: u16); fn volume(&self) -> u16; - fn get_soft_volume(&self) -> Box { + fn get_soft_volume(&self) -> Box { Box::new(NoOpVolume) } } -pub trait VolumeGetter { +pub trait VolumeGetter: Send { fn attenuation_factor(&self) -> f64; } diff --git a/playback/src/mixer/softmixer.rs b/playback/src/mixer/softmixer.rs index 061f39b94..2f7d21f76 100644 --- a/playback/src/mixer/softmixer.rs +++ b/playback/src/mixer/softmixer.rs @@ -35,7 +35,7 @@ impl Mixer for SoftMixer { .store(mapped_volume.to_bits(), Ordering::Relaxed) } - fn get_soft_volume(&self) -> Box { + fn get_soft_volume(&self) -> Box { Box::new(SoftVolume(self.volume.clone())) } } diff --git a/playback/src/player.rs b/playback/src/player.rs index 96bd70817..f50e72ef9 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -29,22 +29,18 @@ use crate::{ READ_AHEAD_DURING_PLAYBACK, }, audio_backend::Sink, - config::{Bitrate, NormalisationMethod, NormalisationType, PlayerConfig}, - convert::Converter, + config::{Bitrate, PlayerConfig}, core::{util::SeqGenerator, Error, Session, SpotifyId}, decoder::{AudioDecoder, AudioPacket, AudioPacketPosition, SymphoniaDecoder}, metadata::audio::{AudioFileFormat, AudioFiles, AudioItem}, mixer::VolumeGetter, + sample_pipeline::SamplePipeline, }; #[cfg(feature = "passthrough-decoder")] use crate::decoder::PassthroughDecoder; -use crate::SAMPLES_PER_SECOND; - const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; -pub const DB_VOLTAGE_RATIO: f64 = 20.0; -pub const PCM_AT_0DBFS: f64 = 1.0; // Spotify inserts a custom Ogg packet at the start with custom metadata values, that you would // otherwise expect in Vorbis comments. This packet isn't well-formed and players may balk at it. @@ -75,15 +71,10 @@ struct PlayerInternal { state: PlayerState, preload: PlayerPreload, - sink: Box, sink_status: SinkStatus, sink_event_callback: Option, - volume_getter: Box, + sample_pipeline: SamplePipeline, event_senders: Vec>, - converter: Converter, - - normalisation_integrator: f64, - normalisation_peak: f64, auto_normalise_as_album: bool, @@ -265,22 +256,6 @@ impl PlayerEvent { pub type PlayerEventChannel = mpsc::UnboundedReceiver; -pub fn db_to_ratio(db: f64) -> f64 { - f64::powf(10.0, db / DB_VOLTAGE_RATIO) -} - -pub fn ratio_to_db(ratio: f64) -> f64 { - ratio.log10() * DB_VOLTAGE_RATIO -} - -pub fn duration_to_coefficient(duration: Duration) -> f64 { - f64::exp(-1.0 / (duration.as_secs_f64() * SAMPLES_PER_SECOND as f64)) -} - -pub fn coefficient_to_duration(coefficient: f64) -> Duration { - Duration::from_secs_f64(-1.0 / f64::ln(coefficient) / SAMPLES_PER_SECOND as f64) -} - #[derive(Clone, Copy, Debug)] pub struct NormalisationData { // Spotify provides these as `f32`, but audio metadata can contain up to `f64`. @@ -335,86 +310,13 @@ impl NormalisationData { album_peak, }) } - - fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f64 { - if !config.normalisation { - return 1.0; - } - - let (gain_db, gain_peak) = if config.normalisation_type == NormalisationType::Album { - (data.album_gain_db, data.album_peak) - } else { - (data.track_gain_db, data.track_peak) - }; - - // As per the ReplayGain 1.0 & 2.0 (proposed) spec: - // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Clipping_prevention - // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Clipping_prevention - let normalisation_factor = if config.normalisation_method == NormalisationMethod::Basic { - // For Basic Normalisation, factor = min(ratio of (ReplayGain + PreGain), 1.0 / peak level). - // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Peak_amplitude - // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Peak_amplitude - // We then limit that to 1.0 as not to exceed dBFS (0.0 dB). - let factor = f64::min( - db_to_ratio(gain_db + config.normalisation_pregain_db), - PCM_AT_0DBFS / gain_peak, - ); - - if factor > PCM_AT_0DBFS { - info!( - "Lowering gain by {:.2} dB for the duration of this track to avoid potentially exceeding dBFS.", - ratio_to_db(factor) - ); - - PCM_AT_0DBFS - } else { - factor - } - } else { - // For Dynamic Normalisation it's up to the player to decide, - // factor = ratio of (ReplayGain + PreGain). - // We then let the dynamic limiter handle gain reduction. - let factor = db_to_ratio(gain_db + config.normalisation_pregain_db); - let threshold_ratio = db_to_ratio(config.normalisation_threshold_dbfs); - - if factor > PCM_AT_0DBFS { - let factor_db = gain_db + config.normalisation_pregain_db; - let limiting_db = factor_db + config.normalisation_threshold_dbfs.abs(); - - warn!( - "This track may exceed dBFS by {:.2} dB and be subject to {:.2} dB of dynamic limiting at it's peak.", - factor_db, limiting_db - ); - } else if factor > threshold_ratio { - let limiting_db = gain_db - + config.normalisation_pregain_db - + config.normalisation_threshold_dbfs.abs(); - - info!( - "This track may be subject to {:.2} dB of dynamic limiting at it's peak.", - limiting_db - ); - } - - factor - }; - - debug!("Normalisation Data: {:?}", data); - debug!( - "Calculated Normalisation Factor for {:?}: {:.2}%", - config.normalisation_type, - normalisation_factor * 100.0 - ); - - normalisation_factor - } } impl Player { pub fn new( config: PlayerConfig, session: Session, - volume_getter: Box, + volume_getter: Box, sink_builder: F, ) -> Self where @@ -422,32 +324,6 @@ impl Player { { let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); - if config.normalisation { - debug!("Normalisation Type: {:?}", config.normalisation_type); - debug!( - "Normalisation Pregain: {:.1} dB", - config.normalisation_pregain_db - ); - debug!( - "Normalisation Threshold: {:.1} dBFS", - config.normalisation_threshold_dbfs - ); - debug!("Normalisation Method: {:?}", config.normalisation_method); - - if config.normalisation_method == NormalisationMethod::Dynamic { - // as_millis() has rounding errors (truncates) - debug!( - "Normalisation Attack: {:.0} ms", - coefficient_to_duration(config.normalisation_attack_cf).as_secs_f64() * 1000. - ); - debug!( - "Normalisation Release: {:.0} ms", - coefficient_to_duration(config.normalisation_release_cf).as_secs_f64() * 1000. - ); - debug!("Normalisation Knee: {} dB", config.normalisation_knee_db); - } - } - let player_id = PLAYER_COUNTER.fetch_add(1, Ordering::AcqRel); let thread_name = format!("player:{}", player_id); @@ -455,7 +331,7 @@ impl Player { let builder = thread::Builder::new().name(thread_name.clone()); let handle = match builder.spawn(move || { - let converter = Converter::new(config.ditherer); + let sample_pipeline = SamplePipeline::new(&config, sink_builder(), volume_getter); let internal = PlayerInternal { session, @@ -465,15 +341,10 @@ impl Player { state: PlayerState::Stopped, preload: PlayerPreload::None, - sink: sink_builder(), sink_status: SinkStatus::Closed, sink_event_callback: None, - volume_getter, + sample_pipeline, event_senders: vec![], - converter, - - normalisation_peak: 0.0, - normalisation_integrator: 0.0, auto_normalise_as_album: false, @@ -685,7 +556,6 @@ enum PlayerState { decoder: Decoder, audio_item: AudioItem, normalisation_data: NormalisationData, - normalisation_factor: f64, stream_loader_controller: StreamLoaderController, bytes_per_second: usize, duration_ms: u32, @@ -699,7 +569,6 @@ enum PlayerState { decoder: Decoder, normalisation_data: NormalisationData, audio_item: AudioItem, - normalisation_factor: f64, stream_loader_controller: StreamLoaderController, bytes_per_second: usize, duration_ms: u32, @@ -810,7 +679,6 @@ impl PlayerState { decoder, audio_item, normalisation_data, - normalisation_factor, stream_loader_controller, duration_ms, bytes_per_second, @@ -824,7 +692,6 @@ impl PlayerState { decoder, audio_item, normalisation_data, - normalisation_factor, stream_loader_controller, duration_ms, bytes_per_second, @@ -855,7 +722,6 @@ impl PlayerState { decoder, audio_item, normalisation_data, - normalisation_factor, stream_loader_controller, duration_ms, bytes_per_second, @@ -870,7 +736,6 @@ impl PlayerState { decoder, audio_item, normalisation_data, - normalisation_factor, stream_loader_controller, duration_ms, bytes_per_second, @@ -1271,11 +1136,12 @@ impl Future for PlayerInternal { if self.state.is_playing() { self.ensure_sink_running(); + let sample_pipeline_latency_ms = self.sample_pipeline.get_latency_ms(); + if let PlayerState::Playing { track_id, play_request_id, ref mut decoder, - normalisation_factor, ref mut stream_position_ms, ref mut reported_nominal_start_time, .. @@ -1284,7 +1150,9 @@ impl Future for PlayerInternal { match decoder.next_packet() { Ok(result) => { if let Some((ref packet_position, ref packet)) = result { - let new_stream_position_ms = packet_position.position_ms; + let new_stream_position_ms = packet_position + .position_ms + .saturating_sub(sample_pipeline_latency_ms); let expected_position_ms = std::mem::replace( &mut *stream_position_ms, new_stream_position_ms, @@ -1357,7 +1225,7 @@ impl Future for PlayerInternal { } } - self.handle_packet(result, normalisation_factor); + self.handle_packet(result); } Err(e) => { error!("Skipping to next track, unable to get next packet for track <{:?}>: {:?}", track_id, e); @@ -1423,7 +1291,7 @@ impl PlayerInternal { if let Some(callback) = &mut self.sink_event_callback { callback(SinkStatus::Running); } - match self.sink.start() { + match self.sample_pipeline.start() { Ok(()) => self.sink_status = SinkStatus::Running, Err(e) => { error!("{}", e); @@ -1437,7 +1305,7 @@ impl PlayerInternal { match self.sink_status { SinkStatus::Running => { trace!("== Stopping sink =="); - match self.sink.stop() { + match self.sample_pipeline.stop() { Ok(()) => { self.sink_status = if temporarily { SinkStatus::TemporarilyClosed @@ -1557,132 +1425,16 @@ impl PlayerInternal { } } - fn handle_packet( - &mut self, - packet: Option<(AudioPacketPosition, AudioPacket)>, - normalisation_factor: f64, - ) { + fn handle_packet(&mut self, packet: Option<(AudioPacketPosition, AudioPacket)>) { match packet { - Some((_, mut packet)) => { + Some((_, packet)) => { if !packet.is_empty() { - if let AudioPacket::Samples(ref mut data) = packet { - // Get the volume for the packet. - // In the case of hardware volume control this will - // always be 1.0 (no change). - let volume = self.volume_getter.attenuation_factor(); - - // For the basic normalisation method, a normalisation factor of 1.0 indicates that - // there is nothing to normalise (all samples should pass unaltered). For the - // dynamic method, there may still be peaks that we want to shave off. - - // No matter the case we apply volume attenuation last if there is any. - if !self.config.normalisation { - if volume < 1.0 { - for sample in data.iter_mut() { - *sample *= volume; - } - } - } else if self.config.normalisation_method == NormalisationMethod::Basic - && (normalisation_factor < 1.0 || volume < 1.0) - { - for sample in data.iter_mut() { - *sample *= normalisation_factor * volume; - } - } else if self.config.normalisation_method == NormalisationMethod::Dynamic { - // zero-cost shorthands - let threshold_db = self.config.normalisation_threshold_dbfs; - let knee_db = self.config.normalisation_knee_db; - let attack_cf = self.config.normalisation_attack_cf; - let release_cf = self.config.normalisation_release_cf; - - for sample in data.iter_mut() { - *sample *= normalisation_factor; - - // Feedforward limiter in the log domain - // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic - // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio - // Engineering Society, 60, 399-408. - - // Some tracks have samples that are precisely 0.0. That's silence - // and we know we don't need to limit that, in which we can spare - // the CPU cycles. - // - // Also, calling `ratio_to_db(0.0)` returns `inf` and would get the - // peak detector stuck. Also catch the unlikely case where a sample - // is decoded as `NaN` or some other non-normal value. - let limiter_db = if sample.is_normal() { - // step 1-4: half-wave rectification and conversion into dB - // and gain computer with soft knee and subtractor - let bias_db = ratio_to_db(sample.abs()) - threshold_db; - let knee_boundary_db = bias_db * 2.0; - - if knee_boundary_db < -knee_db { - 0.0 - } else if knee_boundary_db.abs() <= knee_db { - // The textbook equation: - // ratio_to_db(sample.abs()) - (ratio_to_db(sample.abs()) - (bias_db + knee_db / 2.0).powi(2) / (2.0 * knee_db)) - // Simplifies to: - // ((2.0 * bias_db) + knee_db).powi(2) / (8.0 * knee_db) - // Which in our case further simplifies to: - // (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) - // because knee_boundary_db is 2.0 * bias_db. - (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) - } else { - // Textbook: - // ratio_to_db(sample.abs()) - threshold_db, which is already our bias_db. - bias_db - } - } else { - 0.0 - }; - - // Spare the CPU unless (1) the limiter is engaged, (2) we - // were in attack or (3) we were in release, and that attack/ - // release wasn't finished yet. - if limiter_db > 0.0 - || self.normalisation_integrator > 0.0 - || self.normalisation_peak > 0.0 - { - // step 5: smooth, decoupled peak detector - // Textbook: - // release_cf * self.normalisation_integrator + (1.0 - release_cf) * limiter_db - // Simplifies to: - // release_cf * self.normalisation_integrator - release_cf * limiter_db + limiter_db - self.normalisation_integrator = f64::max( - limiter_db, - release_cf * self.normalisation_integrator - - release_cf * limiter_db - + limiter_db, - ); - // Textbook: - // attack_cf * self.normalisation_peak + (1.0 - attack_cf) * self.normalisation_integrator - // Simplifies to: - // attack_cf * self.normalisation_peak - attack_cf * self.normalisation_integrator + self.normalisation_integrator - self.normalisation_peak = attack_cf * self.normalisation_peak - - attack_cf * self.normalisation_integrator - + self.normalisation_integrator; - - // step 6: make-up gain applied later (volume attenuation) - // Applying the standard normalisation factor here won't work, - // because there are tracks with peaks as high as 6 dB above - // the default threshold, so that would clip. - - // steps 7-8: conversion into level and multiplication into gain stage - *sample *= db_to_ratio(-self.normalisation_peak); - } - - *sample *= volume; - } - } - } - - if let Err(e) = self.sink.write(packet, &mut self.converter) { + if let Err(e) = self.sample_pipeline.write(packet) { error!("{}", e); self.handle_pause(); } } } - None => { self.state.playing_to_end_of_track(); if let PlayerState::EndOfTrack { @@ -1716,16 +1468,10 @@ impl PlayerInternal { let position_ms = loaded_track.stream_position_ms; - let mut config = self.config.clone(); - if config.normalisation_type == NormalisationType::Auto { - if self.auto_normalise_as_album { - config.normalisation_type = NormalisationType::Album; - } else { - config.normalisation_type = NormalisationType::Track; - } - }; - let normalisation_factor = - NormalisationData::get_factor(&config, loaded_track.normalisation_data); + self.sample_pipeline.set_normalisation_factor( + self.auto_normalise_as_album, + loaded_track.normalisation_data, + ); if start_playback { self.ensure_sink_running(); @@ -1741,7 +1487,6 @@ impl PlayerInternal { decoder: loaded_track.decoder, audio_item: loaded_track.audio_item, normalisation_data: loaded_track.normalisation_data, - normalisation_factor, stream_loader_controller: loaded_track.stream_loader_controller, duration_ms: loaded_track.duration_ms, bytes_per_second: loaded_track.bytes_per_second, @@ -1760,7 +1505,6 @@ impl PlayerInternal { decoder: loaded_track.decoder, audio_item: loaded_track.audio_item, normalisation_data: loaded_track.normalisation_data, - normalisation_factor, stream_loader_controller: loaded_track.stream_loader_controller, duration_ms: loaded_track.duration_ms, bytes_per_second: loaded_track.bytes_per_second, diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index 34a71fc15..e534bea71 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -1,5 +1,4 @@ use crate::{ - MS_PER_PAGE, audio_backend::{Sink, SinkResult}, config::PlayerConfig, convert::Converter, @@ -8,6 +7,7 @@ use crate::{ normaliser::Normaliser, player::NormalisationData, resampler::StereoInterleavedResampler, + MS_PER_PAGE, }; pub struct SamplePipeline { diff --git a/src/main.rs b/src/main.rs index 48edf1c95..24ab4b3c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,11 +24,12 @@ use librespot::{ playback::{ audio_backend::{self, SinkBuilder, BACKENDS}, config::{ - AudioFormat, Bitrate, NormalisationMethod, NormalisationType, PlayerConfig, VolumeCtrl, + AudioFormat, Bitrate, InterpolationQuality, NormalisationMethod, NormalisationType, + PlayerConfig, SampleRate, VolumeCtrl, }, dither, mixer::{self, MixerConfig, MixerFn}, - player::{coefficient_to_duration, duration_to_coefficient, Player}, + player::Player, }, }; @@ -239,6 +240,8 @@ fn get_setup() -> Setup { const VOLUME_RANGE: &str = "volume-range"; const ZEROCONF_PORT: &str = "zeroconf-port"; const ZEROCONF_INTERFACE: &str = "zeroconf-interface"; + const INTERPOLATION_QUALITY: &str = "interpolation-quality"; + const SAMPLE_RATE: &str = "sample-rate"; // Mostly arbitrary. const AP_PORT_SHORT: &str = "a"; @@ -576,6 +579,16 @@ fn get_setup() -> Setup { ZEROCONF_INTERFACE, "Comma-separated interface IP addresses on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD.", "IP" + ).optopt( + "", + INTERPOLATION_QUALITY, + "Interpolation Quality to use if Resampling {Low|Medium|High}. Defaults to Low.", + "QUALITY" + ).optopt( + "", + SAMPLE_RATE, + "Sample Rate to Resample to {44.1kHz|48kHz|88.2kHz|96kHz}. Defaults to 44.1kHz meaning no resampling.", + "SAMPLERATE" ); #[cfg(feature = "passthrough-decoder")] @@ -732,10 +745,18 @@ fn get_setup() -> Setup { let invalid_error_msg = |long: &str, short: &str, invalid: &str, valid_values: &str, default_value: &str| { - error!("Invalid `--{long}` / `-{short}`: \"{invalid}\""); + if short.is_empty() { + error!("Invalid `--{long}`: \"{invalid}\""); + } else { + error!("Invalid `--{long}` / `-{short}`: \"{invalid}\""); + } if !valid_values.is_empty() { - println!("Valid `--{long}` / `-{short}` values: {valid_values}"); + if short.is_empty() { + println!("Valid `--{long}` values: {valid_values}"); + } else { + println!("Valid `--{long}` / `-{short}` values: {valid_values}"); + } } if !default_value.is_empty() { @@ -761,6 +782,42 @@ fn get_setup() -> Setup { exit(1); }); + let interpolation_quality = opt_str(INTERPOLATION_QUALITY) + .as_deref() + .map(|interpolation_quality| { + InterpolationQuality::from_str(interpolation_quality).unwrap_or_else(|_| { + let default_value = &format!("{}", InterpolationQuality::default()); + invalid_error_msg( + INTERPOLATION_QUALITY, + "", + interpolation_quality, + "Low, Medium, High", + default_value, + ); + + exit(1); + }) + }) + .unwrap_or_default(); + + let sample_rate = opt_str(SAMPLE_RATE) + .as_deref() + .map(|sample_rate| { + SampleRate::from_str(sample_rate).unwrap_or_else(|_| { + let default_value = &format!("{}", SampleRate::default()); + invalid_error_msg( + SAMPLE_RATE, + "", + sample_rate, + "44.1kHz, 48kHz, 88.2kHz, 96kHz", + default_value, + ); + + exit(1); + }) + }) + .unwrap_or_default(); + let format = opt_str(FORMAT) .as_deref() .map(|format| { @@ -782,7 +839,7 @@ fn get_setup() -> Setup { let device = opt_str(DEVICE); if let Some(ref value) = device { if value == "?" { - backend(device, format); + backend(device, format, sample_rate.as_u32()); exit(0); } else if value.is_empty() { empty_string_error_msg(DEVICE, DEVICE_SHORT); @@ -1491,9 +1548,8 @@ fn get_setup() -> Setup { normalisation_attack_cf = opt_str(NORMALISATION_ATTACK) .map(|attack| match attack.parse::() { - Ok(value) if (VALID_NORMALISATION_ATTACK_RANGE).contains(&value) => { - duration_to_coefficient(Duration::from_millis(value)) - } + Ok(value) if (VALID_NORMALISATION_ATTACK_RANGE).contains(&value) => sample_rate + .duration_to_normalisation_coefficient(Duration::from_millis(value)), _ => { let valid_values = &format!( "{} - {}", @@ -1506,7 +1562,10 @@ fn get_setup() -> Setup { NORMALISATION_ATTACK_SHORT, &attack, valid_values, - &coefficient_to_duration(player_default_config.normalisation_attack_cf) + &sample_rate + .normalisation_coefficient_to_duration( + player_default_config.normalisation_attack_cf, + ) .as_millis() .to_string(), ); @@ -1514,12 +1573,15 @@ fn get_setup() -> Setup { exit(1); } }) - .unwrap_or(player_default_config.normalisation_attack_cf); + .unwrap_or( + sample_rate.duration_to_normalisation_coefficient(Duration::from_millis(5)), + ); normalisation_release_cf = opt_str(NORMALISATION_RELEASE) .map(|release| match release.parse::() { Ok(value) if (VALID_NORMALISATION_RELEASE_RANGE).contains(&value) => { - duration_to_coefficient(Duration::from_millis(value)) + sample_rate + .duration_to_normalisation_coefficient(Duration::from_millis(value)) } _ => { let valid_values = &format!( @@ -1533,17 +1595,20 @@ fn get_setup() -> Setup { NORMALISATION_RELEASE_SHORT, &release, valid_values, - &coefficient_to_duration( - player_default_config.normalisation_release_cf, - ) - .as_millis() - .to_string(), + &sample_rate + .normalisation_coefficient_to_duration( + player_default_config.normalisation_release_cf, + ) + .as_millis() + .to_string(), ); exit(1); } }) - .unwrap_or(player_default_config.normalisation_release_cf); + .unwrap_or( + sample_rate.duration_to_normalisation_coefficient(Duration::from_millis(100)), + ); normalisation_knee_db = opt_str(NORMALISATION_KNEE) .map(|knee| match knee.parse::() { @@ -1608,6 +1673,8 @@ fn get_setup() -> Setup { bitrate, gapless, passthrough, + interpolation_quality, + sample_rate, normalisation, normalisation_type, normalisation_method, @@ -1734,8 +1801,9 @@ async fn main() { let format = setup.format; let backend = setup.backend; let device = setup.device.clone(); + let sample_rate = player_config.sample_rate.as_u32(); let player = Player::new(player_config, session.clone(), soft_volume, move || { - (backend)(device, format) + (backend)(device, format, sample_rate) }); if let Some(player_event_program) = setup.player_event_program.clone() { From 2a8da828c4035fead5f42dded41875327c4e467d Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 22 Jun 2023 00:45:32 -0500 Subject: [PATCH 11/53] Fix clippy lint --- playback/src/audio_backend/portaudio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index f1978a0d6..4a07fb535 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -2,7 +2,7 @@ use super::{Open, Sink, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE}; +use crate::NUM_CHANNELS; use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo}; use portaudio_rs::stream::*; use std::process::exit; @@ -76,14 +76,14 @@ impl<'a> Open for PortAudioSink<'a> { }; macro_rules! open_sink { - ($sink: expr, $type: ty) => {{ + ($sink: expr, $type: ty, $sample_rate: ident) => {{ let params = StreamParameters { device: device_idx, channel_count: NUM_CHANNELS as u32, suggested_latency: latency, data: 0.0 as $type, }; - $sink(None, params, sample_rate) + $sink(None, params, $sample_rate) }}; } match format { @@ -141,9 +141,9 @@ impl<'a> Sink for PortAudioSink<'a> { }}; } match self { - Self::F32(stream, _) => stop_sink!(ref mut stream), - Self::S32(stream, _) => stop_sink!(ref mut stream), - Self::S16(stream, _) => stop_sink!(ref mut stream), + Self::F32(stream, _, _) => stop_sink!(ref mut stream), + Self::S32(stream, _, _) => stop_sink!(ref mut stream), + Self::S16(stream, _, _) => stop_sink!(ref mut stream), }; Ok(()) @@ -161,15 +161,15 @@ impl<'a> Sink for PortAudioSink<'a> { .map_err(|e| SinkError::OnWrite(e.to_string()))?; let result = match self { - Self::F32(stream, _parameters) => { + Self::F32(stream, _parameters, _sample_rate) => { let samples_f32: &[f32] = &converter.f64_to_f32(samples); write_sink!(ref mut stream, samples_f32) } - Self::S32(stream, _parameters) => { + Self::S32(stream, _parameters, _sample_rate) => { let samples_s32: &[i32] = &converter.f64_to_s32(samples); write_sink!(ref mut stream, samples_s32) } - Self::S16(stream, _parameters) => { + Self::S16(stream, _parameters, _sample_rate) => { let samples_s16: &[i16] = &converter.f64_to_s16(samples); write_sink!(ref mut stream, samples_s16) } From 586e9f1929b8f3c8c4cfd12333065f4c334680d9 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 22 Jun 2023 01:39:54 -0500 Subject: [PATCH 12/53] Fix clippy lint round 1000 + a small bug fix --- playback/src/audio_backend/portaudio.rs | 9 ++++++--- src/main.rs | 14 ++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 4a07fb535..178c8386a 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -86,10 +86,13 @@ impl<'a> Open for PortAudioSink<'a> { $sink(None, params, $sample_rate) }}; } + + let sample_rate = sample_rate as f64; + match format { - AudioFormat::F32 => open_sink!(Self::F32, f32, sample_rate as f64), - AudioFormat::S32 => open_sink!(Self::S32, i32, sample_rate as f64), - AudioFormat::S16 => open_sink!(Self::S16, i16, sample_rate as f64), + AudioFormat::F32 => open_sink!(Self::F32, f32, sample_rate), + AudioFormat::S32 => open_sink!(Self::S32, i32, sample_rate), + AudioFormat::S16 => open_sink!(Self::S16, i16, sample_rate), _ => { unimplemented!("PortAudio currently does not support {format:?} output") } diff --git a/src/main.rs b/src/main.rs index 24ab4b3c6..95e2f6eb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1562,12 +1562,7 @@ fn get_setup() -> Setup { NORMALISATION_ATTACK_SHORT, &attack, valid_values, - &sample_rate - .normalisation_coefficient_to_duration( - player_default_config.normalisation_attack_cf, - ) - .as_millis() - .to_string(), + "5", ); exit(1); @@ -1595,12 +1590,7 @@ fn get_setup() -> Setup { NORMALISATION_RELEASE_SHORT, &release, valid_values, - &sample_rate - .normalisation_coefficient_to_duration( - player_default_config.normalisation_release_cf, - ) - .as_millis() - .to_string(), + "100", ); exit(1); From b5e0ea2bd308239b2f5f98b0dd4e4971a53818f7 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 22 Jun 2023 02:06:24 -0500 Subject: [PATCH 13/53] Fix examples and update change log --- CHANGELOG.md | 4 ++++ examples/play.rs | 3 ++- examples/play_connect.rs | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de0ad1fc..7a2d91e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ https://github.com/librespot-org/librespot - [playback] The passthrough decoder is now feature-gated (breaking) - [playback] `rodio`: call play and pause - [protocol] protobufs have been updated +- [playback] Moved audio processing with the exception of decoding out of `player` (breaking) ### Added @@ -98,6 +99,8 @@ https://github.com/librespot-org/librespot - [playback] Add metadata support via a `TrackChanged` event - [connect] Add `activate` and `load` functions to `Spirc`, allowing control over local connect sessions - [metadata] Add `Lyrics` +- [playback] Add `normaliser`, `resampler` and `sample_pipeline`. +- [playback] Add resampling support to 48kHz, 88.2kHz, and 96kHz. ### Fixed @@ -115,6 +118,7 @@ https://github.com/librespot-org/librespot - [playback] Handle seek, pause, and play commands while loading - [playback] Handle disabled normalisation correctly when using fixed volume - [metadata] Fix missing colon when converting named spotify IDs to URIs +- [playback] Better thread handling in `player`. ## [0.4.2] - 2022-07-29 diff --git a/examples/play.rs b/examples/play.rs index eb7dc3826..a3196fcfd 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -20,6 +20,7 @@ async fn main() { let session_config = SessionConfig::default(); let player_config = PlayerConfig::default(); let audio_format = AudioFormat::default(); + let sample_rate = player_config.sample_rate.as_u32(); let args: Vec<_> = env::args().collect(); if args.len() != 4 { @@ -41,7 +42,7 @@ async fn main() { } let mut player = Player::new(player_config, session, Box::new(NoOpVolume), move || { - backend(None, audio_format) + backend(None, audio_format, sample_rate) }); player.load(track, true, 0); diff --git a/examples/play_connect.rs b/examples/play_connect.rs index 2b23a7d3f..f7a84d43b 100644 --- a/examples/play_connect.rs +++ b/examples/play_connect.rs @@ -25,6 +25,7 @@ async fn main() { let player_config = PlayerConfig::default(); let audio_format = AudioFormat::default(); let connect_config = ConnectConfig::default(); + let sample_rate = player_config.sample_rate.as_u32(); let mut args: Vec<_> = env::args().collect(); let context_uri = if args.len() == 4 { @@ -46,7 +47,7 @@ async fn main() { player_config, session.clone(), Box::new(NoOpVolume), - move || backend(None, audio_format), + move || backend(None, audio_format, sample_rate), ); let (spirc, spirc_task) = Spirc::new( From 09bd1bd324a0f2c64ebe955ba3e04d1703431cc2 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 22 Jun 2023 04:43:56 -0500 Subject: [PATCH 14/53] Fix up notify --- playback/src/player.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index f50e72ef9..bf2b915cf 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1153,6 +1153,7 @@ impl Future for PlayerInternal { let new_stream_position_ms = packet_position .position_ms .saturating_sub(sample_pipeline_latency_ms); + let expected_position_ms = std::mem::replace( &mut *stream_position_ms, new_stream_position_ms, @@ -1200,7 +1201,7 @@ impl Future for PlayerInternal { } } - notify + notify || sample_pipeline_latency_ms > 1000 } }; From 8d35b4b8607bf3729845b52fc1ec5588aa13844b Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 22 Jun 2023 14:29:04 -0500 Subject: [PATCH 15/53] Update notify_about_position logic It would be so much easier to use elapsed but elapsed could potentially panic is rare cases. See: https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity Otherwise this is pretty straight forward. If anything fails getting expected_position_ms it will return 0 which will trigger a notify if either stream_position_ms or decoder_position_ms is > 1000. If all goes well it's simply a matter of calculating the max delta of expected_position_ms and stream_position_ms and expected_position_ms and decoder_position_ms. So if the decoder or the sample pipeline are off by more than 1 sec we notify. --- playback/src/player.rs | 90 +++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index bf2b915cf..6b1ba10cd 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1150,68 +1150,58 @@ impl Future for PlayerInternal { match decoder.next_packet() { Ok(result) => { if let Some((ref packet_position, ref packet)) = result { - let new_stream_position_ms = packet_position - .position_ms - .saturating_sub(sample_pipeline_latency_ms); + let decoder_position_ms = packet_position.position_ms; - let expected_position_ms = std::mem::replace( - &mut *stream_position_ms, - new_stream_position_ms, - ); + *stream_position_ms = + decoder_position_ms.saturating_sub(sample_pipeline_latency_ms); if !passthrough { match packet.samples() { Ok(_) => { - let new_stream_position = Duration::from_millis( - new_stream_position_ms as u64, - ); - let now = Instant::now(); - // Only notify if we're skipped some packets *or* we are behind. - // If we're ahead it's probably due to a buffer of the backend - // and we're actually in time. - let notify_about_position = - match *reported_nominal_start_time { - None => true, - Some(reported_nominal_start_time) => { - let mut notify = false; - - if packet_position.skipped { - if let Some(ahead) = new_stream_position - .checked_sub(Duration::from_millis( - expected_position_ms as u64, - )) - { - notify |= - ahead >= Duration::from_secs(1) - } - } - - if let Some(lag) = now - .checked_duration_since( - reported_nominal_start_time, - ) - { - if let Some(lag) = - lag.checked_sub(new_stream_position) - { - notify |= - lag >= Duration::from_secs(1) - } - } - - notify || sample_pipeline_latency_ms > 1000 - } - }; + let notify_about_position = { + // It would be so much easier to use elapsed but elapsed could + // potentially panic is rare cases. + // See: + // https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity + // + // Otherwise this is pretty straight forward. If anything fails getting + // expected_position_ms it will return 0 which will trigger a notify if + // either stream_position_ms or decoder_position_ms is > 1000. If all goes + // well it's simply a matter of calculating the max delta of expected_position_ms + // and stream_position_ms and expected_position_ms and decoder_position_ms. + // So if the decoder or the sample pipeline are off by more than 1 sec we notify. + let expected_position_ms = now + .checked_duration_since( + reported_nominal_start_time.unwrap_or(now), + ) + .unwrap_or(Duration::ZERO) + .as_millis(); + + let max_expected_position_delta_ms = + expected_position_ms + .abs_diff(*stream_position_ms as u128) + .max( + expected_position_ms.abs_diff( + decoder_position_ms as u128, + ), + ); + + max_expected_position_delta_ms > 1000 + }; if notify_about_position { - *reported_nominal_start_time = - now.checked_sub(new_stream_position); + let position_ms = *stream_position_ms; + + *reported_nominal_start_time = now.checked_sub( + Duration::from_millis(position_ms as u64), + ); + self.send_event(PlayerEvent::PositionCorrection { play_request_id, track_id, - position_ms: new_stream_position_ms, + position_ms, }); } } From 0e3ffe539466dcd3695ccc319356f277d9ef877f Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 02:26:57 -0500 Subject: [PATCH 16/53] Harmonize thread names and debug messages --- playback/src/player.rs | 13 +++++++---- playback/src/resampler.rs | 14 ++++++++---- src/player_event_handler.rs | 43 ++++++++++++++++++++++++++++++------- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index 6b1ba10cd..35b65f429 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -81,7 +81,7 @@ struct PlayerInternal { player_id: usize, } -static PLAYER_COUNTER: AtomicUsize = AtomicUsize::new(0); +pub static PLAYER_COUNTER: AtomicUsize = AtomicUsize::new(0); enum PlayerCommand { Load { @@ -507,11 +507,11 @@ impl Player { impl Drop for Player { fn drop(&mut self) { - debug!("Shutting down player thread ..."); + debug!("Shutting down thread ..."); self.commands = None; if let Some(handle) = self.thread_handle.take() { if let Err(e) = handle.join() { - error!("Player thread Error: {:?}", e); + error!(" thread Error: {:?}", e); } } } @@ -1953,7 +1953,12 @@ impl PlayerInternal { let load_handles_clone = self.load_handles.clone(); let handle = tokio::runtime::Handle::current(); - let thread_name = format!("loader:{}", spotify_id.to_uri().unwrap_or_default()); + // The player increments the player id when it gets it... + let thread_name = format!( + "loader:{}:{}", + PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1), + spotify_id.to_uri().unwrap_or_default() + ); let builder = thread::Builder::new().name(thread_name.clone()); diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index f9da63b0f..a087d2fc7 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -2,12 +2,14 @@ use std::{ collections::{vec_deque, VecDeque}, marker::Send, process::exit, + sync::atomic::Ordering, sync::mpsc, thread, }; use crate::{ config::{InterpolationQuality, SampleRate}, + player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE, }; @@ -287,8 +289,8 @@ impl ResampleWorker { } ResampleTask::Terminate => { match thread::current().name() { - Some(name) => debug!("drop [{name}] thread"), - None => debug!("drop thread"), + Some(name) => debug!(" [{name}] thread finished"), + None => debug!(" thread finished"), } break; @@ -353,6 +355,7 @@ impl ResampleWorker { impl Drop for ResampleWorker { fn drop(&mut self) { + debug!("Shutting down thread ..."); self.task_sender .take() .and_then(|sender| sender.send(ResampleTask::Terminate).ok()); @@ -399,8 +402,11 @@ impl StereoInterleavedResampler { _ => { debug!("Interpolation Quality: {interpolation_quality}"); - let left_thread_name = "resampler:left".to_string(); - let right_thread_name = "resampler:right".to_string(); + // The player increments the player id when it gets it... + let player_id = PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1); + + let left_thread_name = format!("resampler:{player_id}:left"); + let right_thread_name = format!("resampler:{player_id}:right"); match interpolation_quality { InterpolationQuality::Low => { diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index d9d6de21e..4fe2a271a 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,10 +1,10 @@ use log::{debug, error, warn}; -use std::{collections::HashMap, process::Command, thread}; +use std::{collections::HashMap, process::exit, process::Command, sync::atomic::Ordering, thread}; use librespot::{ metadata::audio::UniqueFields, - playback::player::{PlayerEvent, PlayerEventChannel, SinkStatus}, + playback::player::{PlayerEvent, PlayerEventChannel, SinkStatus, PLAYER_COUNTER}, }; pub struct EventHandler { @@ -14,9 +14,25 @@ pub struct EventHandler { impl EventHandler { pub fn new(mut player_events: PlayerEventChannel, onevent: &str) -> Self { let on_event = onevent.to_string(); - let thread_handle = Some(thread::spawn(move || loop { + + // The player increments the player id when it gets it... + let thread_name = format!( + "event-handler:{}", + PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1) + ); + + let builder = thread::Builder::new().name(thread_name.clone()); + + let thread_handle = match builder.spawn(move || loop { match player_events.blocking_recv() { - None => break, + None => { + match thread::current().name() { + Some(name) => debug!(" [{name}] thread finished"), + None => debug!(" thread finished"), + } + + break; + } Some(event) => { let mut env_vars = HashMap::new(); @@ -245,18 +261,29 @@ impl EventHandler { } } } - })); + }) { + Ok(handle) => { + debug!("Created [{thread_name}] thread"); + handle + } + Err(e) => { + error!("Error creating [{thread_name}] thread: {e}"); + exit(1); + } + }; - Self { thread_handle } + Self { + thread_handle: Some(thread_handle), + } } } impl Drop for EventHandler { fn drop(&mut self) { - debug!("Shutting down EventHandler thread ..."); + debug!("Shutting down thread ..."); if let Some(handle) = self.thread_handle.take() { if let Err(e) = handle.join() { - error!("EventHandler thread Error: {:?}", e); + error!(" thread Error: {:?}", e); } } } From bfb0366c90f9fb73d5f96eeb545f145b3be3166e Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 10:10:14 -0500 Subject: [PATCH 17/53] Make sure the ResampleWorker task_receiver is also drained on drop --- playback/src/resampler.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index a087d2fc7..560ffea58 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -288,6 +288,14 @@ impl ResampleWorker { .ok(); } ResampleTask::Terminate => { + loop { + let drained = task_receiver.recv().ok(); + + if drained.is_none() { + break; + } + } + match thread::current().name() { Some(name) => debug!(" [{name}] thread finished"), None => debug!(" thread finished"), From 5da8ddf5e21879d5c18e8ee69229d8d915ada569 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 11:24:10 -0500 Subject: [PATCH 18/53] Rename set_factor to update_normalisation_data to better indicate what it does --- playback/src/normaliser.rs | 6 +++++- playback/src/player.rs | 2 +- playback/src/sample_pipeline.rs | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs index d08577383..a72f61ae0 100644 --- a/playback/src/normaliser.rs +++ b/playback/src/normaliser.rs @@ -246,7 +246,11 @@ impl Normaliser { self.normalisation.stop(); } - pub fn set_factor(&mut self, auto_normalise_as_album: bool, data: NormalisationData) { + pub fn update_normalisation_data( + &mut self, + auto_normalise_as_album: bool, + data: NormalisationData, + ) { if self.normalisation != Normalisation::None { self.factor = self.get_factor(auto_normalise_as_album, data); } diff --git a/playback/src/player.rs b/playback/src/player.rs index 35b65f429..c993f5f66 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1459,7 +1459,7 @@ impl PlayerInternal { let position_ms = loaded_track.stream_position_ms; - self.sample_pipeline.set_normalisation_factor( + self.sample_pipeline.update_normalisation_data( self.auto_normalise_as_album, loaded_track.normalisation_data, ); diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index e534bea71..fb33cb958 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -57,12 +57,13 @@ impl SamplePipeline { Ok(()) } - pub fn set_normalisation_factor( + pub fn update_normalisation_data( &mut self, auto_normalise_as_album: bool, data: NormalisationData, ) { - self.normaliser.set_factor(auto_normalise_as_album, data); + self.normaliser + .update_normalisation_data(auto_normalise_as_album, data); } pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> { From 46b8f84d6aa947a1ccfc9c845a2c3760a5bf15e4 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 12:00:42 -0500 Subject: [PATCH 19/53] Make sure there is only ever one allocation when converting Collect is probably fine but for code that's this hot it's worth the couple extra lines to make certain there's only ever one allocation when it comes to the returned Vec. --- playback/src/convert.rs | 58 +++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/playback/src/convert.rs b/playback/src/convert.rs index a7efe452b..f825490c2 100644 --- a/playback/src/convert.rs +++ b/playback/src/convert.rs @@ -80,36 +80,60 @@ impl Converter { } pub fn f64_to_f32(&mut self, samples: &[f64]) -> Vec { - samples.iter().map(|sample| *sample as f32).collect() + let mut output = Vec::with_capacity(samples.len()); + + output.extend(samples.iter().map(|sample| *sample as f32)); + + output } pub fn f64_to_s32(&mut self, samples: &[f64]) -> Vec { - samples - .iter() - .map(|sample| self.scale(*sample, Self::SCALE_S32) as i32) - .collect() + let mut output = Vec::with_capacity(samples.len()); + + output.extend( + samples + .iter() + .map(|sample| self.scale(*sample, Self::SCALE_S32) as i32), + ); + + output } // S24 is 24-bit PCM packed in an upper 32-bit word pub fn f64_to_s24(&mut self, samples: &[f64]) -> Vec { - samples - .iter() - .map(|sample| self.clamping_scale(*sample, Self::SCALE_S24) as i32) - .collect() + let mut output = Vec::with_capacity(samples.len()); + + output.extend( + samples + .iter() + .map(|sample| self.clamping_scale(*sample, Self::SCALE_S24) as i32), + ); + + output } // S24_3 is 24-bit PCM in a 3-byte array pub fn f64_to_s24_3(&mut self, samples: &[f64]) -> Vec { - samples - .iter() - .map(|sample| i24::from_s24(self.clamping_scale(*sample, Self::SCALE_S24) as i32)) - .collect() + let mut output = Vec::with_capacity(samples.len()); + + output.extend( + samples + .iter() + .map(|sample| i24::from_s24(self.clamping_scale(*sample, Self::SCALE_S24) as i32)), + ); + + output } pub fn f64_to_s16(&mut self, samples: &[f64]) -> Vec { - samples - .iter() - .map(|sample| self.scale(*sample, Self::SCALE_S16) as i16) - .collect() + let mut output = Vec::with_capacity(samples.len()); + + output.extend( + samples + .iter() + .map(|sample| self.scale(*sample, Self::SCALE_S16) as i16), + ); + + output } } From 340bac5eb004ff13d39f1629d446d63958d7fbad Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 12:40:13 -0500 Subject: [PATCH 20/53] Warn the user if the try to set interpolation-quality with 44.1kHz --- src/main.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 95e2f6eb7..5590379dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -782,16 +782,16 @@ fn get_setup() -> Setup { exit(1); }); - let interpolation_quality = opt_str(INTERPOLATION_QUALITY) + let sample_rate = opt_str(SAMPLE_RATE) .as_deref() - .map(|interpolation_quality| { - InterpolationQuality::from_str(interpolation_quality).unwrap_or_else(|_| { - let default_value = &format!("{}", InterpolationQuality::default()); + .map(|sample_rate| { + SampleRate::from_str(sample_rate).unwrap_or_else(|_| { + let default_value = &format!("{}", SampleRate::default()); invalid_error_msg( - INTERPOLATION_QUALITY, + SAMPLE_RATE, "", - interpolation_quality, - "Low, Medium, High", + sample_rate, + "44.1kHz, 48kHz, 88.2kHz, 96kHz", default_value, ); @@ -800,21 +800,29 @@ fn get_setup() -> Setup { }) .unwrap_or_default(); - let sample_rate = opt_str(SAMPLE_RATE) + let interpolation_quality = opt_str(INTERPOLATION_QUALITY) .as_deref() - .map(|sample_rate| { - SampleRate::from_str(sample_rate).unwrap_or_else(|_| { - let default_value = &format!("{}", SampleRate::default()); + .map(|interpolation_quality| match sample_rate { + SampleRate::Hz44100 => { + warn!( + "--{} has no effect with a sample rate of {sample_rate}.", + INTERPOLATION_QUALITY + ); + + InterpolationQuality::default() + } + _ => InterpolationQuality::from_str(interpolation_quality).unwrap_or_else(|_| { + let default_value = &format!("{}", InterpolationQuality::default()); invalid_error_msg( - SAMPLE_RATE, + INTERPOLATION_QUALITY, "", - sample_rate, - "44.1kHz, 48kHz, 88.2kHz, 96kHz", + interpolation_quality, + "Low, Medium, High", default_value, ); exit(1); - }) + }), }) .unwrap_or_default(); From ad4763005d172eeea7717a928239dbf833182b1e Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 23 Jun 2023 18:51:17 -0500 Subject: [PATCH 21/53] Add release-dist-optimized profile release-dist-optimized inherits from `release`. Useful if you're distributing librespot at part of a project. The diffrences are: panic = "abort", Makes librespot abort instead of unwind and hang on a panic. Extremely useful when running librespot unattended as a system service for example to allow for auto-restarts. codegen-units = 1 and lto = true, Take slighly longer to compile but produce more optimized binaries. --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 98b8405bb..e09990148 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,12 @@ passthrough-decoder = ["librespot-playback/passthrough-decoder"] default = ["rodio-backend"] +[profile.release-dist-optimized] +inherits = "release" +panic = "abort" +codegen-units = 1 +lto = true + [package.metadata.deb] maintainer = "librespot-org" copyright = "2018 Paul Liétar" From 74b52e83fa89640b4a4c11f7333298bbf91e317c Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 15:06:28 -0500 Subject: [PATCH 22/53] Improve resampler performance Do less calls into the worker. --- playback/src/resampler.rs | 117 ++++++-------------------------- playback/src/sample_pipeline.rs | 2 +- 2 files changed, 22 insertions(+), 97 deletions(-) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 560ffea58..ed6c0cb3d 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -241,18 +241,12 @@ impl MonoResampler for MonoLinearResampler { enum ResampleTask { Stop, Terminate, - GetLatency, - ProcessSamples(Vec), -} - -enum ResampleResult { - Latency(u64), - ProcessedSamples(Option>), + Resample(Vec), } struct ResampleWorker { task_sender: Option>, - result_receiver: Option>, + result_receiver: Option>, u64)>>, handle: Option>, } @@ -275,17 +269,11 @@ impl ResampleWorker { } Ok(task) => match task { ResampleTask::Stop => resampler.stop(), - ResampleTask::GetLatency => { + ResampleTask::Resample(samples) => { + let resampled = resampler.resample(&samples); 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(); + result_sender.send((resampled, latency)).ok(); } ResampleTask::Terminate => { loop { @@ -323,41 +311,23 @@ impl ResampleWorker { } } - 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) { + fn resample(&mut self, samples: Vec) { self.task_sender .as_mut() - .and_then(|sender| sender.send(ResampleTask::ProcessSamples(samples)).ok()); + .and_then(|sender| sender.send(ResampleTask::Resample(samples)).ok()); } - fn receive_result(&mut self) -> Option> { + fn get_resampled(&mut self) -> (Option>, u64) { self.result_receiver .as_mut() .and_then(|result_receiver| result_receiver.recv().ok()) - .and_then(|result| match result { - ResampleResult::ProcessedSamples(samples) => samples, - _ => None, - }) + .unwrap_or((None, 0)) } } @@ -392,8 +362,7 @@ enum Resampler { pub struct StereoInterleavedResampler { resampler: Resampler, - latency_flag: bool, - process_flag: bool, + latency_pcm: u64, } impl StereoInterleavedResampler { @@ -445,50 +414,15 @@ impl StereoInterleavedResampler { Self { resampler, - latency_flag: true, - process_flag: false, + latency_pcm: 0, } } 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() - } - } - } + self.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> { - let alternate_process_flag = self.alternate_process_flag(); - + pub fn resample(&mut self, input_samples: &[f64]) -> Option> { match &mut self.resampler { // Bypass is basically a no-op. Resampler::Bypass => Some(input_samples.to_vec()), @@ -498,26 +432,17 @@ impl StereoInterleavedResampler { } => { 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); + left_resampler.resample(left_samples); + right_resampler.resample(right_samples); - let processed_right_samples = right_resampler.receive_result(); - let processed_left_samples = left_resampler.receive_result(); + let (left_resampled, left_latency_pcm) = left_resampler.get_resampled(); + let (right_resampled, right_latency_pcm) = right_resampler.get_resampled(); - (processed_left_samples, processed_right_samples) - }; + // They should always be equal + self.latency_pcm = left_latency_pcm.max(right_latency_pcm); - processed_left_samples.and_then(|left_samples| { - processed_right_samples.map(|right_samples| { + left_resampled.and_then(|left_samples| { + right_resampled.map(|right_samples| { Self::interleave_samples(&left_samples, &right_samples) }) }) diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index fb33cb958..5b15cb2e6 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -69,7 +69,7 @@ impl SamplePipeline { pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> { if let AudioPacket::Samples(samples) = packet { self.resampler - .process(&samples) + .resample(&samples) .map(|processed_samples| self.normaliser.normalise(&processed_samples)) .map(|new_packet| self.sink.write(new_packet, &mut self.converter)) .transpose()?; From 0bdfa726cabba0f23770b27e9f2d9885193ebfab Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 15:26:29 -0500 Subject: [PATCH 23/53] Remove round from pulse get_latency_pcm, floor math is fine --- playback/src/audio_backend/pulseaudio.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index d60351e92..25ad8c5d2 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -140,9 +140,9 @@ impl Sink for PulseAudioSink { self.sink .as_mut() .and_then(|sink| { - sink.get_latency().ok().map(|micro_sec| { - (micro_sec.as_secs_f64() * DECODER_SAMPLE_RATE as f64).round() as u64 - }) + sink.get_latency() + .ok() + .map(|micro_sec| (micro_sec.as_secs_f64() * DECODER_SAMPLE_RATE as f64) as u64) }) .unwrap_or(0) } From 5e02b6643d523cefd16892dac96b5350e8bd891a Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 17:43:10 -0500 Subject: [PATCH 24/53] Simplify time calculations in SymphoniaDecoder Time impl's from f64 (as secs) so there's no need to manually calculate it beyond converting ms to sec. If we grab the TimeBase in new we don't need to continually call decoder.codec_params().time_base everytime we want to convert ts to ms. --- playback/src/decoder/symphonia_decoder.rs | 27 ++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/playback/src/decoder/symphonia_decoder.rs b/playback/src/decoder/symphonia_decoder.rs index 2bf2517d3..1a4aecabe 100644 --- a/playback/src/decoder/symphonia_decoder.rs +++ b/playback/src/decoder/symphonia_decoder.rs @@ -8,7 +8,7 @@ use symphonia::{ formats::{FormatOptions, FormatReader, SeekMode, SeekTo}, io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions}, meta::{StandardTagKey, Value}, - units::Time, + units::TimeBase, }, default::{ codecs::{MpaDecoder, VorbisDecoder}, @@ -27,6 +27,7 @@ use crate::{ pub struct SymphoniaDecoder { format: Box, decoder: Box, + time_base: Option, sample_buffer: Option>, } @@ -88,9 +89,12 @@ impl SymphoniaDecoder { ))); } + let time_base = decoder.codec_params().time_base; + Ok(Self { format, decoder, + time_base, // We set the sample buffer when decoding the first full packet, // whose duration is also the ideal sample buffer size. @@ -133,30 +137,23 @@ impl SymphoniaDecoder { } fn ts_to_ms(&self, ts: u64) -> u32 { - let time_base = self.decoder.codec_params().time_base; - let seeked_to_ms = match time_base { - Some(time_base) => { + // Falls back in the unexpected case that the format has no base time set. + self.time_base + .map(|time_base| { let time = time_base.calc_time(ts); - (time.seconds as f64 + time.frac) * 1000. - } - // Fallback in the unexpected case that the format has no base time set. - None => ts as f64 * PAGES_PER_MS, - }; - seeked_to_ms as u32 + ((time.seconds as f64 + time.frac) * 1000.) as u32 + }) + .unwrap_or((ts as f64 * PAGES_PER_MS) as u32) } } impl AudioDecoder for SymphoniaDecoder { fn seek(&mut self, position_ms: u32) -> Result { - let seconds = position_ms as u64 / 1000; - let frac = (position_ms as f64 % 1000.) / 1000.; - let time = Time::new(seconds, frac); - // `track_id: None` implies the default track ID (of the container, not of Spotify). let seeked_to_ts = self.format.seek( SeekMode::Accurate, SeekTo::Time { - time, + time: (position_ms as f64 / 1000.).into(), track_id: None, }, )?; From f7c56dff4416f61f2ebb06878886c445f1b29164 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 18:59:01 -0500 Subject: [PATCH 25/53] Remove AudioPacketPosition We don't need it since we now tell if we've skipped packets by calculating a max delta. --- playback/src/decoder/mod.rs | 17 +---------------- playback/src/decoder/passthrough_decoder.rs | 10 +++------- playback/src/decoder/symphonia_decoder.rs | 13 +++---------- playback/src/player.rs | 8 +++----- 4 files changed, 10 insertions(+), 38 deletions(-) diff --git a/playback/src/decoder/mod.rs b/playback/src/decoder/mod.rs index f980b6800..6d82cbc41 100644 --- a/playback/src/decoder/mod.rs +++ b/playback/src/decoder/mod.rs @@ -1,5 +1,3 @@ -use std::ops::Deref; - use thiserror::Error; #[cfg(feature = "passthrough-decoder")] @@ -58,22 +56,9 @@ impl AudioPacket { } } -#[derive(Debug, Clone)] -pub struct AudioPacketPosition { - pub position_ms: u32, - pub skipped: bool, -} - -impl Deref for AudioPacketPosition { - type Target = u32; - fn deref(&self) -> &Self::Target { - &self.position_ms - } -} - pub trait AudioDecoder { fn seek(&mut self, position_ms: u32) -> Result; - fn next_packet(&mut self) -> DecoderResult>; + fn next_packet(&mut self) -> DecoderResult>; } impl From for librespot_core::error::Error { diff --git a/playback/src/decoder/passthrough_decoder.rs b/playback/src/decoder/passthrough_decoder.rs index 59a721632..43cf7433b 100644 --- a/playback/src/decoder/passthrough_decoder.rs +++ b/playback/src/decoder/passthrough_decoder.rs @@ -7,7 +7,7 @@ use std::{ // TODO: move this to the Symphonia Ogg demuxer use ogg::{OggReadError, Packet, PacketReader, PacketWriteEndInfo, PacketWriter}; -use super::{AudioDecoder, AudioPacket, AudioPacketPosition, DecoderError, DecoderResult}; +use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult}; use crate::{ metadata::audio::{AudioFileFormat, AudioFiles}, @@ -136,7 +136,7 @@ impl AudioDecoder for PassthroughDecoder { } } - fn next_packet(&mut self) -> DecoderResult> { + fn next_packet(&mut self) -> DecoderResult> { // write headers if we are (re)starting if !self.bos { self.wtr @@ -207,14 +207,10 @@ impl AudioDecoder for PassthroughDecoder { if !data.is_empty() { let position_ms = Self::position_pcm_to_ms(pckgp_page); - let packet_position = AudioPacketPosition { - position_ms, - skipped: false, - }; let ogg_data = AudioPacket::Raw(std::mem::take(data)); - return Ok(Some((packet_position, ogg_data))); + return Ok(Some((position_ms, ogg_data))); } } } diff --git a/playback/src/decoder/symphonia_decoder.rs b/playback/src/decoder/symphonia_decoder.rs index 1a4aecabe..c716f7e64 100644 --- a/playback/src/decoder/symphonia_decoder.rs +++ b/playback/src/decoder/symphonia_decoder.rs @@ -16,7 +16,7 @@ use symphonia::{ }, }; -use super::{AudioDecoder, AudioPacket, AudioPacketPosition, DecoderError, DecoderResult}; +use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult}; use crate::{ metadata::audio::{AudioFileFormat, AudioFiles}, @@ -165,9 +165,7 @@ impl AudioDecoder for SymphoniaDecoder { Ok(self.ts_to_ms(seeked_to_ts.actual_ts)) } - fn next_packet(&mut self) -> DecoderResult> { - let mut skipped = false; - + fn next_packet(&mut self) -> DecoderResult> { loop { let packet = match self.format.next_packet() { Ok(packet) => packet, @@ -184,10 +182,6 @@ impl AudioDecoder for SymphoniaDecoder { }; let position_ms = self.ts_to_ms(packet.ts()); - let packet_position = AudioPacketPosition { - position_ms, - skipped, - }; match self.decoder.decode(&packet) { Ok(decoded) => { @@ -203,13 +197,12 @@ impl AudioDecoder for SymphoniaDecoder { sample_buffer.copy_interleaved_ref(decoded); let samples = AudioPacket::Samples(sample_buffer.samples().to_vec()); - return Ok(Some((packet_position, samples))); + return Ok(Some((position_ms, samples))); } Err(Error::DecodeError(_)) => { // The packet failed to decode due to corrupted or invalid data, get a new // packet and try again. warn!("Skipping malformed audio packet at {position_ms} ms"); - skipped = true; continue; } Err(err) => return Err(err.into()), diff --git a/playback/src/player.rs b/playback/src/player.rs index c993f5f66..5d1662213 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -31,7 +31,7 @@ use crate::{ audio_backend::Sink, config::{Bitrate, PlayerConfig}, core::{util::SeqGenerator, Error, Session, SpotifyId}, - decoder::{AudioDecoder, AudioPacket, AudioPacketPosition, SymphoniaDecoder}, + decoder::{AudioDecoder, AudioPacket, SymphoniaDecoder}, metadata::audio::{AudioFileFormat, AudioFiles, AudioItem}, mixer::VolumeGetter, sample_pipeline::SamplePipeline, @@ -1149,9 +1149,7 @@ impl Future for PlayerInternal { { match decoder.next_packet() { Ok(result) => { - if let Some((ref packet_position, ref packet)) = result { - let decoder_position_ms = packet_position.position_ms; - + if let Some((decoder_position_ms, ref packet)) = result { *stream_position_ms = decoder_position_ms.saturating_sub(sample_pipeline_latency_ms); @@ -1416,7 +1414,7 @@ impl PlayerInternal { } } - fn handle_packet(&mut self, packet: Option<(AudioPacketPosition, AudioPacket)>) { + fn handle_packet(&mut self, packet: Option<(u32, AudioPacket)>) { match packet { Some((_, packet)) => { if !packet.is_empty() { From ac2c05a0f859efef1bb9af7cef6580d93add4415 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 20:16:21 -0500 Subject: [PATCH 26/53] Reset the resampler's latency when stopped --- playback/src/resampler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index ed6c0cb3d..e847fdaa9 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -451,6 +451,8 @@ impl StereoInterleavedResampler { } pub fn stop(&mut self) { + self.latency_pcm = 0; + match &mut self.resampler { // Stop does nothing // if we're bypassed. From 528a8a431e9b46b32fe308855ddd0dd0ef07df10 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 24 Jun 2023 22:36:33 -0500 Subject: [PATCH 27/53] Don't pad the alsa buffer anymore on stop. It's no longer necessary with the SymphoniaDecoder, in fact it causes the problem it used to prevent. Namely pops when you pause. --- playback/src/audio_backend/alsa.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 9a8721e09..e5c311c73 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -246,9 +246,6 @@ impl Sink for AlsaSink { } fn stop(&mut self) -> SinkResult<()> { - // Zero fill the remainder of the period buffer and - // write any leftover data before draining the actual PCM buffer. - self.period_buffer.resize(self.period_buffer.capacity(), 0); self.write_buf()?; if let Some(pcm) = self.pcm.take() { From d3ae1cdc55e56be8b223bbff61ace8ff5cc8f740 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sun, 25 Jun 2023 21:09:43 -0500 Subject: [PATCH 28/53] Update all deps --- Cargo.lock | 445 +++++++++++++++++++++---------------------- Cargo.toml | 4 +- connect/Cargo.toml | 2 +- core/Cargo.toml | 10 +- discovery/Cargo.toml | 2 +- playback/Cargo.toml | 2 +- 6 files changed, 228 insertions(+), 237 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d1e56b18..51682c4ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -67,9 +67,9 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" @@ -79,7 +79,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -117,9 +117,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytemuck" @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9" +checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c" dependencies = [ "smallvec", "target-lexicon", @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -374,11 +374,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + [[package]] name = "der" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" dependencies = [ "const-oid", "pem-rfc7468", @@ -387,9 +393,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", @@ -435,6 +441,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -479,9 +491,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -542,7 +554,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -602,9 +614,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -613,15 +625,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gio-sys" -version = "0.17.4" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5" +checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" dependencies = [ "glib-sys", "gobject-sys", @@ -632,9 +644,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6" +checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" dependencies = [ "bitflags", "futures-channel", @@ -655,9 +667,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7206c5c03851ef126ea1444990e81fdd6765fb799d5bc694e4897ca01bb97f" +checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" dependencies = [ "anyhow", "heck", @@ -670,9 +682,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.17.4" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5" +checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" dependencies = [ "libc", "system-deps", @@ -686,9 +698,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gobject-sys" -version = "0.17.4" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0" +checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" dependencies = [ "glib-sys", "libc", @@ -713,9 +725,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4530401c89be6dc10d77ae1587b811cf455c97dce7abf594cb9164527c7da7fc" +checksum = "3113531138b4e41968e33fd003a0d1a635ef6e0cbc309dd5004123000863ac54" dependencies = [ "bitflags", "cfg-if", @@ -848,7 +860,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -861,6 +873,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "headers" version = "0.3.8" @@ -1042,17 +1060,17 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.21.1", - "rustls-native-certs 0.6.2", + "rustls 0.21.2", + "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1075,7 +1093,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -1098,9 +1126,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -1197,9 +1225,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1221,9 +1249,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -1237,9 +1265,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libmdns" @@ -1375,7 +1403,7 @@ name = "librespot-core" version = "0.5.0-dev" dependencies = [ "aes", - "base64 0.21.0", + "base64 0.21.2", "byteorder", "bytes", "dns-sd", @@ -1427,7 +1455,7 @@ name = "librespot-discovery" version = "0.5.0-dev" dependencies = [ "aes", - "base64 0.21.0", + "base64 0.21.2", "cfg-if", "ctr", "dns-sd", @@ -1508,15 +1536,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -1524,12 +1552,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "mach2" @@ -1584,14 +1609,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1808,9 +1832,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "memchr", ] @@ -1849,9 +1873,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl-probe" @@ -1880,18 +1904,18 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "backtrace", "cfg-if", "libc", "petgraph", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", "thread-id", - "windows-sys 0.45.0", + "windows-targets 0.48.0", ] [[package]] @@ -1927,9 +1951,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" @@ -1938,7 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -2015,12 +2039,12 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "priority-queue" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca9c6be70d989d21a136eb86c2d83e4b328447fac4a88dace2143c179c86267" +checksum = "fff39edfcaec0d64e8d0da38564fad195d2d51b680940295fcc307366e101e61" dependencies = [ "autocfg", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -2059,9 +2083,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -2099,7 +2123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" dependencies = [ "anyhow", - "indexmap", + "indexmap 1.9.3", "log", "protobuf", "protobuf-support", @@ -2119,9 +2143,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" dependencies = [ "memchr", "serde", @@ -2129,9 +2153,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2202,9 +2226,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", @@ -2213,9 +2237,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "ring" @@ -2298,9 +2322,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno", @@ -2325,21 +2349,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", -] - -[[package]] -name = "rustls" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" dependencies = [ "log", "ring", @@ -2361,9 +2373,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -2377,7 +2389,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", ] [[package]] @@ -2471,9 +2483,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags", "core-foundation", @@ -2494,29 +2506,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", @@ -2525,9 +2537,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -2626,15 +2638,15 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "symphonia" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3671dd6f64f4f9d5c87179525054cfc1f60de23ba1f193bd6ceab812737403f1" +checksum = "62e48dba70095f265fdb269b99619b95d04c89e619538138383e63310b14d941" dependencies = [ "lazy_static", "symphonia-bundle-mp3", @@ -2646,9 +2658,9 @@ dependencies = [ [[package]] name = "symphonia-bundle-mp3" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a0846e7a2c9a8081ff799fc83a975170417ad2a143f644a77ec2e3e82a2b73" +checksum = "0f31d7fece546f1e6973011a9eceae948133bbd18fd3d52f6073b1e38ae6368a" dependencies = [ "bitflags", "lazy_static", @@ -2659,9 +2671,9 @@ dependencies = [ [[package]] name = "symphonia-codec-vorbis" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfed6f7b6bfa21d7cef1acefc8eae5db80df1608a1aca91871b07cbd28d7b74" +checksum = "3953397e3506aa01350c4205817e4f95b58d476877a42f0458d07b665749e203" dependencies = [ "log", "symphonia-core", @@ -2670,9 +2682,9 @@ dependencies = [ [[package]] name = "symphonia-core" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9567e2d8a5f866b2f94f5d366d811e0c6826babcff6d37de9e1a6690d38869" +checksum = "f7c73eb88fee79705268cc7b742c7bc93a7b76e092ab751d0833866970754142" dependencies = [ "arrayvec", "bitflags", @@ -2683,9 +2695,9 @@ dependencies = [ [[package]] name = "symphonia-format-ogg" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474df6e86b871dcb56913130bada1440245f483057c4a2d8a2981455494c4439" +checksum = "9bf1a00ccd11452d44048a0368828040f778ae650418dbd9d8765b7ee2574c8d" dependencies = [ "log", "symphonia-core", @@ -2695,9 +2707,9 @@ dependencies = [ [[package]] name = "symphonia-metadata" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd35c263223ef6161000be79b124a75de3e065eea563bf3ef169b3e94c7bb2e" +checksum = "89c3e1937e31d0e068bbe829f66b2f2bfaa28d056365279e0ef897172c3320c0" dependencies = [ "encoding_rs", "lazy_static", @@ -2707,9 +2719,9 @@ dependencies = [ [[package]] name = "symphonia-utils-xiph" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce340a6c33ac06cb42de01220308ec056e8a2a3d5cc664aaf34567392557136b" +checksum = "a450ca645b80d69aff8b35576cbfdc7f20940b29998202aab910045714c951f8" dependencies = [ "symphonia-core", "symphonia-metadata", @@ -2728,9 +2740,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ "proc-macro2", "quote", @@ -2739,9 +2751,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.29.0" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f1dc6930a439cc5d154221b5387d153f8183529b07c19aca24ea31e0a167e1" +checksum = "9557d0845b86eea8182f7b10dff120214fb6cd9fd937b6f4917714e546a38695" dependencies = [ "cfg-if", "core-foundation-sys 0.8.4", @@ -2753,9 +2765,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ "cfg-expr", "heck", @@ -2766,21 +2778,22 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2809,14 +2822,14 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "thread-id" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f" +checksum = "3ee93aa2b8331c0fec9091548843f2c90019571814057da3b783f9de09349d73" dependencies = [ "libc", "redox_syscall 0.2.16", @@ -2825,9 +2838,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "itoa", "serde", @@ -2867,9 +2880,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -2892,7 +2905,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -2908,22 +2921,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.8", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.1", + "rustls 0.21.2", "tokio", ] @@ -2940,18 +2942,17 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" dependencies = [ "futures-util", "log", - "rustls 0.20.8", - "rustls-native-certs 0.6.2", + "rustls 0.21.2", + "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", "tungstenite", - "webpki 0.22.0", ] [[package]] @@ -2970,9 +2971,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" dependencies = [ "serde", "serde_spanned", @@ -2982,20 +2983,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -3036,18 +3037,18 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", "rand", - "rustls 0.20.8", + "rustls 0.21.2", "sha1", "thiserror", "url", @@ -3069,9 +3070,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -3096,9 +3097,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -3113,9 +3114,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" dependencies = [ "getrandom", "rand", @@ -3123,9 +3124,9 @@ dependencies = [ [[package]] name = "vergen" -version = "8.1.3" +version = "8.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e03272e388fb78fc79481a493424f78d77be1d55f21bcd314b5a6716e195afe" +checksum = "8b3c89c2c7e50f33e4d35527e5bf9c11d6d132226dbbd1753f0fbe9f19ef88c6" dependencies = [ "anyhow", "rustversion", @@ -3156,11 +3157,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -3172,9 +3172,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3182,24 +3182,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -3209,9 +3209,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3219,28 +3219,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -3332,15 +3332,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -3466,9 +3457,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index e09990148..ae98c4e10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,11 +55,11 @@ futures-util = { version = "0.3", default_features = false } getopts = "0.2" hex = "0.4" log = "0.4" -rpassword = "7.0" +rpassword = "7.2" sha1 = "0.10" thiserror = "1.0" tokio = { version = "1", features = ["rt", "macros", "signal", "sync", "parking_lot", "process"] } -url = "2.2" +url = "2.4" [features] alsa-backend = ["librespot-playback/alsa-backend"] diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 1e02df84d..7665ffa4f 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/librespot-org/librespot" edition = "2021" [dependencies] -form_urlencoded = "1.0" +form_urlencoded = "1.2" futures-util = "0.3" log = "0.4" protobuf = "3" diff --git a/core/Cargo.toml b/core/Cargo.toml index 1f0673cb8..4583d91c7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,13 +19,13 @@ base64 = "0.21" byteorder = "1.4" bytes = "1" dns-sd = { version = "0.1", optional = true } -form_urlencoded = "1.0" +form_urlencoded = "1.2" futures-core = "0.3" futures-util = { version = "0.3", features = ["alloc", "bilock", "sink", "unstable"] } governor = { version = "0.5", default-features = false, features = ["std", "jitter"] } hex = "0.4" hmac = "0.12" -httparse = "1.7" +httparse = "1.8" http = "0.2" hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] } hyper-proxy = { version = "0.9", default-features = false, features = ["rustls"] } @@ -39,11 +39,11 @@ num-traits = "0.2" once_cell = "1" parking_lot = { version = "0.12", features = ["deadlock_detection"] } pbkdf2 = { version = "0.12", default-features = false, features = ["hmac"] } -priority-queue = "1.2" +priority-queue = "1.3" protobuf = "3" -quick-xml = { version = "0.28", features = ["serialize"] } +quick-xml = { version = "0.29", features = ["serialize"] } rand = "0.8" -rsa = "0.9.2" +rsa = "0.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha1 = { version = "0.10", features = ["oid"] } diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index b30ddd993..80377803e 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -14,7 +14,7 @@ base64 = "0.21" cfg-if = "1.0" ctr = "0.9" dns-sd = { version = "0.1.3", optional = true } -form_urlencoded = "1.0" +form_urlencoded = "1.2" futures-core = "0.3" futures-util = "0.3" hmac = "0.12" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 5ef5b4f7a..060e1686e 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -44,7 +44,7 @@ glib = { version = "0.17", optional = true } # Rodio dependencies rodio = { version = "0.17.1", optional = true, default-features = false } -cpal = { version = "0.15.1", optional = true } +cpal = { version = "0.15.2", optional = true } # Container and audio decoder symphonia = { version = "0.5", default-features = false, features = ["mp3", "ogg", "vorbis"] } From 8e6d452765c4494b3657260112bce40fe1a31070 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 02:32:39 -0500 Subject: [PATCH 29/53] Don't use Instant elapsed It would be so much easier to use elapsed but elapsed could potentially panic is rare cases. See: https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity --- core/src/session.rs | 16 ++++++++++++++-- core/src/spclient.rs | 18 ++++++++++++++---- src/main.rs | 11 ++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/core/src/session.rs b/core/src/session.rs index 5f08b1341..58397c8d5 100755 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -252,8 +252,20 @@ impl Session { if session.is_invalid() { break; } - let last_ping = session.0.data.read().last_ping.unwrap_or_else(Instant::now); - if last_ping.elapsed() >= SESSION_TIMEOUT { + + // It would be so much easier to use elapsed but elapsed could + // potentially panic is rare cases. + // See: + // https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity + let now = Instant::now(); + + let last_ping = session.0.data.read().last_ping.unwrap_or(now); + + let since_last_ping = now + .checked_duration_since(last_ping) + .unwrap_or(SESSION_TIMEOUT); + + if since_last_ping >= SESSION_TIMEOUT { session.shutdown(); // TODO: Optionally reconnect (with cached/last credentials?) return Err(io::Error::new( diff --git a/core/src/spclient.rs b/core/src/spclient.rs index d6c5ffb1b..88130317c 100644 --- a/core/src/spclient.rs +++ b/core/src/spclient.rs @@ -114,8 +114,9 @@ impl SpClient { dst: &mut [u8], ) -> Result<(), Error> { // after a certain number of seconds, the challenge expires - const TIMEOUT: u64 = 5; // seconds - let now = Instant::now(); + const TIMEOUT: Duration = Duration::from_secs(5); + + let then = Instant::now(); let md = Sha1::digest(ctx); @@ -123,9 +124,18 @@ impl SpClient { let target: i64 = BigEndian::read_i64(&md[12..20]); let suffix = loop { - if now.elapsed().as_secs() >= TIMEOUT { + // It would be so much easier to use elapsed but elapsed could + // potentially panic is rare cases. + // See: + // https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity + if Instant::now() + .checked_duration_since(then) + .unwrap_or(TIMEOUT) + >= TIMEOUT + { return Err(Error::deadline_exceeded(format!( - "{TIMEOUT} seconds expired" + "{} seconds expired", + TIMEOUT.as_secs(), ))); } diff --git a/src/main.rs b/src/main.rs index 5590379dd..3e06a0c06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1836,7 +1836,16 @@ async fn main() { warn!("Spirc shut down unexpectedly"); let mut reconnect_exceeds_rate_limit = || { - auto_connect_times.retain(|&t| t.elapsed() < RECONNECT_RATE_LIMIT_WINDOW); + // It would be so much easier to use elapsed but elapsed could + // potentially panic is rare cases. + // See: + // https://doc.rust-lang.org/std/time/struct.Instant.html#monotonicity + let now = Instant::now(); + + auto_connect_times.retain(|&t| { + now.checked_duration_since(t).unwrap_or(RECONNECT_RATE_LIMIT_WINDOW) < RECONNECT_RATE_LIMIT_WINDOW + }); + auto_connect_times.len() > RECONNECT_RATE_LIMIT }; From 98f1fe84dd8ee9d6e5d487315c2e75feddda93bb Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 05:11:30 -0500 Subject: [PATCH 30/53] Reduce allocations --- playback/src/normaliser.rs | 46 ++++++++++++++------------------- playback/src/resampler.rs | 29 ++++++++++++--------- playback/src/sample_pipeline.rs | 2 +- 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs index a72f61ae0..8ee6d10f5 100644 --- a/playback/src/normaliser.rs +++ b/playback/src/normaliser.rs @@ -10,32 +10,26 @@ use crate::{ struct NoNormalisation; impl NoNormalisation { - fn normalise(samples: &[f64], volume: f64) -> Vec { + fn normalise(mut samples: Vec, volume: f64) -> Vec { if volume < 1.0 { - let mut output = Vec::with_capacity(samples.len()); - - output.extend(samples.iter().map(|sample| sample * volume)); - - output - } else { - samples.to_vec() + samples.iter_mut().for_each(|sample| *sample *= volume); } + + samples } } struct BasicNormalisation; impl BasicNormalisation { - fn normalise(samples: &[f64], volume: f64, factor: f64) -> Vec { + fn normalise(mut samples: Vec, volume: f64, factor: f64) -> Vec { if volume < 1.0 || factor < 1.0 { - let mut output = Vec::with_capacity(samples.len()); - - output.extend(samples.iter().map(|sample| sample * factor * volume)); - - output - } else { - samples.to_vec() + samples + .iter_mut() + .for_each(|sample| *sample *= factor * volume); } + + samples } } @@ -85,11 +79,9 @@ impl DynamicNormalisation { self.peak = 0.0; } - fn normalise(&mut self, samples: &[f64], volume: f64, factor: f64) -> Vec { - let mut output = Vec::with_capacity(samples.len()); - - output.extend(samples.iter().map(|sample| { - let mut sample = sample * factor; + fn normalise(&mut self, mut samples: Vec, volume: f64, factor: f64) -> Vec { + samples.iter_mut().for_each(|sample| { + *sample *= factor; // Feedforward limiter in the log domain // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic @@ -154,13 +146,13 @@ impl DynamicNormalisation { // the default threshold, so that would clip. // steps 7-8: conversion into level and multiplication into gain stage - sample *= db_to_ratio(-self.peak); + *sample *= db_to_ratio(-self.peak); } - sample * volume - })); + *sample *= volume + }); - output + samples } } @@ -204,7 +196,7 @@ impl Normalisation { } } - fn normalise(&mut self, samples: &[f64], volume: f64, factor: f64) -> Vec { + fn normalise(&mut self, samples: Vec, volume: f64, factor: f64) -> Vec { use Normalisation::*; match self { @@ -236,7 +228,7 @@ impl Normaliser { } } - pub fn normalise(&mut self, samples: &[f64]) -> AudioPacket { + pub fn normalise(&mut self, samples: Vec) -> AudioPacket { let volume = self.volume_getter.attenuation_factor(); AudioPacket::Samples(self.normalisation.normalise(samples, volume, self.factor)) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index e847fdaa9..3e66791c9 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -469,22 +469,27 @@ impl StereoInterleavedResampler { fn interleave_samples(left_samples: &[f64], right_samples: &[f64]) -> Vec { // Re-interleave the resampled channels. - left_samples - .iter() - .zip(right_samples.iter()) - .flat_map(|(&x, &y)| vec![x, y]) - .collect() + let mut output = Vec::with_capacity(left_samples.len() + right_samples.len()); + + output.extend( + left_samples + .iter() + .zip(right_samples.iter()) + .flat_map(|(&left, &right)| std::iter::once(left).chain(std::iter::once(right))), + ); + + output } fn deinterleave_samples(samples: &[f64]) -> (Vec, Vec) { // Split the stereo interleaved samples into left and right channels. - let (left_samples, right_samples): (Vec, Vec) = samples - .chunks(2) - .map(|chunk| { - let [left_sample, right_sample] = [chunk[0], chunk[1]]; - (left_sample, right_sample) - }) - .unzip(); + let samples_len = samples.len() / 2; + + let mut left_samples = Vec::with_capacity(samples_len); + let mut right_samples = Vec::with_capacity(samples_len); + + left_samples.extend(samples.iter().step_by(2)); + right_samples.extend(samples.iter().skip(1).step_by(2)); (left_samples, right_samples) } diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index 5b15cb2e6..b6bffb89d 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -70,7 +70,7 @@ impl SamplePipeline { if let AudioPacket::Samples(samples) = packet { self.resampler .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)) .transpose()?; } else { From 2fa2bf86cb385d5b915171d3a9290c2085dea88c Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 15:43:21 -0500 Subject: [PATCH 31/53] Don't handle messages that are not intended for us in spirc. This prevents 2 active instances of librespot from effectively arguing over who the current active instance actually is. Without it events recurse. --- connect/src/spirc.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 857a44092..b9d822310 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -872,6 +872,11 @@ impl SpircTask { ); } + // Don't handle messages that are not intended for us. + if !update.recipient.contains(device_id) { + return Ok(()); + } + match update.typ() { MessageType::kMessageTypeHello => self.notify(Some(ident)), From c29ec3f4731d030fd321bd3defbaa752745886ad Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 17:45:28 -0500 Subject: [PATCH 32/53] Add warning about Normalisation Attack or Release being to small --- playback/src/normaliser.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs index 8ee6d10f5..d9085785b 100644 --- a/playback/src/normaliser.rs +++ b/playback/src/normaliser.rs @@ -46,23 +46,29 @@ struct DynamicNormalisation { impl DynamicNormalisation { fn new(config: &PlayerConfig) -> Self { // as_millis() has rounding errors (truncates) - debug!( - "Normalisation Attack: {:.0} ms", - config - .sample_rate - .normalisation_coefficient_to_duration(config.normalisation_attack_cf) - .as_secs_f64() - * 1000. - ); + let attack = config + .sample_rate + .normalisation_coefficient_to_duration(config.normalisation_attack_cf) + .as_secs_f64() + * 1000.0; + + if attack < 1.0 { + warn!("Normalisation Attack: {:.0} ms, an Attack of < 1.0 ms will cause severe distortion", attack); + } else { + debug!("Normalisation Attack: {:.0} ms", attack); + } - debug!( - "Normalisation Release: {:.0} ms", - config - .sample_rate - .normalisation_coefficient_to_duration(config.normalisation_release_cf) - .as_secs_f64() - * 1000. - ); + let release = config + .sample_rate + .normalisation_coefficient_to_duration(config.normalisation_release_cf) + .as_secs_f64() + * 1000.0; + + if release < 1.0 { + warn!("Normalisation Release: {:.0} ms, a Release of < 1.0 ms will cause severe distortion", release); + } else { + debug!("Normalisation Release: {:.0} ms", release); + } Self { threshold_db: config.normalisation_threshold_dbfs, From 4b081b13651bb0b61aa8a7b55e46163427cc2325 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 18:00:39 -0500 Subject: [PATCH 33/53] Warn that trying to resample 44.1kHz to 44.1kHz is a stupid thing to do. --- playback/src/config.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 952e43bfb..fb019dbfa 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -226,10 +226,14 @@ impl SampleRate { // Dummy values to satisfy // the match statement. // 44.1kHz will be bypassed. - Hz44100 => ResampleSpec { - resample_factor_reciprocal: 1.0, - interpolation_output_size: RESAMPLER_INPUT_SIZE, - }, + Hz44100 => { + warn!("Resampling 44.1kHz to 44.1kHz is just a really CPU intensive no-op, you should not be doing it"); + + ResampleSpec { + resample_factor_reciprocal: 1.0, + interpolation_output_size: RESAMPLER_INPUT_SIZE, + } + } Hz48000 => ResampleSpec { resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL, interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE, From d1e4b4464abe7c86482892d2f827f76d58bbe7a2 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 26 Jun 2023 21:31:03 -0500 Subject: [PATCH 34/53] Break message recursion. --- connect/src/spirc.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index b9d822310..e6d467889 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -842,6 +842,7 @@ impl SpircTask { // First see if this update was intended for us. let device_id = &self.ident; let ident = update.ident(); + if ident == device_id || (!update.recipient.is_empty() && !update.recipient.contains(device_id)) { @@ -872,14 +873,7 @@ impl SpircTask { ); } - // Don't handle messages that are not intended for us. - if !update.recipient.contains(device_id) { - return Ok(()); - } - match update.typ() { - MessageType::kMessageTypeHello => self.notify(Some(ident)), - MessageType::kMessageTypeLoad => { self.handle_load(update.state.get_or_default())?; self.notify(None) @@ -996,8 +990,10 @@ impl SpircTask { && self.device.became_active_at() <= update.device_state.became_active_at() { self.handle_disconnect(); + self.notify(None)?; } - self.notify(None) + + Ok(()) } _ => Ok(()), From 841f9235497d8c65fbf7a56070ee3f99a340f8f5 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Tue, 27 Jun 2023 16:13:31 -0500 Subject: [PATCH 35/53] Remove unnecessary step Our interpolation_coefficients are already normalized. --- playback/src/resampler.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 3e66791c9..58cda2d60 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -50,7 +50,6 @@ impl<'a> IntoIterator for &'a DelayLine { struct WindowedSincInterpolator { interpolation_coefficients: Vec, - interpolation_coefficients_sum: f64, delay_line: DelayLine, } @@ -59,13 +58,10 @@ impl WindowedSincInterpolator { 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, } } @@ -76,20 +72,12 @@ impl WindowedSincInterpolator { self.delay_line.push(sample); // Temporal convolution - let mut output_sample = self - .interpolation_coefficients + 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) { From 20414e44b843c9d70493b9272b84d53df017a944 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Tue, 27 Jun 2023 17:05:52 -0500 Subject: [PATCH 36/53] More warnings --- playback/src/config.rs | 25 +++++++++++++++++++++++-- playback/src/normaliser.rs | 12 ++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index fb019dbfa..e50e52627 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -74,6 +74,11 @@ impl InterpolationQuality { let mut coefficients = Vec::with_capacity(interpolation_coefficients_length); + if interpolation_coefficients_length == 0 { + warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec because Linear Interpolation does not use coefficients"); + return coefficients; + } + let last_index = interpolation_coefficients_length as f64 - 1.0; let sinc_center = last_index * 0.5; @@ -201,11 +206,27 @@ impl SampleRate { } pub fn duration_to_normalisation_coefficient(&self, duration: Duration) -> f64 { - (-1.0 / (duration.as_secs_f64() * self.samples_per_second())).exp() + let secs = duration.as_secs_f64(); + let ms = secs * 1000.0; + + if ms < 1.0 { + warn!("Coefficient Duration: {:.0} ms, a Normalisation Attack/Release of < 1 ms will cause severe distortion", ms); + } + + (-1.0 / (secs * self.samples_per_second())).exp() } pub fn normalisation_coefficient_to_duration(&self, coefficient: f64) -> Duration { - Duration::from_secs_f64(-1.0 / coefficient.ln() / self.samples_per_second()) + let duration = Duration::from_secs_f64(-1.0 / coefficient.ln() / self.samples_per_second()); + + let secs = duration.as_secs_f64(); + let ms = secs * 1000.0; + + if ms < 1.0 { + warn!("Coefficient Duration: {:.0} ms, a Normalisation Attack/Release of < 1 ms will cause severe distortion", ms); + } + + duration } fn samples_per_second(&self) -> f64 { diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs index d9085785b..0807e4492 100644 --- a/playback/src/normaliser.rs +++ b/playback/src/normaliser.rs @@ -52,11 +52,7 @@ impl DynamicNormalisation { .as_secs_f64() * 1000.0; - if attack < 1.0 { - warn!("Normalisation Attack: {:.0} ms, an Attack of < 1.0 ms will cause severe distortion", attack); - } else { - debug!("Normalisation Attack: {:.0} ms", attack); - } + debug!("Normalisation Attack: {:.0} ms", attack); let release = config .sample_rate @@ -64,11 +60,7 @@ impl DynamicNormalisation { .as_secs_f64() * 1000.0; - if release < 1.0 { - warn!("Normalisation Release: {:.0} ms, a Release of < 1.0 ms will cause severe distortion", release); - } else { - debug!("Normalisation Release: {:.0} ms", release); - } + debug!("Normalisation Release: {:.0} ms", release); Self { threshold_db: config.normalisation_threshold_dbfs, From cea92a9c3ae147f75fab78e15c5416b155a54dcd Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Tue, 27 Jun 2023 19:21:02 -0500 Subject: [PATCH 37/53] Avoid unnecessary Vec -> Slice -> Vec In the case of a resampler bypass --- playback/src/resampler.rs | 6 +++--- playback/src/sample_pipeline.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 58cda2d60..3dfd3f3c3 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -410,15 +410,15 @@ impl StereoInterleavedResampler { self.latency_pcm } - pub fn resample(&mut self, input_samples: &[f64]) -> Option> { + pub fn resample(&mut self, input_samples: Vec) -> Option> { match &mut self.resampler { // Bypass is basically a no-op. - Resampler::Bypass => Some(input_samples.to_vec()), + Resampler::Bypass => Some(input_samples), Resampler::Worker { left_resampler, right_resampler, } => { - let (left_samples, right_samples) = Self::deinterleave_samples(input_samples); + let (left_samples, right_samples) = Self::deinterleave_samples(&input_samples); left_resampler.resample(left_samples); right_resampler.resample(right_samples); diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index b6bffb89d..bae88279d 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -69,7 +69,7 @@ impl SamplePipeline { pub fn write(&mut self, packet: AudioPacket) -> SinkResult<()> { if let AudioPacket::Samples(samples) = packet { self.resampler - .resample(&samples) + .resample(samples) .map(|processed_samples| self.normaliser.normalise(processed_samples)) .map(|new_packet| self.sink.write(new_packet, &mut self.converter)) .transpose()?; From 1ab5bac786279c9aac1965144deddcaf71fc5d31 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Wed, 28 Jun 2023 22:00:33 -0500 Subject: [PATCH 38/53] Address review comments --- connect/src/spirc.rs | 1 + playback/src/audio_backend/alsa.rs | 49 +++++++++--------------------- playback/src/config.rs | 22 ++++++++++++++ playback/src/lib.rs | 7 +++++ playback/src/player.rs | 4 +-- playback/src/resampler.rs | 2 +- src/player_event_handler.rs | 9 ++++-- 7 files changed, 55 insertions(+), 39 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index e6d467889..cec19ea17 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -874,6 +874,7 @@ impl SpircTask { } match update.typ() { + MessageType::kMessageTypeHello => self.notify(Some(ident)), MessageType::kMessageTypeLoad => { self.handle_load(update.state.get_or_default())?; self.notify(None) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index e5c311c73..285d70e25 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -1,8 +1,8 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::config::AudioFormat; +use crate::config::{AudioFormat, SampleRate}; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; +use crate::{COMMON_SAMPLE_RATES, NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; use alsa::device_name::HintIter; use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, ValueOr}; @@ -12,22 +12,6 @@ use thiserror::Error; const OPTIMAL_NUM_PERIODS: Frames = 5; const MIN_NUM_PERIODS: Frames = 2; -const COMMON_SAMPLE_RATES: [u32; 14] = [ - 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, - 768000, -]; - -const SUPPORTED_SAMPLE_RATES: [u32; 4] = [44100, 48000, 88200, 96000]; - -const FORMATS: [AudioFormat; 6] = [ - AudioFormat::S16, - AudioFormat::S24, - AudioFormat::S24_3, - AudioFormat::S32, - AudioFormat::F32, - AudioFormat::F64, -]; - #[derive(Debug, Error)] enum AlsaError { #[error(" Device {device} Unsupported Format {alsa_format} ({format:?}), {e}, Supported Format(s): {supported_formats:?}")] @@ -134,25 +118,22 @@ fn list_compatible_devices() -> SinkResult<()> { { let mut supported_formats_and_samplerates = String::new(); - for format in FORMATS.iter() { + for format in AudioFormat::default().into_iter() { let hwp = hwp.clone(); - if hwp.set_format((*format).into()).is_ok() { - let sample_rates: Vec = SUPPORTED_SAMPLE_RATES - .iter() + if hwp.set_format(format.into()).is_ok() { + let sample_rates: Vec = SampleRate::default() + .into_iter() .filter_map(|sample_rate| { let hwp = hwp.clone(); if hwp - .set_rate(*sample_rate, ValueOr::Nearest) + .set_rate( + sample_rate.as_u32(), + ValueOr::Nearest, + ) .is_ok() { - match *sample_rate { - 44100 => Some("44.1kHz".to_string()), - 48000 => Some("48kHz".to_string()), - 88200 => Some("88.2kHz".to_string()), - 96000 => Some("96kHz".to_string()), - _ => None, - } + Some(sample_rate.to_string()) } else { None } @@ -161,7 +142,7 @@ fn list_compatible_devices() -> SinkResult<()> { if !sample_rates.is_empty() { let format_and_sample_rates = - if *format == AudioFormat::S24_3 { + if format == AudioFormat::S24_3 { format!( "\n\t\tFormat: {format:?} Sample Rate(s): {}", sample_rates.join(", ") @@ -379,10 +360,10 @@ impl AlsaSink { let alsa_format = self.format.into(); hwp.set_format(alsa_format).map_err(|e| { - let supported_formats = FORMATS - .iter() + let supported_formats = AudioFormat::default() + .into_iter() .filter_map(|f| { - if hwp.test_format((*f).into()).is_ok() { + if hwp.test_format(f.into()).is_ok() { Some(format!("{f:?}")) } else { None diff --git a/playback/src/config.rs b/playback/src/config.rs index e50e52627..ca17837a0 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -151,6 +151,17 @@ pub enum SampleRate { Hz96000, } +impl IntoIterator for SampleRate { + type Item = SampleRate; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + use SampleRate::*; + + vec![Hz44100, Hz48000, Hz88200, Hz96000].into_iter() + } +} + impl FromStr for SampleRate { type Err = (); @@ -302,6 +313,17 @@ pub enum AudioFormat { S16, } +impl IntoIterator for AudioFormat { + type Item = AudioFormat; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + use AudioFormat::*; + + vec![F64, F32, S32, S24, S24_3, S16].into_iter() + } +} + impl FromStr for AudioFormat { type Err = (); fn from_str(s: &str) -> Result { diff --git a/playback/src/lib.rs b/playback/src/lib.rs index e56b2cae5..6797d6db6 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -25,6 +25,13 @@ pub const SAMPLES_PER_SECOND: u32 = SAMPLE_RATE * NUM_CHANNELS as u32; pub const PAGES_PER_MS: f64 = SAMPLE_RATE as f64 / 1000.0; pub const MS_PER_PAGE: f64 = 1000.0 / SAMPLE_RATE as f64; +// not used by all backends +#[allow(dead_code)] +const COMMON_SAMPLE_RATES: [u32; 14] = [ + 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, + 768000, +]; + pub fn db_to_ratio(db: f64) -> f64 { f64::powf(10.0, db / DB_VOLTAGE_RATIO) } diff --git a/playback/src/player.rs b/playback/src/player.rs index 5d1662213..e522de1e8 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -324,7 +324,7 @@ impl Player { { let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); - let player_id = PLAYER_COUNTER.fetch_add(1, Ordering::AcqRel); + let player_id = PLAYER_COUNTER.fetch_add(1, Ordering::SeqCst); let thread_name = format!("player:{}", player_id); @@ -1954,7 +1954,7 @@ impl PlayerInternal { // The player increments the player id when it gets it... let thread_name = format!( "loader:{}:{}", - PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1), + PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1), spotify_id.to_uri().unwrap_or_default() ); diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 3dfd3f3c3..b921df2f5 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -368,7 +368,7 @@ impl StereoInterleavedResampler { debug!("Interpolation Quality: {interpolation_quality}"); // The player increments the player id when it gets it... - let player_id = PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1); + let player_id = PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1); let left_thread_name = format!("resampler:{player_id}:left"); let right_thread_name = format!("resampler:{player_id}:right"); diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 4fe2a271a..0c16a0535 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,6 +1,11 @@ use log::{debug, error, warn}; -use std::{collections::HashMap, process::exit, process::Command, sync::atomic::Ordering, thread}; +use std::{ + collections::HashMap, + process::{exit, Command}, + sync::atomic::Ordering, + thread, +}; use librespot::{ metadata::audio::UniqueFields, @@ -18,7 +23,7 @@ impl EventHandler { // The player increments the player id when it gets it... let thread_name = format!( "event-handler:{}", - PLAYER_COUNTER.load(Ordering::Relaxed).saturating_sub(1) + PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1) ); let builder = thread::Builder::new().name(thread_name.clone()); From 15e2b441aa3d888db8a9b81b1aca814ac720bab9 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 29 Jun 2023 16:35:54 -0500 Subject: [PATCH 39/53] Fully implement CommonSampleRates --- playback/src/audio_backend/alsa.rs | 18 ++++- playback/src/lib.rs | 117 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 285d70e25..86010e50c 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -2,7 +2,7 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::config::{AudioFormat, SampleRate}; use crate::convert::Converter; use crate::decoder::AudioPacket; -use crate::{COMMON_SAMPLE_RATES, NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; +use crate::{CommonSampleRates, NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; use alsa::device_name::HintIter; use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, ValueOr}; @@ -35,7 +35,7 @@ enum AlsaError { UnsupportedSampleRate { device: String, samplerate: u32, - supported_rates: Vec, + supported_rates: Vec, e: alsa::Error, }, @@ -382,9 +382,21 @@ impl AlsaSink { hwp.set_rate(self.sample_rate, ValueOr::Nearest) .map_err(|e| { + let common_sample_rates = CommonSampleRates::default(); + let supported_rates = (hwp.get_rate_min().unwrap_or_default() ..=hwp.get_rate_max().unwrap_or_default()) - .filter(|r| COMMON_SAMPLE_RATES.contains(r) && hwp.test_rate(*r).is_ok()) + .filter_map(|r| { + if common_sample_rates.contains(r) && hwp.test_rate(r).is_ok() { + Some( + CommonSampleRates::try_from(r) + .unwrap_or_default() + .to_string(), + ) + } else { + None + } + }) .collect(); AlsaError::UnsupportedSampleRate { diff --git a/playback/src/lib.rs b/playback/src/lib.rs index 6797d6db6..ed206de04 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -1,3 +1,6 @@ +use std::convert::TryFrom; +use std::fmt; + #[macro_use] extern crate log; @@ -39,3 +42,117 @@ pub fn db_to_ratio(db: f64) -> f64 { pub fn ratio_to_db(ratio: f64) -> f64 { ratio.log10() * DB_VOLTAGE_RATIO } + +// not used by all backends +#[allow(dead_code)] +#[derive(Copy, Clone, Debug, Default)] +pub enum CommonSampleRates { + #[default] + Hz8000, + Hz11025, + Hz16000, + Hz22050, + Hz44100, + Hz48000, + Hz88200, + Hz96000, + Hz176400, + Hz192000, + Hz352800, + Hz384000, + Hz705600, + Hz768000, +} + +impl TryFrom for CommonSampleRates { + type Error = (); + + fn try_from(value: u32) -> Result { + use CommonSampleRates::*; + + match value { + 8000 => Ok(Hz8000), + 11025 => Ok(Hz11025), + 16000 => Ok(Hz16000), + 22050 => Ok(Hz22050), + 44100 => Ok(Hz44100), + 48000 => Ok(Hz48000), + 88200 => Ok(Hz88200), + 96000 => Ok(Hz96000), + 176400 => Ok(Hz176400), + 192000 => Ok(Hz192000), + 352800 => Ok(Hz352800), + 384000 => Ok(Hz384000), + 705600 => Ok(Hz705600), + 768000 => Ok(Hz768000), + _ => Err(()), + } + } +} + +impl fmt::Display for CommonSampleRates { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use CommonSampleRates::*; + + let rate_str = match self { + Hz8000 => "8kHz", + Hz11025 => "11.025kHz", + Hz16000 => "16kHz", + Hz22050 => "22.05kHz", + Hz44100 => "44.1kHz", + Hz48000 => "48kHz", + Hz88200 => "88.2kHz", + Hz96000 => "96kHz", + Hz176400 => "176.4kHz", + Hz192000 => "192kHz", + Hz352800 => "352.8kHz", + Hz384000 => "384kHz", + Hz705600 => "705.6kHz", + Hz768000 => "768kHz", + }; + + write!(f, "{}", rate_str) + } +} + +impl IntoIterator for CommonSampleRates { + type Item = CommonSampleRates; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + use CommonSampleRates::*; + + vec![ + Hz8000, Hz11025, Hz16000, Hz22050, Hz44100, Hz48000, Hz88200, Hz96000, Hz176400, + Hz192000, Hz352800, Hz384000, Hz705600, Hz768000, + ] + .into_iter() + } +} + +impl CommonSampleRates { + pub fn as_u32(&self) -> u32 { + use CommonSampleRates::*; + + match self { + Hz8000 => 8000, + Hz11025 => 11025, + Hz16000 => 16000, + Hz22050 => 22050, + Hz44100 => 44100, + Hz48000 => 48000, + Hz88200 => 88200, + Hz96000 => 96000, + Hz176400 => 176400, + Hz192000 => 192000, + Hz352800 => 352800, + Hz384000 => 384000, + Hz705600 => 705600, + Hz768000 => 768000, + } + } + + pub fn contains(&self, rate: u32) -> bool { + self.into_iter().any(|r| r.as_u32() == rate) + } +} From 242e68a98cc44b68b2d7688127c09299afe6dfde Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 30 Jun 2023 10:56:44 -0500 Subject: [PATCH 40/53] Revert remaining spirc changes It's best to deal with this after discovery is fixed. --- connect/src/spirc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index cec19ea17..396970fdb 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -991,10 +991,8 @@ impl SpircTask { && self.device.became_active_at() <= update.device_state.became_active_at() { self.handle_disconnect(); - self.notify(None)?; } - - Ok(()) + self.notify(None) } _ => Ok(()), From 25425dab5cf484d15cd348f694646c3944a98580 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 30 Jun 2023 12:36:12 -0500 Subject: [PATCH 41/53] Put CommonSampleRates to some use and clean up backend imports --- playback/src/audio_backend/alsa.rs | 28 +++++++++++++++++------- playback/src/audio_backend/gstreamer.rs | 12 ++++++++-- playback/src/audio_backend/jackaudio.rs | 17 +++++++++----- playback/src/audio_backend/pipe.rs | 20 +++++++++++------ playback/src/audio_backend/portaudio.rs | 26 ++++++++++++++-------- playback/src/audio_backend/pulseaudio.rs | 17 +++++++++----- playback/src/audio_backend/rodio.rs | 25 +++++++++++---------- playback/src/audio_backend/sdl.rs | 20 +++++++++++------ playback/src/audio_backend/subprocess.rs | 21 ++++++++++++------ playback/src/lib.rs | 9 -------- 10 files changed, 123 insertions(+), 72 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 86010e50c..8b138618a 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -1,11 +1,18 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::config::{AudioFormat, SampleRate}; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::{CommonSampleRates, NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; -use alsa::device_name::HintIter; -use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; -use alsa::{Direction, ValueOr}; + +use crate::{ + config::{AudioFormat, SampleRate}, + convert::Converter, + decoder::AudioPacket, + CommonSampleRates, NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE, +}; + +use alsa::{ + device_name::HintIter, + pcm::{Access, Format, Frames, HwParams, PCM}, + Direction, ValueOr, +}; + use std::process::exit; use thiserror::Error; @@ -204,7 +211,12 @@ impl Open for AlsaSink { let latency_scale_factor = DECODER_SAMPLE_RATE as f64 / sample_rate as f64; - info!("Using AlsaSink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using AlsaSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); Self { pcm: None, diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index ec748a982..f70ee37f0 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -13,7 +13,9 @@ use std::sync::Arc; use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::{config::AudioFormat, convert::Converter, decoder::AudioPacket, NUM_CHANNELS}; +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, +}; pub struct GstreamerSink { appsrc: gst_app::AppSrc, @@ -25,7 +27,13 @@ pub struct GstreamerSink { impl Open for GstreamerSink { fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { - info!("Using GStreamer sink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using GstreamerSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); + gst::init().expect("failed to init GStreamer!"); let gst_format = match format { diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 1122a427b..1236e1f30 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -1,11 +1,13 @@ use super::{Open, Sink, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::NUM_CHANNELS; + +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, +}; + use jack::{ AsyncClient, AudioOut, Client, ClientOptions, Control, Port, ProcessHandler, ProcessScope, }; + use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; pub struct JackSink { @@ -42,9 +44,12 @@ impl Open for JackSink { if format != AudioFormat::F32 { warn!("JACK currently does not support {format:?} output"); } + info!( - "Using JACK sink with format {:?}, sample rate: {sample_rate}", - AudioFormat::F32 + "Using JackSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() ); let client_name = client_name.unwrap_or_else(|| "librespot".to_string()); diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 05db49534..6c0751e73 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -1,11 +1,12 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; +use crate::{config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates}; + +use std::{ + fs::OpenOptions, + io::{self, Write}, + process::exit, +}; -use std::fs::OpenOptions; -use std::io::{self, Write}; -use std::process::exit; use thiserror::Error; #[derive(Debug, Error)] @@ -48,7 +49,12 @@ impl Open for StdoutSink { exit(0); } - info!("Using StdoutSink (pipe) with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using StdoutSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); Self { output: None, diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 178c8386a..2868926be 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -1,12 +1,15 @@ use super::{Open, Sink, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::NUM_CHANNELS; -use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo}; -use portaudio_rs::stream::*; -use std::process::exit; -use std::time::Duration; + +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, +}; + +use portaudio_rs::{ + device::{get_default_output_index, DeviceIndex, DeviceInfo}, + stream::*, +}; + +use std::{process::exit, time::Duration}; pub enum PortAudioSink<'a> { F32( @@ -55,7 +58,12 @@ fn find_output(device: &str) -> Option { impl<'a> Open for PortAudioSink<'a> { fn open(device: Option, format: AudioFormat, sample_rate: u32) -> PortAudioSink<'a> { - info!("Using PortAudio sink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using PortAudioSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); portaudio_rs::initialize().unwrap(); diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 25ad8c5d2..b43f1886e 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -1,8 +1,10 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE as DECODER_SAMPLE_RATE}; + +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, + SAMPLE_RATE as DECODER_SAMPLE_RATE, +}; + use libpulse_binding::{self as pulse, error::PAErr, stream::Direction}; use libpulse_simple_binding::Simple; use std::env; @@ -76,7 +78,12 @@ impl Open for PulseAudioSink { format }; - info!("Using PulseAudioSink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using PulseAudioSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); let sample_spec = pulse::sample::Spec { format: format.into(), diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 05a060cbe..956014a4a 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -1,15 +1,13 @@ -use std::process::exit; -use std::thread; -use std::time::Duration; +use std::{process::exit, thread, time::Duration}; use cpal::traits::{DeviceTrait, HostTrait}; use thiserror::Error; use super::{Sink, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::NUM_CHANNELS; + +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, +}; #[cfg(all( feature = "rodiojack-backend", @@ -176,15 +174,18 @@ pub fn open( format: AudioFormat, sample_rate: u32, ) -> RodioSink { - info!( - "Using Rodio sink with format {format:?} and cpal host: {}", - host.id().name() - ); - if format != AudioFormat::S16 && format != AudioFormat::F32 { unimplemented!("Rodio currently only supports F32 and S16 formats"); } + info!( + "Using RodioSink with format: {format:?}, sample rate: {}, and cpal host: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string(), + host.id().name(), + ); + let (sink, stream) = create_sink(&host, device).unwrap(); debug!("Rodio sink was created"); diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index ce7c146b9..a3856a256 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -1,11 +1,12 @@ use super::{Open, Sink, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use crate::{NUM_CHANNELS, SAMPLE_RATE}; + +use crate::{ + config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates, NUM_CHANNELS, + SAMPLE_RATE, +}; + use sdl2::audio::{AudioQueue, AudioSpecDesired}; -use std::thread; -use std::time::Duration; +use std::{thread, time::Duration}; pub enum SdlSink { F32(AudioQueue), @@ -15,7 +16,12 @@ pub enum SdlSink { impl Open for SdlSink { fn open(device: Option, format: AudioFormat, sample_rate: u32) -> Self { - info!("Using SDL sink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using SdlSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); if device.is_some() { warn!("SDL sink does not support specifying a device name"); diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 15778dbef..21f9a291b 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -1,11 +1,13 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; -use crate::config::AudioFormat; -use crate::convert::Converter; -use crate::decoder::AudioPacket; -use shell_words::split; -use std::io::{ErrorKind, Write}; -use std::process::{exit, Child, Command, Stdio}; +use crate::{config::AudioFormat, convert::Converter, decoder::AudioPacket, CommonSampleRates}; + +use std::{ + io::{ErrorKind, Write}, + process::{exit, Child, Command, Stdio}, +}; + +use shell_words::split; use thiserror::Error; #[derive(Debug, Error)] @@ -72,7 +74,12 @@ impl Open for SubprocessSink { exit(0); } - info!("Using SubprocessSink with format: {format:?}, sample rate: {sample_rate}"); + info!( + "Using SubprocessSink with format: {format:?}, sample rate: {}", + CommonSampleRates::try_from(sample_rate) + .unwrap_or_default() + .to_string() + ); Self { shell_command, diff --git a/playback/src/lib.rs b/playback/src/lib.rs index ed206de04..c7d8297d1 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -28,13 +28,6 @@ pub const SAMPLES_PER_SECOND: u32 = SAMPLE_RATE * NUM_CHANNELS as u32; pub const PAGES_PER_MS: f64 = SAMPLE_RATE as f64 / 1000.0; pub const MS_PER_PAGE: f64 = 1000.0 / SAMPLE_RATE as f64; -// not used by all backends -#[allow(dead_code)] -const COMMON_SAMPLE_RATES: [u32; 14] = [ - 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, - 768000, -]; - pub fn db_to_ratio(db: f64) -> f64 { f64::powf(10.0, db / DB_VOLTAGE_RATIO) } @@ -43,8 +36,6 @@ pub fn ratio_to_db(ratio: f64) -> f64 { ratio.log10() * DB_VOLTAGE_RATIO } -// not used by all backends -#[allow(dead_code)] #[derive(Copy, Clone, Debug, Default)] pub enum CommonSampleRates { #[default] From c4dc7347c7df5e686f8c9374b3373c92eae0cd86 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Fri, 30 Jun 2023 13:55:34 -0500 Subject: [PATCH 42/53] Change Interpolation Quality to default to High --- playback/src/config.rs | 2 +- src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index ca17837a0..0a44853fa 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -35,9 +35,9 @@ const FOUR_TIMES_PI: f64 = 4.0 * std::f64::consts::PI; #[derive(Clone, Copy, Debug, Default)] pub enum InterpolationQuality { - #[default] Low, Medium, + #[default] High, } diff --git a/src/main.rs b/src/main.rs index 8e6d7fad2..0b7d10ae2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -582,7 +582,7 @@ fn get_setup() -> Setup { ).optopt( "", INTERPOLATION_QUALITY, - "Interpolation Quality to use if Resampling {Low|Medium|High}. Defaults to Low.", + "Interpolation Quality to use if Resampling {Low|Medium|High}. Defaults to High.", "QUALITY" ).optopt( "", From 3dafb5904edc9130d38b499019ad882f66dc888e Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sat, 1 Jul 2023 22:25:48 -0500 Subject: [PATCH 43/53] Fix up for #1178 sys.uptime() returns sec not mins so it should be 60 not 1. --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 0b7d10ae2..f31526cb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1757,7 +1757,7 @@ async fn main() { Err(e) => { sys.refresh_processes(); - if sys.uptime() <= 1 { + if sys.uptime() <= 60 { debug!("Retrying to initialise discovery: {e}"); tokio::time::sleep(DISCOVERY_RETRY_TIMEOUT).await; } else { From 74e3f938da8668766fe0a99c8154121ee3995017 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sun, 2 Jul 2023 03:52:40 -0500 Subject: [PATCH 44/53] Make sure that decoder picks up where it left of going from paused to play Since we are including the pipeline latency in the position we need to seek to the correct position when going from paused to play. We can also drop the ALSA and PulseAudio buffers instead of draining them since their latency's are factored in. --- playback/src/audio_backend/alsa.rs | 12 +++--------- playback/src/audio_backend/pulseaudio.rs | 2 +- playback/src/player.rs | 4 +++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 8b138618a..45bb8375b 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -52,9 +52,6 @@ enum AlsaError { #[error(" Device {device} May be Invalid, Busy, or Already in Use, {e}")] PcmSetUp { device: String, e: alsa::Error }, - #[error(" Failed to Drain PCM Buffer, {0}")] - DrainFailure(alsa::Error), - #[error(" {0}")] OnWrite(alsa::Error), @@ -76,7 +73,7 @@ impl From for SinkError { use AlsaError::*; let es = e.to_string(); match e { - DrainFailure(_) | OnWrite(_) => SinkError::OnWrite(es), + OnWrite(_) => SinkError::OnWrite(es), PcmSetUp { .. } => SinkError::ConnectionRefused(es), _ => SinkError::InvalidParams(es), } @@ -239,11 +236,8 @@ impl Sink for AlsaSink { } fn stop(&mut self) -> SinkResult<()> { - self.write_buf()?; - - if let Some(pcm) = self.pcm.take() { - pcm.drain().map_err(AlsaError::DrainFailure)?; - } + self.period_buffer.clear(); + self.pcm = None; Ok(()) } diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index b43f1886e..cae97d1b3 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -137,7 +137,7 @@ impl Sink for PulseAudioSink { fn stop(&mut self) -> SinkResult<()> { if let Some(sink) = self.sink.take() { - sink.drain().map_err(PulseError::DrainFailure)?; + sink.flush().map_err(PulseError::DrainFailure)?; } Ok(()) diff --git a/playback/src/player.rs b/playback/src/player.rs index e522de1e8..aa29c818f 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -676,7 +676,7 @@ impl PlayerState { Paused { track_id, play_request_id, - decoder, + mut decoder, audio_item, normalisation_data, stream_loader_controller, @@ -686,6 +686,8 @@ impl PlayerState { suggested_to_preload_next_track, is_explicit, } => { + let stream_position_ms = decoder.seek(stream_position_ms).unwrap_or_default(); + *self = Playing { track_id, play_request_id, From 87bbd539b7a595ba34d6981cdeba38bb2f423fd9 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sun, 2 Jul 2023 17:59:11 -0500 Subject: [PATCH 45/53] Add anti-alias filtering just in case Cap the output bandwidth to 92%. Even at 48kHz it still translates to 100% source bandwidth. This just provides a little bit of anti-alias filtering. There is more then likely nothing there to filter, but it doesn't hurt or cost us anything to make sure. --- playback/src/config.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 0a44853fa..ee8f1b761 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -19,11 +19,20 @@ const HZ96000_SAMPLES_PER_SECOND: f64 = 96_000.0 * 2.0; // 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; + const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ88200_RESAMPLE_FACTOR_RECIPROCAL)) as usize; + const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; +// This is the bandwidth of the output. +// Even at 48kHz it still translates to 100% source bandwidth. +// This just provides a little bit of anti-alias filtering. +// There is more then likely nothing there to filter, +// but it doesn't hurt or cost us anything to make sure. +const WINDOWED_SINC_INTERPOLATION_BANDWIDTH: f64 = 0.92; + // Blackman Window coefficients const BLACKMAN_A0: f64 = 0.42; const BLACKMAN_A1: f64 = 0.5; @@ -89,7 +98,8 @@ impl InterpolationQuality { |interpolation_coefficient_index| { let index_float = interpolation_coefficient_index as f64; let sample_index_fractional = (index_float * resample_factor_reciprocal).fract(); - let sinc_center_offset = index_float - sinc_center; + let sinc_center_offset = + (index_float - sinc_center) * WINDOWED_SINC_INTERPOLATION_BANDWIDTH; let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional); From cb8f6c954d8a9f9bdfb349fa088e8c09bb0bf253 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sun, 2 Jul 2023 22:13:20 -0500 Subject: [PATCH 46/53] Duh, just use resample_factor_reciprocal The resample_factor_reciprocal also happens to be our anti-alias cutoff. In this case it represents the minimum output bandwidth needed to fully represent the input. --- playback/src/config.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index ee8f1b761..7e53bbc28 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -26,13 +26,6 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; -// This is the bandwidth of the output. -// Even at 48kHz it still translates to 100% source bandwidth. -// This just provides a little bit of anti-alias filtering. -// There is more then likely nothing there to filter, -// but it doesn't hurt or cost us anything to make sure. -const WINDOWED_SINC_INTERPOLATION_BANDWIDTH: f64 = 0.92; - // Blackman Window coefficients const BLACKMAN_A0: f64 = 0.42; const BLACKMAN_A1: f64 = 0.5; @@ -99,7 +92,10 @@ impl InterpolationQuality { let index_float = interpolation_coefficient_index as f64; let sample_index_fractional = (index_float * resample_factor_reciprocal).fract(); let sinc_center_offset = - (index_float - sinc_center) * WINDOWED_SINC_INTERPOLATION_BANDWIDTH; + // The resample_factor_reciprocal also happens to be our + // anti-alias cutoff. In this case it represents the minimum + // output bandwidth needed to fully represent the input. + (index_float - sinc_center) * resample_factor_reciprocal; let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional); From 33e2ce65d3d920388a26b16162683ff8cec247e0 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 3 Jul 2023 14:10:21 -0500 Subject: [PATCH 47/53] Anti-alias Linear Interpolation also. --- playback/src/config.rs | 98 +++++++++++++++++++++++++++++++-------- playback/src/resampler.rs | 82 +++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 31 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 7e53bbc28..4efae2b41 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -26,6 +26,8 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; +pub const NUM_FIR_FILTER_TAPS: usize = 5; + // Blackman Window coefficients const BLACKMAN_A0: f64 = 0.42; const BLACKMAN_A1: f64 = 0.5; @@ -77,7 +79,9 @@ impl InterpolationQuality { let mut coefficients = Vec::with_capacity(interpolation_coefficients_length); if interpolation_coefficients_length == 0 { - warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec because Linear Interpolation does not use coefficients"); + warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec"); + warn!("Linear Interpolation does not use coefficients"); + return coefficients; } @@ -91,36 +95,61 @@ impl InterpolationQuality { |interpolation_coefficient_index| { let index_float = interpolation_coefficient_index as f64; let sample_index_fractional = (index_float * resample_factor_reciprocal).fract(); - let sinc_center_offset = - // The resample_factor_reciprocal also happens to be our - // anti-alias cutoff. In this case it represents the minimum - // output bandwidth needed to fully represent the input. - (index_float - sinc_center) * resample_factor_reciprocal; let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional); - let sinc_value = Self::sinc(sinc_center_offset); - // Calculate the Blackman window function for the given center offset - // w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)), - // where n is the center offset, N is the window size, - // and A0, A1, A2 are precalculated coefficients + let fir_filter = Self::fir_filter( + index_float, + last_index, + sinc_center, + resample_factor_reciprocal, + ); + + let coefficient = sample_index_fractional_sinc_weight * fir_filter; + + coefficient_sum += coefficient; + + coefficient + }, + )); + + coefficients + .iter_mut() + .for_each(|coefficient| *coefficient /= coefficient_sum); + + coefficients + } + + pub fn get_fir_filter_coefficients(&self, resample_factor_reciprocal: f64) -> Vec { + let mut coefficients = Vec::with_capacity(NUM_FIR_FILTER_TAPS); + + if self.get_interpolation_coefficients_length() != 0 { + warn!("InterpolationQuality::Medium/High::get_fir_filter_coefficients always returns an empty Vec"); + warn!("The FIR Filter coefficients are a part of the Windowed Sinc Interpolation coefficients"); + + return coefficients; + } - let two_pi_n = TWO_TIMES_PI * index_float; - let four_pi_n = FOUR_TIMES_PI * index_float; + let last_index = NUM_FIR_FILTER_TAPS as f64 - 1.0; - let blackman_window_value = BLACKMAN_A0 - - BLACKMAN_A1 * (two_pi_n / last_index).cos() - + BLACKMAN_A2 * (four_pi_n / last_index).cos(); + let sinc_center = last_index * 0.5; - let sinc_window = sinc_value * blackman_window_value; + let mut coefficient_sum = 0.0; - let coefficient = sinc_window * sample_index_fractional_sinc_weight; + coefficients.extend( + (0..NUM_FIR_FILTER_TAPS).map(|fir_filter_coefficient_index| { + let coefficient = Self::fir_filter( + fir_filter_coefficient_index as f64, + last_index, + sinc_center, + resample_factor_reciprocal, + ); coefficient_sum += coefficient; coefficient - }, - )); + }), + ); coefficients .iter_mut() @@ -146,6 +175,35 @@ impl InterpolationQuality { pi_x.sin() / pi_x } } + + fn blackman(index: f64, last_index: f64) -> f64 { + // Calculate the Blackman window function for the given center offset + // w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)), + // where n is the center offset, N is the window size, + // and A0, A1, A2 are precalculated coefficients + let two_pi_n = TWO_TIMES_PI * index; + let four_pi_n = FOUR_TIMES_PI * index; + + BLACKMAN_A0 - BLACKMAN_A1 * (two_pi_n / last_index).cos() + + BLACKMAN_A2 * (four_pi_n / last_index).cos() + } + + fn fir_filter( + index: f64, + last_index: f64, + sinc_center: f64, + resample_factor_reciprocal: f64, + ) -> f64 { + // The resample_factor_reciprocal also happens to be our + // anti-alias cutoff. In this case it represents the minimum + // output bandwidth needed to fully represent the input. + let adjusted_sinc_center_offset = (index - sinc_center) * resample_factor_reciprocal; + + let sinc_value = Self::sinc(adjusted_sinc_center_offset); + let blackman_window_value = Self::blackman(index, last_index); + + sinc_value * blackman_window_value + } } #[derive(Clone, Copy, Debug, Default)] diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index b921df2f5..9c00bd1e2 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -8,28 +8,28 @@ use std::{ }; use crate::{ - config::{InterpolationQuality, SampleRate}, + config::{InterpolationQuality, SampleRate, NUM_FIR_FILTER_TAPS}, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE, }; struct DelayLine { buffer: VecDeque, - interpolation_coefficients_length: usize, + coefficients_length: usize, } impl DelayLine { - fn new(interpolation_coefficients_length: usize) -> DelayLine { + fn new(coefficients_length: usize) -> DelayLine { Self { - buffer: VecDeque::with_capacity(interpolation_coefficients_length), - interpolation_coefficients_length, + buffer: VecDeque::with_capacity(coefficients_length), + coefficients_length, } } fn push(&mut self, sample: f64) { self.buffer.push_back(sample); - while self.buffer.len() > self.interpolation_coefficients_length { + while self.buffer.len() > self.coefficients_length { self.buffer.pop_front(); } } @@ -48,12 +48,21 @@ impl<'a> IntoIterator for &'a DelayLine { } } +trait Convoluter { + fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self + where + Self: Sized; + + fn convolute(&mut self, sample: f64) -> f64; + fn clear(&mut self); +} + struct WindowedSincInterpolator { interpolation_coefficients: Vec, delay_line: DelayLine, } -impl WindowedSincInterpolator { +impl Convoluter for WindowedSincInterpolator { fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self { let interpolation_coefficients = interpolation_quality.get_interpolation_coefficients(resample_factor_reciprocal); @@ -66,7 +75,7 @@ impl WindowedSincInterpolator { } } - fn interpolate(&mut self, sample: f64) -> f64 { + fn convolute(&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); @@ -85,6 +94,41 @@ impl WindowedSincInterpolator { } } +struct LinearFirFilter { + fir_filter_coefficients: Vec, + delay_line: DelayLine, +} + +impl Convoluter for LinearFirFilter { + fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self { + let fir_filter_coefficients = + interpolation_quality.get_fir_filter_coefficients(resample_factor_reciprocal); + + let delay_line = DelayLine::new(fir_filter_coefficients.len()); + + Self { + fir_filter_coefficients, + delay_line, + } + } + + fn convolute(&mut self, sample: f64) -> f64 { + self.delay_line.push(sample); + + // Temporal convolution + self.fir_filter_coefficients + .iter() + .zip(&self.delay_line) + .fold(0.0, |acc, (coefficient, delay_line_sample)| { + acc + coefficient * delay_line_sample + }) + } + + fn clear(&mut self) { + self.delay_line.clear(); + } +} + trait MonoResampler { fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self where @@ -156,7 +200,7 @@ impl MonoResampler for MonoSincResampler { // 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.interpolator.convolute(sample) })); self.input_buffer.drain(..input_size); @@ -166,27 +210,39 @@ impl MonoResampler for MonoSincResampler { } struct MonoLinearResampler { + fir_filter: LinearFirFilter, input_buffer: Vec, resample_factor_reciprocal: f64, + delay_line_latency: u64, interpolation_output_size: usize, } impl MonoResampler for MonoLinearResampler { - fn new(sample_rate: SampleRate, _: InterpolationQuality) -> Self { + fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self { let spec = sample_rate.get_resample_spec(); + let delay_line_latency = + (NUM_FIR_FILTER_TAPS as f64 * spec.resample_factor_reciprocal) as u64; + Self { + fir_filter: LinearFirFilter::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.input_buffer.len() as u64 + self.delay_line_latency } fn stop(&mut self) { + self.fir_filter.clear(); self.input_buffer.clear(); } @@ -220,6 +276,10 @@ impl MonoResampler for MonoLinearResampler { // Remove the last garbage sample. output.pop(); + output + .iter_mut() + .for_each(|sample| *sample = self.fir_filter.convolute(*sample)); + self.input_buffer.drain(..input_size); Some(output) From ac68d2431e75e214882a4091138ac8d7944d87dd Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 3 Jul 2023 15:20:23 -0500 Subject: [PATCH 48/53] DRAY things up a bit --- playback/src/resampler.rs | 78 +++++++-------------------------------- 1 file changed, 14 insertions(+), 64 deletions(-) diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 9c00bd1e2..a35dcefa3 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -48,66 +48,17 @@ impl<'a> IntoIterator for &'a DelayLine { } } -trait Convoluter { - fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self - where - Self: Sized; - - fn convolute(&mut self, sample: f64) -> f64; - fn clear(&mut self); -} - -struct WindowedSincInterpolator { - interpolation_coefficients: Vec, - delay_line: DelayLine, -} - -impl Convoluter for WindowedSincInterpolator { - fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self { - let interpolation_coefficients = - interpolation_quality.get_interpolation_coefficients(resample_factor_reciprocal); - - let delay_line = DelayLine::new(interpolation_coefficients.len()); - - Self { - interpolation_coefficients, - delay_line, - } - } - - fn convolute(&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 - self.interpolation_coefficients - .iter() - .zip(&self.delay_line) - .fold(0.0, |acc, (coefficient, delay_line_sample)| { - acc + coefficient * delay_line_sample - }) - } - - fn clear(&mut self) { - self.delay_line.clear(); - } -} - -struct LinearFirFilter { - fir_filter_coefficients: Vec, +struct ConvolutionFilter { + coefficients: Vec, delay_line: DelayLine, } -impl Convoluter for LinearFirFilter { - fn new(interpolation_quality: InterpolationQuality, resample_factor_reciprocal: f64) -> Self { - let fir_filter_coefficients = - interpolation_quality.get_fir_filter_coefficients(resample_factor_reciprocal); - - let delay_line = DelayLine::new(fir_filter_coefficients.len()); +impl ConvolutionFilter { + fn new(coefficients: Vec) -> Self { + let delay_line = DelayLine::new(coefficients.len()); Self { - fir_filter_coefficients, + coefficients, delay_line, } } @@ -116,7 +67,7 @@ impl Convoluter for LinearFirFilter { self.delay_line.push(sample); // Temporal convolution - self.fir_filter_coefficients + self.coefficients .iter() .zip(&self.delay_line) .fold(0.0, |acc, (coefficient, delay_line_sample)| { @@ -140,7 +91,7 @@ trait MonoResampler { } struct MonoSincResampler { - interpolator: WindowedSincInterpolator, + interpolator: ConvolutionFilter, input_buffer: Vec, resample_factor_reciprocal: f64, delay_line_latency: u64, @@ -156,9 +107,9 @@ impl MonoResampler for MonoSincResampler { * spec.resample_factor_reciprocal) as u64; Self { - interpolator: WindowedSincInterpolator::new( - interpolation_quality, - spec.resample_factor_reciprocal, + interpolator: ConvolutionFilter::new( + interpolation_quality + .get_interpolation_coefficients(spec.resample_factor_reciprocal), ), input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize), @@ -210,7 +161,7 @@ impl MonoResampler for MonoSincResampler { } struct MonoLinearResampler { - fir_filter: LinearFirFilter, + fir_filter: ConvolutionFilter, input_buffer: Vec, resample_factor_reciprocal: f64, delay_line_latency: u64, @@ -225,9 +176,8 @@ impl MonoResampler for MonoLinearResampler { (NUM_FIR_FILTER_TAPS as f64 * spec.resample_factor_reciprocal) as u64; Self { - fir_filter: LinearFirFilter::new( - interpolation_quality, - spec.resample_factor_reciprocal, + fir_filter: ConvolutionFilter::new( + interpolation_quality.get_fir_filter_coefficients(spec.resample_factor_reciprocal), ), input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize), From e31293cc107b0c0c02dc0245efcaf1d4584332ed Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Tue, 4 Jul 2023 04:02:39 -0500 Subject: [PATCH 49/53] Better Filters All Windows calculated with pyfda (Python Filter Design Analysis Tool) https://github.com/chipmuenk/pyfda Window = Kaiser beta = 8.6 (Similar to a Blackman Window) fc = 22.5kHz -86dB by 23kHz This also gets rid of Linear Interpolation which leaves only Low and High both being Windowed Sinc. --- playback/src/config.rs | 154 +--- playback/src/filter_coefficients.rs | 1187 +++++++++++++++++++++++++++ playback/src/lib.rs | 1 + playback/src/resampler.rs | 132 +-- src/main.rs | 4 +- 5 files changed, 1233 insertions(+), 245 deletions(-) create mode 100644 playback/src/filter_coefficients.rs diff --git a/playback/src/config.rs b/playback/src/config.rs index 4efae2b41..85449eb48 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,7 +1,14 @@ use std::{mem, str::FromStr, time::Duration}; pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer}; -use crate::{convert::i24, RESAMPLER_INPUT_SIZE, SAMPLE_RATE}; + +use crate::{ + convert::i24, + filter_coefficients::{ + HZ48000_HIGH, HZ48000_LOW, HZ88200_HIGH, HZ88200_LOW, HZ96000_HIGH, HZ96000_LOW, + }, + 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; @@ -26,21 +33,9 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; -pub const NUM_FIR_FILTER_TAPS: usize = 5; - -// Blackman Window coefficients -const BLACKMAN_A0: f64 = 0.42; -const BLACKMAN_A1: f64 = 0.5; -const BLACKMAN_A2: f64 = 0.08; - -// Constants for calculations -const TWO_TIMES_PI: f64 = 2.0 * std::f64::consts::PI; -const FOUR_TIMES_PI: f64 = 4.0 * std::f64::consts::PI; - #[derive(Clone, Copy, Debug, Default)] pub enum InterpolationQuality { Low, - Medium, #[default] High, } @@ -53,7 +48,6 @@ impl FromStr for InterpolationQuality { match s.to_lowercase().as_ref() { "low" => Ok(Low), - "medium" => Ok(Medium), "high" => Ok(High), _ => Err(()), } @@ -66,91 +60,25 @@ impl std::fmt::Display for InterpolationQuality { match self { Low => write!(f, "Low"), - Medium => write!(f, "Medium"), High => write!(f, "High"), } } } impl InterpolationQuality { - pub fn get_interpolation_coefficients(&self, resample_factor_reciprocal: f64) -> Vec { - let interpolation_coefficients_length = self.get_interpolation_coefficients_length(); - - let mut coefficients = Vec::with_capacity(interpolation_coefficients_length); - - if interpolation_coefficients_length == 0 { - warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec"); - warn!("Linear Interpolation does not use coefficients"); - - return coefficients; - } - - let last_index = interpolation_coefficients_length as f64 - 1.0; - - let sinc_center = last_index * 0.5; - + pub fn get_interpolation_coefficients( + &self, + mut coefficients: Vec, + resample_factor_reciprocal: f64, + ) -> Vec { let mut coefficient_sum = 0.0; - coefficients.extend((0..interpolation_coefficients_length).map( - |interpolation_coefficient_index| { - let index_float = interpolation_coefficient_index as f64; - let sample_index_fractional = (index_float * resample_factor_reciprocal).fract(); - - let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional); - - let fir_filter = Self::fir_filter( - index_float, - last_index, - sinc_center, - resample_factor_reciprocal, - ); - - let coefficient = sample_index_fractional_sinc_weight * fir_filter; - - coefficient_sum += coefficient; - - coefficient - }, - )); - - coefficients - .iter_mut() - .for_each(|coefficient| *coefficient /= coefficient_sum); - - coefficients - } - - pub fn get_fir_filter_coefficients(&self, resample_factor_reciprocal: f64) -> Vec { - let mut coefficients = Vec::with_capacity(NUM_FIR_FILTER_TAPS); - - if self.get_interpolation_coefficients_length() != 0 { - warn!("InterpolationQuality::Medium/High::get_fir_filter_coefficients always returns an empty Vec"); - warn!("The FIR Filter coefficients are a part of the Windowed Sinc Interpolation coefficients"); + for (index, coefficient) in coefficients.iter_mut().enumerate() { + *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract()); - return coefficients; + coefficient_sum += *coefficient; } - let last_index = NUM_FIR_FILTER_TAPS as f64 - 1.0; - - let sinc_center = last_index * 0.5; - - let mut coefficient_sum = 0.0; - - coefficients.extend( - (0..NUM_FIR_FILTER_TAPS).map(|fir_filter_coefficient_index| { - let coefficient = Self::fir_filter( - fir_filter_coefficient_index as f64, - last_index, - sinc_center, - resample_factor_reciprocal, - ); - - coefficient_sum += coefficient; - - coefficient - }), - ); - coefficients .iter_mut() .for_each(|coefficient| *coefficient /= coefficient_sum); @@ -158,15 +86,6 @@ impl InterpolationQuality { coefficients } - pub fn get_interpolation_coefficients_length(&self) -> usize { - use InterpolationQuality::*; - match self { - Low => 0, - Medium => 129, - High => 257, - } - } - fn sinc(x: f64) -> f64 { if x.abs() < f64::EPSILON { 1.0 @@ -175,35 +94,6 @@ impl InterpolationQuality { pi_x.sin() / pi_x } } - - fn blackman(index: f64, last_index: f64) -> f64 { - // Calculate the Blackman window function for the given center offset - // w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)), - // where n is the center offset, N is the window size, - // and A0, A1, A2 are precalculated coefficients - let two_pi_n = TWO_TIMES_PI * index; - let four_pi_n = FOUR_TIMES_PI * index; - - BLACKMAN_A0 - BLACKMAN_A1 * (two_pi_n / last_index).cos() - + BLACKMAN_A2 * (four_pi_n / last_index).cos() - } - - fn fir_filter( - index: f64, - last_index: f64, - sinc_center: f64, - resample_factor_reciprocal: f64, - ) -> f64 { - // The resample_factor_reciprocal also happens to be our - // anti-alias cutoff. In this case it represents the minimum - // output bandwidth needed to fully represent the input. - let adjusted_sinc_center_offset = (index - sinc_center) * resample_factor_reciprocal; - - let sinc_value = Self::sinc(adjusted_sinc_center_offset); - let blackman_window_value = Self::blackman(index, last_index); - - sinc_value * blackman_window_value - } } #[derive(Clone, Copy, Debug, Default)] @@ -262,10 +152,12 @@ impl std::fmt::Display for SampleRate { } } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Debug, Default)] pub struct ResampleSpec { pub resample_factor_reciprocal: f64, pub interpolation_output_size: usize, + pub high_coefficients: Vec, + pub low_coefficients: Vec, } impl SampleRate { @@ -328,19 +220,27 @@ impl SampleRate { ResampleSpec { resample_factor_reciprocal: 1.0, interpolation_output_size: RESAMPLER_INPUT_SIZE, + high_coefficients: vec![], + low_coefficients: vec![], } } Hz48000 => ResampleSpec { resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL, interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE, + high_coefficients: HZ48000_HIGH.to_vec(), + low_coefficients: HZ48000_LOW.to_vec(), }, Hz88200 => ResampleSpec { resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL, interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE, + high_coefficients: HZ88200_HIGH.to_vec(), + low_coefficients: HZ88200_LOW.to_vec(), }, Hz96000 => ResampleSpec { resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL, interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE, + high_coefficients: HZ96000_HIGH.to_vec(), + low_coefficients: HZ96000_LOW.to_vec(), }, } } diff --git a/playback/src/filter_coefficients.rs b/playback/src/filter_coefficients.rs new file mode 100644 index 000000000..96a2c42b7 --- /dev/null +++ b/playback/src/filter_coefficients.rs @@ -0,0 +1,1187 @@ +// All Windows calculated with pyfda (Python Filter Design Analysis Tool) +// https://github.com/chipmuenk/pyfda +// Window = Kaiser +// beta = 8.6 (Similar to a Blackman Window) +// fc = 22.5kHz +// -86dB by 23kHz +#[allow(clippy::excessive_precision)] +pub const HZ48000_HIGH: [f64; 257] = [ + -1.4287799853519804e-19, + -8.529928242661527e-07, + 2.1395999264221267e-06, + -3.880458963785995e-06, + 6.05893601403254e-06, + -8.613527303281799e-06, + 1.1432294466108613e-05, + -1.4350136921309217e-05, + 1.7149627991629572e-05, + -1.956600058898423e-05, + 2.1296664705444228e-05, + -2.201537812656286e-05, + 2.1390883855947267e-05, + -1.9109487313809015e-05, + 1.490069206906713e-05, + -8.564666558168402e-06, + -2.4187562985455694e-19, + 1.0770051522629687e-05, + -2.357480377576126e-05, + 3.80789154370508e-05, + -5.37726902353467e-05, + 6.997172608513909e-05, + -8.582740473543781e-05, + 0.0001003492559243388, + -0.00011243964464523357, + 0.00012094053924787859, + -0.0001246913509069703, + 0.00012259602869291528, + -0.00011369679289372431, + 9.725114087319391e-05, + -7.280811540502643e-05, + 4.027933539700687e-05, + -8.761722338284784e-19, + -4.7224970562679786e-05, + 0.0001000942700094669, + -0.00015680711547266284, + 0.00021508702286534775, + -0.00027223465216542913, + 0.00032521058924288055, + -0.00037074739096535606, + 0.00040548854166410335, + -0.00042615022958145474, + 0.0004297001408507999, + -0.00041354588242148774, + 0.00037572428338265103, + -0.0003150817831073127, + 0.00023143548325566652, + -0.00012570429408715404, + 8.736848542712518e-18, + 0.00014233096258373752, + -0.00029672267470682895, + 0.00045746741021428363, + -0.0006178587763843014, + 0.0007704002699025563, + -0.0009070758950716863, + 0.0010196760363510072, + -0.001100168333253743, + 0.0011411000481453986, + -0.0011360155279515375, + 0.0010798700171927062, + -0.0009694194473815131, + 0.0008035650497546995, + -0.0005836318265664195, + 0.0003135611435773604, + -3.649849800074037e-17, + -0.0003477271314898191, + 0.0007177736996011124, + -0.0010960583702874734, + 0.0014666830951029159, + -0.0018124745987316703, + 0.0021156355406508768, + -0.0023584853377038536, + 0.002524264795160409, + -0.0025979735902941564, + 0.0025672055778467244, + -0.002422944105830126, + 0.0021602782649567656, + -0.0017790014114850766, + 0.001284055499878637, + -0.0006857887606066072, + 7.209995999923543e-18, + 0.000752249860282943, + -0.0015450696726810457, + 0.0023484142653889145, + -0.003128995352023552, + 0.00385140677724417, + -0.004479433137231434, + 0.004977500880593015, + -0.005312222191564676, + 0.005453974732678532, + -0.005378455081534177, + 0.005068140772503758, + -0.004513595500789128, + 0.003714554414731469, + -0.0026807315526779464, + 0.0014322992948101853, + -8.297338214945322e-17, + -0.001575137531433456, + 0.003242489907858635, + -0.004942936992960944, + 0.006610347331237228, + -0.008173416627546943, + 0.009557809308226558, + -0.010688539065884867, + 0.011492512118343826, + -0.011901147266026046, + 0.01185298019699458, + -0.011296156240663045, + 0.01019071615665419, + -0.00851058366772981, + 0.006245171256720193, + -0.0034005320447053696, + 1.0063905996254467e-16, + 0.003915722194550309, + -0.008289049174613953, + 0.013046573943979376, + -0.01810068406300195, + 0.023351692368405477, + -0.02869042197225499, + 0.03400116752010972, + -0.03916493952679325, + 0.044062886688337265, + -0.04857978290438118, + 0.052607461706064784, + -0.056048081078209105, + 0.058817106327538726, + -0.06084590754218613, + 0.06208388100251895, + 0.9375003025680546, + 0.06208388100251895, + -0.06084590754218613, + 0.058817106327538726, + -0.056048081078209105, + 0.052607461706064784, + -0.04857978290438118, + 0.044062886688337265, + -0.03916493952679325, + 0.03400116752010972, + -0.02869042197225499, + 0.023351692368405477, + -0.01810068406300195, + 0.013046573943979376, + -0.008289049174613953, + 0.003915722194550309, + 1.0063905996254467e-16, + -0.0034005320447053696, + 0.006245171256720193, + -0.00851058366772981, + 0.01019071615665419, + -0.011296156240663045, + 0.01185298019699458, + -0.011901147266026046, + 0.011492512118343826, + -0.010688539065884867, + 0.009557809308226558, + -0.008173416627546943, + 0.006610347331237228, + -0.004942936992960944, + 0.003242489907858635, + -0.001575137531433456, + -8.297338214945322e-17, + 0.0014322992948101853, + -0.0026807315526779464, + 0.003714554414731469, + -0.004513595500789128, + 0.005068140772503758, + -0.005378455081534177, + 0.005453974732678532, + -0.005312222191564676, + 0.004977500880593015, + -0.004479433137231434, + 0.00385140677724417, + -0.003128995352023552, + 0.0023484142653889145, + -0.0015450696726810457, + 0.000752249860282943, + 7.209995999923543e-18, + -0.0006857887606066072, + 0.001284055499878637, + -0.0017790014114850766, + 0.0021602782649567656, + -0.002422944105830126, + 0.0025672055778467244, + -0.0025979735902941564, + 0.002524264795160409, + -0.0023584853377038536, + 0.0021156355406508768, + -0.0018124745987316703, + 0.0014666830951029159, + -0.0010960583702874734, + 0.0007177736996011124, + -0.0003477271314898191, + -3.649849800074037e-17, + 0.0003135611435773604, + -0.0005836318265664195, + 0.0008035650497546995, + -0.0009694194473815131, + 0.0010798700171927062, + -0.0011360155279515375, + 0.0011411000481453986, + -0.001100168333253743, + 0.0010196760363510072, + -0.0009070758950716863, + 0.0007704002699025563, + -0.0006178587763843014, + 0.00045746741021428363, + -0.00029672267470682895, + 0.00014233096258373752, + 8.736848542712518e-18, + -0.00012570429408715404, + 0.00023143548325566652, + -0.0003150817831073127, + 0.00037572428338265103, + -0.00041354588242148774, + 0.0004297001408507999, + -0.00042615022958145474, + 0.00040548854166410335, + -0.00037074739096535606, + 0.00032521058924288055, + -0.00027223465216542913, + 0.00021508702286534775, + -0.00015680711547266284, + 0.0001000942700094669, + -4.7224970562679786e-05, + -8.761722338284784e-19, + 4.027933539700687e-05, + -7.280811540502643e-05, + 9.725114087319391e-05, + -0.00011369679289372431, + 0.00012259602869291528, + -0.0001246913509069703, + 0.00012094053924787859, + -0.00011243964464523357, + 0.0001003492559243388, + -8.582740473543781e-05, + 6.997172608513909e-05, + -5.37726902353467e-05, + 3.80789154370508e-05, + -2.357480377576126e-05, + 1.0770051522629687e-05, + -2.4187562985455694e-19, + -8.564666558168402e-06, + 1.490069206906713e-05, + -1.9109487313809015e-05, + 2.1390883855947267e-05, + -2.201537812656286e-05, + 2.1296664705444228e-05, + -1.956600058898423e-05, + 1.7149627991629572e-05, + -1.4350136921309217e-05, + 1.1432294466108613e-05, + -8.613527303281799e-06, + 6.05893601403254e-06, + -3.880458963785995e-06, + 2.1395999264221267e-06, + -8.529928242661527e-07, + -1.4287799853519804e-19, +]; + +#[allow(clippy::excessive_precision)] +pub const HZ88200_HIGH: [f64; 257] = [ + -2.7177973650537016e-06, + 2.615116391456718e-06, + 4.371253470079787e-06, + -4.527642205236769e-06, + -6.343166732321034e-06, + 7.206853357278462e-06, + 8.608531809529578e-06, + -1.0831207319855358e-05, + -1.11168738348255e-05, + 1.5597004565971875e-05, + 1.3787248972132889e-05, + -2.1716246512168148e-05, + -1.6503125021559895e-05, + 2.9413629227200937e-05, + 1.9107254724178818e-05, + -3.8922617925675015e-05, + -2.139666772922098e-05, + 5.04805630067818e-05, + 2.3117918532840763e-05, + -6.432283649476294e-05, + -2.396273660981686e-05, + 8.067598787603222e-05, + 2.3564231002785538e-05, + -9.974994011030785e-05, + -2.149380441843341e-05, + 0.00012172926954179013, + 1.7258931076241913e-05, + -0.00014676363699943706, + -1.030194790401862e-05, + 0.0001749574609349626, + 1.8686890713852827e-19, + -0.0002063589463295164, + 1.4333731499066454e-05, + 0.00024094860458611302, + -3.344740787382722e-05, + -0.0002786274189628284, + 5.814642716712084e-05, + 0.00031920482651865724, + -8.928776855240112e-05, + -0.0003623867002575848, + 0.0001277727488249831, + 0.0004077635233859557, + -0.00017453818403045162, + -0.00045479895057210033, + 0.0002305459825657413, + 0.000502818948057389, + -0.00029677123118056025, + -0.000551001694669649, + 0.00037418887145730905, + 0.0005983684084941591, + -0.00046375910077735565, + -0.0006437752384124257, + 0.0005664116676635973, + 0.0006859063251323009, + -0.0006830292658687915, + -0.0007232680918226466, + 0.0008144302637888361, + 0.000754184768991671, + -0.0009613510348663591, + -0.0007767950905092509, + 0.0011244281797910076, + 0.0007890500159662434, + -0.0013041809517326277, + -0.000788711236573936, + 0.0015009942108703117, + 0.00077335010432126, + -0.00171510224350985, + -0.0007403464826028671, + 0.0019465737836333772, + 0.0006868868445776879, + -0.002195298570445892, + -0.0006099607339225542, + 0.002460975764167073, + 0.0005063544381987515, + -0.002743104523907686, + -0.00037264038864942025, + 0.003040977026074337, + 0.00020516036410315268, + -0.0033536741696310896, + -1.7263210195899785e-18, + 0.0036800641761425457, + -0.0002470486740043912, + -0.004018804248416292, + 0.0005405410133353366, + 0.004368345402486541, + -0.000885455697808869, + -0.004726940534504569, + 0.0012873008119620815, + 0.005092655727810584, + -0.001752261157027223, + -0.005463384747140825, + 0.0022874024955170562, + 0.005836866607731414, + -0.002900955402436162, + -0.006210706048228085, + 0.0036027121698355103, + 0.006582396679029297, + -0.0044045872407267005, + -0.006949346523208571, + 0.005321419255211435, + 0.007308905616676035, + -0.00637213881767667, + -0.007658395288879785, + 0.007581505281049055, + 0.00799513870616462, + -0.008982757034954967, + -0.008316492227824646, + 0.010621782037025395, + 0.00861987710070591, + -0.012563926080135948, + -0.008902811002566763, + 0.01490560790319659, + 0.0091629389377553, + -0.017795223882502632, + -0.009398062991380806, + 0.021473342249052296, + 0.009606170460125666, + -0.026356732255292163, + -0.009785459899039199, + 0.033234410282523816, + 0.009934364653757865, + -0.04379972993086113, + -0.010051573486085123, + 0.06245943058441552, + 0.01013604794705137, + -0.1053787714300839, + -0.010187036204577683, + 0.31806791600342954, + 0.5102041545837825, + 0.31806791600342954, + -0.010187036204577683, + -0.1053787714300839, + 0.01013604794705137, + 0.06245943058441552, + -0.010051573486085123, + -0.04379972993086113, + 0.009934364653757865, + 0.033234410282523816, + -0.009785459899039199, + -0.026356732255292163, + 0.009606170460125666, + 0.021473342249052296, + -0.009398062991380806, + -0.017795223882502632, + 0.0091629389377553, + 0.01490560790319659, + -0.008902811002566763, + -0.012563926080135948, + 0.00861987710070591, + 0.010621782037025395, + -0.008316492227824646, + -0.008982757034954967, + 0.00799513870616462, + 0.007581505281049055, + -0.007658395288879785, + -0.00637213881767667, + 0.007308905616676035, + 0.005321419255211435, + -0.006949346523208571, + -0.0044045872407267005, + 0.006582396679029297, + 0.0036027121698355103, + -0.006210706048228085, + -0.002900955402436162, + 0.005836866607731414, + 0.0022874024955170562, + -0.005463384747140825, + -0.001752261157027223, + 0.005092655727810584, + 0.0012873008119620815, + -0.004726940534504569, + -0.000885455697808869, + 0.004368345402486541, + 0.0005405410133353366, + -0.004018804248416292, + -0.0002470486740043912, + 0.0036800641761425457, + -1.7263210195899785e-18, + -0.0033536741696310896, + 0.00020516036410315268, + 0.003040977026074337, + -0.00037264038864942025, + -0.002743104523907686, + 0.0005063544381987515, + 0.002460975764167073, + -0.0006099607339225542, + -0.002195298570445892, + 0.0006868868445776879, + 0.0019465737836333772, + -0.0007403464826028671, + -0.00171510224350985, + 0.00077335010432126, + 0.0015009942108703117, + -0.000788711236573936, + -0.0013041809517326277, + 0.0007890500159662434, + 0.0011244281797910076, + -0.0007767950905092509, + -0.0009613510348663591, + 0.000754184768991671, + 0.0008144302637888361, + -0.0007232680918226466, + -0.0006830292658687915, + 0.0006859063251323009, + 0.0005664116676635973, + -0.0006437752384124257, + -0.00046375910077735565, + 0.0005983684084941591, + 0.00037418887145730905, + -0.000551001694669649, + -0.00029677123118056025, + 0.000502818948057389, + 0.0002305459825657413, + -0.00045479895057210033, + -0.00017453818403045162, + 0.0004077635233859557, + 0.0001277727488249831, + -0.0003623867002575848, + -8.928776855240112e-05, + 0.00031920482651865724, + 5.814642716712084e-05, + -0.0002786274189628284, + -3.344740787382722e-05, + 0.00024094860458611302, + 1.4333731499066454e-05, + -0.0002063589463295164, + 1.8686890713852827e-19, + 0.0001749574609349626, + -1.030194790401862e-05, + -0.00014676363699943706, + 1.7258931076241913e-05, + 0.00012172926954179013, + -2.149380441843341e-05, + -9.974994011030785e-05, + 2.3564231002785538e-05, + 8.067598787603222e-05, + -2.396273660981686e-05, + -6.432283649476294e-05, + 2.3117918532840763e-05, + 5.04805630067818e-05, + -2.139666772922098e-05, + -3.8922617925675015e-05, + 1.9107254724178818e-05, + 2.9413629227200937e-05, + -1.6503125021559895e-05, + -2.1716246512168148e-05, + 1.3787248972132889e-05, + 1.5597004565971875e-05, + -1.11168738348255e-05, + -1.0831207319855358e-05, + 8.608531809529578e-06, + 7.206853357278462e-06, + -6.343166732321034e-06, + -4.527642205236769e-06, + 4.371253470079787e-06, + 2.615116391456718e-06, + -2.7177973650537016e-06, +]; + +#[allow(clippy::excessive_precision)] +pub const HZ96000_HIGH: [f64; 257] = [ + -7.143923102926616e-20, + -4.351257283515671e-06, + -1.0907621221693655e-06, + 6.683906965798109e-06, + 3.2790831797631692e-06, + -9.13620584774856e-06, + -6.874774126129371e-06, + 1.1310163453885296e-05, + 1.2126657588758059e-05, + -1.265575645173113e-05, + -1.9166554046744026e-05, + 1.248152779539428e-05, + 2.794862730250771e-05, + -9.984713045871162e-06, + -3.8189337778906546e-05, + 4.303067618516991e-06, + 4.931445698739215e-05, + 5.411099152784563e-06, + -6.042042478905683e-05, + -1.989624512124905e-05, + 7.025763351163002e-05, + 3.967018140695091e-05, + -7.72428741451272e-05, + -6.490829524996992e-05, + 7.9507093138109e-05, + 9.532015440656505e-05, + -7.498274957040692e-05, + -0.00013003529388340405, + 6.153246053554203e-05, + 0.0001675104888314477, + -3.711737577180626e-05, + -0.00020547154270862667, + 4.380875381487169e-19, + 0.0002409023748839593, + 5.102778188775271e-05, + -0.0002700928372585857, + -0.00011640463560427953, + 0.00028875415767545045, + 0.00019556435946416691, + -0.0002922072182944144, + -0.0002867246276901985, + 0.0002756441457161496, + 0.0003867211644368446, + -0.0002344581317859229, + -0.0004909090262693411, + 0.00016463032935658855, + 0.0005931514994811955, + -6.315646659694906e-05, + -0.0006859142317553148, + -7.151005261521953e-05, + 0.0007604775938269121, + 0.0002390268636629881, + -0.0008072740136891249, + -0.00043677525441936056, + 0.0008163493865283938, + 0.0006595508119830226, + -0.0007779390126639911, + -0.0008993661964713373, + 0.0006831393454389286, + 0.00114539774688185, + -0.0005246477263799965, + -0.00138410277847316, + 0.00029753389026130304, + 0.0015995271835870914, + -1.8249308204396214e-17, + -0.001773813531049159, + -0.0003659190459608399, + 0.0018879086841156133, + 0.0007937657463383715, + -0.0019224576000959562, + -0.0012722307423329708, + 0.0018588571537189837, + 0.0017849305448029394, + -0.0016804313624166863, + -0.002310431475418928, + 0.0013736781639519724, + 0.0028225487316737353, + -0.0009295287890346657, + -0.0032909363527835883, + 0.0003445546173767534, + 0.003681968512252244, + 0.0003779460639632543, + -0.00395989577856263, + -0.0012270471817312566, + 0.004088242707216325, + 0.0021835391818022633, + -0.004031396395209965, + -0.003219566441111553, + 0.0037563205210019465, + 0.004298589347141008, + -0.003234316955594173, + -0.005375681266525964, + 0.0024427482073774184, + 0.00639814422962896, + -0.0013666295279113297, + -0.007306395272526002, + 4.1486825665423373e-17, + 0.008035036709816487, + 0.0016530123829845687, + -0.008513975101161425, + -0.0035775058023473096, + 0.008669388760192265, + 0.005747558403911614, + -0.008424248812489233, + -0.008126459615440715, + 0.007697946272157984, + 0.01066743495767241, + -0.006404309173724075, + -0.013314855511628901, + 0.004446782604876781, + 0.016005897813639956, + -0.00170849842523587, + -0.018672595414079837, + -0.001967340732302961, + 0.021244201096702293, + 0.006816838930484501, + -0.02364976015970634, + -0.013239145641295004, + 0.02582078137402188, + 0.021992767160156825, + -0.027693884168079493, + -0.03472848053143565, + 0.02921329883114049, + 0.05579974177257601, + -0.03033310130622086, + -0.10130968278740993, + 0.03101907530302571, + 0.3167001312508388, + 0.46875167199845, + 0.3167001312508388, + 0.03101907530302571, + -0.10130968278740993, + -0.03033310130622086, + 0.05579974177257601, + 0.02921329883114049, + -0.03472848053143565, + -0.027693884168079493, + 0.021992767160156825, + 0.02582078137402188, + -0.013239145641295004, + -0.02364976015970634, + 0.006816838930484501, + 0.021244201096702293, + -0.001967340732302961, + -0.018672595414079837, + -0.00170849842523587, + 0.016005897813639956, + 0.004446782604876781, + -0.013314855511628901, + -0.006404309173724075, + 0.01066743495767241, + 0.007697946272157984, + -0.008126459615440715, + -0.008424248812489233, + 0.005747558403911614, + 0.008669388760192265, + -0.0035775058023473096, + -0.008513975101161425, + 0.0016530123829845687, + 0.008035036709816487, + 4.1486825665423373e-17, + -0.007306395272526002, + -0.0013666295279113297, + 0.00639814422962896, + 0.0024427482073774184, + -0.005375681266525964, + -0.003234316955594173, + 0.004298589347141008, + 0.0037563205210019465, + -0.003219566441111553, + -0.004031396395209965, + 0.0021835391818022633, + 0.004088242707216325, + -0.0012270471817312566, + -0.00395989577856263, + 0.0003779460639632543, + 0.003681968512252244, + 0.0003445546173767534, + -0.0032909363527835883, + -0.0009295287890346657, + 0.0028225487316737353, + 0.0013736781639519724, + -0.002310431475418928, + -0.0016804313624166863, + 0.0017849305448029394, + 0.0018588571537189837, + -0.0012722307423329708, + -0.0019224576000959562, + 0.0007937657463383715, + 0.0018879086841156133, + -0.0003659190459608399, + -0.001773813531049159, + -1.8249308204396214e-17, + 0.0015995271835870914, + 0.00029753389026130304, + -0.00138410277847316, + -0.0005246477263799965, + 0.00114539774688185, + 0.0006831393454389286, + -0.0008993661964713373, + -0.0007779390126639911, + 0.0006595508119830226, + 0.0008163493865283938, + -0.00043677525441936056, + -0.0008072740136891249, + 0.0002390268636629881, + 0.0007604775938269121, + -7.151005261521953e-05, + -0.0006859142317553148, + -6.315646659694906e-05, + 0.0005931514994811955, + 0.00016463032935658855, + -0.0004909090262693411, + -0.0002344581317859229, + 0.0003867211644368446, + 0.0002756441457161496, + -0.0002867246276901985, + -0.0002922072182944144, + 0.00019556435946416691, + 0.00028875415767545045, + -0.00011640463560427953, + -0.0002700928372585857, + 5.102778188775271e-05, + 0.0002409023748839593, + 4.380875381487169e-19, + -0.00020547154270862667, + -3.711737577180626e-05, + 0.0001675104888314477, + 6.153246053554203e-05, + -0.00013003529388340405, + -7.498274957040692e-05, + 9.532015440656505e-05, + 7.9507093138109e-05, + -6.490829524996992e-05, + -7.72428741451272e-05, + 3.967018140695091e-05, + 7.025763351163002e-05, + -1.989624512124905e-05, + -6.042042478905683e-05, + 5.411099152784563e-06, + 4.931445698739215e-05, + 4.303067618516991e-06, + -3.8189337778906546e-05, + -9.984713045871162e-06, + 2.794862730250771e-05, + 1.248152779539428e-05, + -1.9166554046744026e-05, + -1.265575645173113e-05, + 1.2126657588758059e-05, + 1.1310163453885296e-05, + -6.874774126129371e-06, + -9.13620584774856e-06, + 3.2790831797631692e-06, + 6.683906965798109e-06, + -1.0907621221693655e-06, + -4.351257283515671e-06, + -7.143923102926616e-20, +]; + +#[allow(clippy::excessive_precision)] +pub const HZ48000_LOW: [f64; 129] = [ + -1.4287804154623814e-19, + -2.181517823792544e-06, + 6.5581470578797384e-06, + -1.3749507785319245e-05, + 2.4253243796448185e-05, + -3.8332995273522084e-05, + 5.589709009118714e-05, + -7.637845076415545e-05, + 9.862862369540776e-05, + -0.00012084049392574903, + 0.00014051485346619586, + -0.0001544852936160124, + 0.00015901371827411808, + -0.0001499650577703289, + 0.00012306455887295043, + -7.423453305983806e-05, + 8.761724975855263e-19, + 0.00010205526341099535, + -0.0002328085860166881, + 0.00039112756777908054, + -0.0005734475676350329, + 0.0007734400525193427, + -0.000981815162903983, + 0.001186299507498468, + -0.0013718244260180216, + 0.0015209507112593692, + -0.0016145432755263242, + 0.0016326939677845523, + -0.0015558734461504878, + 0.001366274669719045, + -0.0010492923645293963, + 0.000595066029150748, + -3.649850898800486e-17, + -0.0007318359380147118, + 0.0015875268203384464, + -0.0025444539959421624, + 0.0035698505829804923, + -0.004620849350959662, + 0.005645080848996707, + -0.006581853334149319, + 0.007363915351356501, + -0.007919768248015761, + 0.008176461349835438, + -0.008062769060436804, + 0.007512618931198147, + -0.006468614873048888, + 0.004885482036021499, + -0.002733251011439905, + 8.297340712720699e-17, + 0.0033060150358527928, + -0.007154990546444822, + 0.011495082976006515, + -0.016252871396153546, + 0.021334807123689837, + -0.026629632648107943, + 0.032011701411864754, + -0.03734508091577928, + 0.042488277143797916, + -0.047299381109978895, + 0.051641410759341526, + -0.05538760532182361, + 0.05842642570422467, + -0.06066602406289918, + 0.06203796801866447, + 0.9375005847868952, + 0.06203796801866447, + -0.06066602406289918, + 0.05842642570422467, + -0.05538760532182361, + 0.051641410759341526, + -0.047299381109978895, + 0.042488277143797916, + -0.03734508091577928, + 0.032011701411864754, + -0.026629632648107943, + 0.021334807123689837, + -0.016252871396153546, + 0.011495082976006515, + -0.007154990546444822, + 0.0033060150358527928, + 8.297340712720699e-17, + -0.002733251011439905, + 0.004885482036021499, + -0.006468614873048888, + 0.007512618931198147, + -0.008062769060436804, + 0.008176461349835438, + -0.007919768248015761, + 0.007363915351356501, + -0.006581853334149319, + 0.005645080848996707, + -0.004620849350959662, + 0.0035698505829804923, + -0.0025444539959421624, + 0.0015875268203384464, + -0.0007318359380147118, + -3.649850898800486e-17, + 0.000595066029150748, + -0.0010492923645293963, + 0.001366274669719045, + -0.0015558734461504878, + 0.0016326939677845523, + -0.0016145432755263242, + 0.0015209507112593692, + -0.0013718244260180216, + 0.001186299507498468, + -0.000981815162903983, + 0.0007734400525193427, + -0.0005734475676350329, + 0.00039112756777908054, + -0.0002328085860166881, + 0.00010205526341099535, + 8.761724975855263e-19, + -7.423453305983806e-05, + 0.00012306455887295043, + -0.0001499650577703289, + 0.00015901371827411808, + -0.0001544852936160124, + 0.00014051485346619586, + -0.00012084049392574903, + 9.862862369540776e-05, + -7.637845076415545e-05, + 5.589709009118714e-05, + -3.8332995273522084e-05, + 2.4253243796448185e-05, + -1.3749507785319245e-05, + 6.5581470578797384e-06, + -2.181517823792544e-06, + -1.4287804154623814e-19, +]; + +#[allow(clippy::excessive_precision)] +pub const HZ88200_LOW: [f64; 129] = [ + 5.875800344814529e-06, + 4.851699044013074e-06, + -1.5670438235895964e-05, + -9.287225758882044e-06, + 3.2188588517572685e-05, + 1.452725442412174e-05, + -5.80015310113526e-05, + -1.975318175268538e-05, + 9.615523116437566e-05, + 2.3552114164532385e-05, + -0.00015014174552603567, + -2.375916580017386e-05, + 0.00022383889492568076, + 1.729437074025395e-05, + -0.00032141607023776947, + -1.8686789504441635e-19, + 0.00044720587729591056, + -3.351608912018519e-05, + -0.0006055434276587542, + 9.002640046744051e-05, + 0.0008005773235174634, + -0.00017781632159122524, + -0.0010360586296973909, + 0.00030680368023040425, + 0.0013151162221446207, + -0.0004886505249968164, + -0.0016400286962743556, + 0.0007368790790811487, + 0.0020120043638640903, + -0.0010670133701345262, + -0.0024309816366330315, + 0.0014967816965620656, + 0.002895462190517508, + -0.002046435147324143, + -0.0034023886674580135, + 0.002739267989293373, + 0.003947077286631264, + -0.003602474524851333, + -0.0045232136384107564, + 0.0046685585578356776, + 0.005122917204805467, + -0.005977654559924746, + -0.005736876918489128, + 0.007581382917565772, + 0.006354556507022021, + -0.009549372598113655, + -0.006964464667634032, + 0.011980635507338782, + 0.007554481497059819, + -0.015024288868017965, + -0.008112229280669358, + 0.018919636740583255, + 0.008625472935527727, + -0.024080164100849143, + -0.009082533288527662, + 0.031289629556764974, + 0.009472695101363785, + -0.042234282967316725, + -0.009786591428611496, + 0.06131211363458383, + 0.01001654655736988, + -0.10467821599139523, + -0.010156861410646928, + 0.31783087768940993, + 0.5102013912850153, + 0.31783087768940993, + -0.010156861410646928, + -0.10467821599139523, + 0.01001654655736988, + 0.06131211363458383, + -0.009786591428611496, + -0.042234282967316725, + 0.009472695101363785, + 0.031289629556764974, + -0.009082533288527662, + -0.024080164100849143, + 0.008625472935527727, + 0.018919636740583255, + -0.008112229280669358, + -0.015024288868017965, + 0.007554481497059819, + 0.011980635507338782, + -0.006964464667634032, + -0.009549372598113655, + 0.006354556507022021, + 0.007581382917565772, + -0.005736876918489128, + -0.005977654559924746, + 0.005122917204805467, + 0.0046685585578356776, + -0.0045232136384107564, + -0.003602474524851333, + 0.003947077286631264, + 0.002739267989293373, + -0.0034023886674580135, + -0.002046435147324143, + 0.002895462190517508, + 0.0014967816965620656, + -0.0024309816366330315, + -0.0010670133701345262, + 0.0020120043638640903, + 0.0007368790790811487, + -0.0016400286962743556, + -0.0004886505249968164, + 0.0013151162221446207, + 0.00030680368023040425, + -0.0010360586296973909, + -0.00017781632159122524, + 0.0008005773235174634, + 9.002640046744051e-05, + -0.0006055434276587542, + -3.351608912018519e-05, + 0.00044720587729591056, + -1.8686789504441635e-19, + -0.00032141607023776947, + 1.729437074025395e-05, + 0.00022383889492568076, + -2.375916580017386e-05, + -0.00015014174552603567, + 2.3552114164532385e-05, + 9.615523116437566e-05, + -1.975318175268538e-05, + -5.80015310113526e-05, + 1.452725442412174e-05, + 3.2188588517572685e-05, + -9.287225758882044e-06, + -1.5670438235895964e-05, + 4.851699044013074e-06, + 5.875800344814529e-06, +]; + +#[allow(clippy::excessive_precision)] +pub const HZ96000_LOW: [f64; 129] = [ + -7.143944801213435e-20, + -1.1128313185646072e-05, + -3.3433343718178e-06, + 2.368294142133405e-05, + 1.3125839456769717e-05, + -4.065919588349948e-05, + -3.361363034503314e-05, + 6.0198389114399714e-05, + 6.974138571798226e-05, + -7.816273123033217e-05, + -0.000126460783407638, + 8.758503947913682e-05, + 0.0002077626777155509, + -7.835700358335798e-05, + -0.0003154059452699033, + 3.729708416324331e-05, + 0.0004474410769117699, + 5.1274839240568415e-05, + -0.0005966722898292008, + -0.00020436483462002328, + 0.0007492498350107482, + 0.0004384998464839666, + -0.0008836155865344651, + -0.0007673289520005847, + 0.000970032155449486, + 0.0011987515041766601, + -0.0009709031498968383, + -0.0017317724164334631, + 0.0008420376771297366, + 0.0023533499143115976, + -0.0005349278077261196, + -0.00303553840647302, + 1.824936363314565e-17, + 0.003733226210629766, + 0.0008093189947979181, + -0.004382713612447862, + -0.0019320007678197645, + 0.004901261108562161, + 0.0033946608064293073, + -0.00518754915999129, + -0.005207105621787521, + 0.0051227092580119855, + 0.007358664812266444, + -0.004571166160481191, + -0.009815768367302802, + 0.003379862524796346, + 0.012521152261727227, + -0.0013732462347513066, + -0.015394948058705999, + -0.0016610156480375167, + 0.018337745467632928, + 0.006006200853277362, + -0.021235526836812676, + -0.012095713970371423, + 0.02396617954234677, + 0.020705989626446524, + -0.02640711788557473, + -0.03348753234339166, + 0.028443411089665006, + 0.05477521964516673, + -0.029975735264560804, + -0.10063702926794785, + 0.030927455828749166, + 0.3164667874739216, + 0.4687530957411302, + 0.3164667874739216, + 0.030927455828749166, + -0.10063702926794785, + -0.029975735264560804, + 0.05477521964516673, + 0.028443411089665006, + -0.03348753234339166, + -0.02640711788557473, + 0.020705989626446524, + 0.02396617954234677, + -0.012095713970371423, + -0.021235526836812676, + 0.006006200853277362, + 0.018337745467632928, + -0.0016610156480375167, + -0.015394948058705999, + -0.0013732462347513066, + 0.012521152261727227, + 0.003379862524796346, + -0.009815768367302802, + -0.004571166160481191, + 0.007358664812266444, + 0.0051227092580119855, + -0.005207105621787521, + -0.00518754915999129, + 0.0033946608064293073, + 0.004901261108562161, + -0.0019320007678197645, + -0.004382713612447862, + 0.0008093189947979181, + 0.003733226210629766, + 1.824936363314565e-17, + -0.00303553840647302, + -0.0005349278077261196, + 0.0023533499143115976, + 0.0008420376771297366, + -0.0017317724164334631, + -0.0009709031498968383, + 0.0011987515041766601, + 0.000970032155449486, + -0.0007673289520005847, + -0.0008836155865344651, + 0.0004384998464839666, + 0.0007492498350107482, + -0.00020436483462002328, + -0.0005966722898292008, + 5.1274839240568415e-05, + 0.0004474410769117699, + 3.729708416324331e-05, + -0.0003154059452699033, + -7.835700358335798e-05, + 0.0002077626777155509, + 8.758503947913682e-05, + -0.000126460783407638, + -7.816273123033217e-05, + 6.974138571798226e-05, + 6.0198389114399714e-05, + -3.361363034503314e-05, + -4.065919588349948e-05, + 1.3125839456769717e-05, + 2.368294142133405e-05, + -3.3433343718178e-06, + -1.1128313185646072e-05, + -7.143944801213435e-20, +]; diff --git a/playback/src/lib.rs b/playback/src/lib.rs index c7d8297d1..5c60791cd 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -13,6 +13,7 @@ pub mod config; pub mod convert; pub mod decoder; pub mod dither; +pub mod filter_coefficients; pub mod mixer; pub mod normaliser; pub mod player; diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index a35dcefa3..25c3a749a 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -1,6 +1,5 @@ use std::{ collections::{vec_deque, VecDeque}, - marker::Send, process::exit, sync::atomic::Ordering, sync::mpsc, @@ -8,7 +7,7 @@ use std::{ }; use crate::{ - config::{InterpolationQuality, SampleRate, NUM_FIR_FILTER_TAPS}, + config::{InterpolationQuality, SampleRate}, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE, }; @@ -80,16 +79,6 @@ impl ConvolutionFilter { } } -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>; -} - struct MonoSincResampler { interpolator: ConvolutionFilter, input_buffer: Vec, @@ -98,18 +87,22 @@ struct MonoSincResampler { interpolation_output_size: usize, } -impl MonoResampler for MonoSincResampler { +impl 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) as u64; + let coefficients = match interpolation_quality { + InterpolationQuality::Low => spec.low_coefficients, + InterpolationQuality::High => spec.high_coefficients, + }; + + let delay_line_latency = + (coefficients.len() as f64 * spec.resample_factor_reciprocal) as u64; Self { interpolator: ConvolutionFilter::new( interpolation_quality - .get_interpolation_coefficients(spec.resample_factor_reciprocal), + .get_interpolation_coefficients(coefficients, spec.resample_factor_reciprocal), ), input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize), @@ -160,82 +153,6 @@ impl MonoResampler for MonoSincResampler { } } -struct MonoLinearResampler { - fir_filter: ConvolutionFilter, - input_buffer: Vec, - resample_factor_reciprocal: f64, - delay_line_latency: u64, - interpolation_output_size: usize, -} - -impl MonoResampler for MonoLinearResampler { - fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self { - let spec = sample_rate.get_resample_spec(); - - let delay_line_latency = - (NUM_FIR_FILTER_TAPS as f64 * spec.resample_factor_reciprocal) as u64; - - Self { - fir_filter: ConvolutionFilter::new( - interpolation_quality.get_fir_filter_coefficients(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.fir_filter.clear(); - self.input_buffer.clear(); - } - - fn resample(&mut self, samples: &[f64]) -> Option> { - 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(); - - output - .iter_mut() - .for_each(|sample| *sample = self.fir_filter.convolute(*sample)); - - self.input_buffer.drain(..input_size); - - Some(output) - } -} - enum ResampleTask { Stop, Terminate, @@ -249,7 +166,7 @@ struct ResampleWorker { } impl ResampleWorker { - fn new(mut resampler: impl MonoResampler + Send + 'static, name: String) -> Self { + fn new(mut resampler: MonoSincResampler, name: String) -> Self { let (task_sender, task_receiver) = mpsc::channel(); let (result_sender, result_receiver) = mpsc::channel(); @@ -383,29 +300,12 @@ impl StereoInterleavedResampler { let left_thread_name = format!("resampler:{player_id}:left"); let right_thread_name = format!("resampler:{player_id}:right"); - 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); + 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), - } - } + Resampler::Worker { + left_resampler: ResampleWorker::new(left, left_thread_name), + right_resampler: ResampleWorker::new(right, right_thread_name), } } }; diff --git a/src/main.rs b/src/main.rs index f31526cb1..231b24518 100644 --- a/src/main.rs +++ b/src/main.rs @@ -582,7 +582,7 @@ fn get_setup() -> Setup { ).optopt( "", INTERPOLATION_QUALITY, - "Interpolation Quality to use if Resampling {Low|Medium|High}. Defaults to High.", + "Interpolation Quality to use if Resampling {Low|High}. Defaults to High.", "QUALITY" ).optopt( "", @@ -817,7 +817,7 @@ fn get_setup() -> Setup { INTERPOLATION_QUALITY, "", interpolation_quality, - "Low, Medium, High", + "Low, High", default_value, ); From 3928d0775fa6246f5f887d64468820943a5689f2 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Tue, 4 Jul 2023 18:34:57 -0500 Subject: [PATCH 50/53] New filter coeff's and only have one quality setting. --- playback/src/config.rs | 173 +- playback/src/filter_coefficients.rs | 2509 ++++++++++++++------------- playback/src/resampler.rs | 52 +- playback/src/sample_pipeline.rs | 3 +- src/main.rs | 37 +- 5 files changed, 1428 insertions(+), 1346 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 85449eb48..e2fafda29 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -4,9 +4,7 @@ pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer}; use crate::{ convert::i24, - filter_coefficients::{ - HZ48000_HIGH, HZ48000_LOW, HZ88200_HIGH, HZ88200_LOW, HZ96000_HIGH, HZ96000_LOW, - }, + filter_coefficients::{HZ48000_COEFFICIENTS, HZ88200_COEFFICIENTS, HZ96000_COEFFICIENTS}, RESAMPLER_INPUT_SIZE, SAMPLE_RATE, }; @@ -33,69 +31,6 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize = const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; -#[derive(Clone, Copy, Debug, Default)] -pub enum InterpolationQuality { - Low, - #[default] - High, -} - -impl FromStr for InterpolationQuality { - type Err = (); - - fn from_str(s: &str) -> Result { - use InterpolationQuality::*; - - match s.to_lowercase().as_ref() { - "low" => Ok(Low), - "high" => Ok(High), - _ => Err(()), - } - } -} - -impl std::fmt::Display for InterpolationQuality { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use InterpolationQuality::*; - - match self { - Low => write!(f, "Low"), - High => write!(f, "High"), - } - } -} - -impl InterpolationQuality { - pub fn get_interpolation_coefficients( - &self, - mut coefficients: Vec, - resample_factor_reciprocal: f64, - ) -> Vec { - let mut coefficient_sum = 0.0; - - for (index, coefficient) in coefficients.iter_mut().enumerate() { - *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract()); - - coefficient_sum += *coefficient; - } - - coefficients - .iter_mut() - .for_each(|coefficient| *coefficient /= coefficient_sum); - - coefficients - } - - fn sinc(x: f64) -> f64 { - if x.abs() < f64::EPSILON { - 1.0 - } else { - let pi_x = std::f64::consts::PI * x; - pi_x.sin() / pi_x - } - } -} - #[derive(Clone, Copy, Debug, Default)] pub enum SampleRate { #[default] @@ -152,14 +87,6 @@ impl std::fmt::Display for SampleRate { } } -#[derive(Debug, Default)] -pub struct ResampleSpec { - pub resample_factor_reciprocal: f64, - pub interpolation_output_size: usize, - pub high_coefficients: Vec, - pub low_coefficients: Vec, -} - impl SampleRate { pub fn as_u32(&self) -> u32 { use SampleRate::*; @@ -207,41 +134,73 @@ impl SampleRate { } } - pub fn get_resample_spec(&self) -> ResampleSpec { + pub fn get_resample_factor_reciprocal(&self) -> Option { + use SampleRate::*; + + match self { + Hz44100 => None, + Hz48000 => Some(HZ48000_RESAMPLE_FACTOR_RECIPROCAL), + Hz88200 => Some(HZ88200_RESAMPLE_FACTOR_RECIPROCAL), + Hz96000 => Some(HZ96000_RESAMPLE_FACTOR_RECIPROCAL), + } + } + + pub fn get_interpolation_output_size(&self) -> Option { use SampleRate::*; match self { - // Dummy values to satisfy - // the match statement. - // 44.1kHz will be bypassed. - Hz44100 => { - warn!("Resampling 44.1kHz to 44.1kHz is just a really CPU intensive no-op, you should not be doing it"); - - ResampleSpec { - resample_factor_reciprocal: 1.0, - interpolation_output_size: RESAMPLER_INPUT_SIZE, - high_coefficients: vec![], - low_coefficients: vec![], - } - } - Hz48000 => ResampleSpec { - resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL, - interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE, - high_coefficients: HZ48000_HIGH.to_vec(), - low_coefficients: HZ48000_LOW.to_vec(), - }, - Hz88200 => ResampleSpec { - resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL, - interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE, - high_coefficients: HZ88200_HIGH.to_vec(), - low_coefficients: HZ88200_LOW.to_vec(), - }, - Hz96000 => ResampleSpec { - resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL, - interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE, - high_coefficients: HZ96000_HIGH.to_vec(), - low_coefficients: HZ96000_LOW.to_vec(), - }, + Hz44100 => None, + Hz48000 => Some(HZ48000_INTERPOLATION_OUTPUT_SIZE), + Hz88200 => Some(HZ88200_INTERPOLATION_OUTPUT_SIZE), + Hz96000 => Some(HZ96000_INTERPOLATION_OUTPUT_SIZE), + } + } + + pub fn get_interpolation_coefficients(&self) -> Option> { + use SampleRate::*; + + match self { + Hz44100 => None, + Hz48000 => Some(Self::calculate_interpolation_coefficients( + HZ48000_COEFFICIENTS.to_vec(), + HZ48000_RESAMPLE_FACTOR_RECIPROCAL, + )), + Hz88200 => Some(Self::calculate_interpolation_coefficients( + HZ88200_COEFFICIENTS.to_vec(), + HZ88200_RESAMPLE_FACTOR_RECIPROCAL, + )), + Hz96000 => Some(Self::calculate_interpolation_coefficients( + HZ96000_COEFFICIENTS.to_vec(), + HZ96000_RESAMPLE_FACTOR_RECIPROCAL, + )), + } + } + + fn calculate_interpolation_coefficients( + mut coefficients: Vec, + resample_factor_reciprocal: f64, + ) -> Vec { + let mut coefficient_sum = 0.0; + + for (index, coefficient) in coefficients.iter_mut().enumerate() { + *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract()); + + coefficient_sum += *coefficient; + } + + coefficients + .iter_mut() + .for_each(|coefficient| *coefficient /= coefficient_sum); + + coefficients + } + + fn sinc(x: f64) -> f64 { + if x.abs() < f64::EPSILON { + 1.0 + } else { + let pi_x = std::f64::consts::PI * x; + pi_x.sin() / pi_x } } } @@ -361,7 +320,6 @@ pub struct PlayerConfig { pub gapless: bool, pub passthrough: bool, - pub interpolation_quality: InterpolationQuality, pub sample_rate: SampleRate, pub normalisation: bool, @@ -384,7 +342,6 @@ impl Default for PlayerConfig { bitrate: Bitrate::default(), gapless: true, normalisation: false, - interpolation_quality: InterpolationQuality::default(), sample_rate: SampleRate::default(), normalisation_type: NormalisationType::default(), normalisation_method: NormalisationMethod::default(), diff --git a/playback/src/filter_coefficients.rs b/playback/src/filter_coefficients.rs index 96a2c42b7..dee8a64bf 100644 --- a/playback/src/filter_coefficients.rs +++ b/playback/src/filter_coefficients.rs @@ -1,1187 +1,1344 @@ // All Windows calculated with pyfda (Python Filter Design Analysis Tool) // https://github.com/chipmuenk/pyfda // Window = Kaiser -// beta = 8.6 (Similar to a Blackman Window) -// fc = 22.5kHz -// -86dB by 23kHz +// beta = 15 +// fc = 22.8kHz +// -140dB well before 24kHz +// The assumption is that the input +// is anti-aliased properly, +// We just want to make sure interpolation +// doesn't add any artifacts. #[allow(clippy::excessive_precision)] -pub const HZ48000_HIGH: [f64; 257] = [ - -1.4287799853519804e-19, - -8.529928242661527e-07, - 2.1395999264221267e-06, - -3.880458963785995e-06, - 6.05893601403254e-06, - -8.613527303281799e-06, - 1.1432294466108613e-05, - -1.4350136921309217e-05, - 1.7149627991629572e-05, - -1.956600058898423e-05, - 2.1296664705444228e-05, - -2.201537812656286e-05, - 2.1390883855947267e-05, - -1.9109487313809015e-05, - 1.490069206906713e-05, - -8.564666558168402e-06, - -2.4187562985455694e-19, - 1.0770051522629687e-05, - -2.357480377576126e-05, - 3.80789154370508e-05, - -5.37726902353467e-05, - 6.997172608513909e-05, - -8.582740473543781e-05, - 0.0001003492559243388, - -0.00011243964464523357, - 0.00012094053924787859, - -0.0001246913509069703, - 0.00012259602869291528, - -0.00011369679289372431, - 9.725114087319391e-05, - -7.280811540502643e-05, - 4.027933539700687e-05, - -8.761722338284784e-19, - -4.7224970562679786e-05, - 0.0001000942700094669, - -0.00015680711547266284, - 0.00021508702286534775, - -0.00027223465216542913, - 0.00032521058924288055, - -0.00037074739096535606, - 0.00040548854166410335, - -0.00042615022958145474, - 0.0004297001408507999, - -0.00041354588242148774, - 0.00037572428338265103, - -0.0003150817831073127, - 0.00023143548325566652, - -0.00012570429408715404, - 8.736848542712518e-18, - 0.00014233096258373752, - -0.00029672267470682895, - 0.00045746741021428363, - -0.0006178587763843014, - 0.0007704002699025563, - -0.0009070758950716863, - 0.0010196760363510072, - -0.001100168333253743, - 0.0011411000481453986, - -0.0011360155279515375, - 0.0010798700171927062, - -0.0009694194473815131, - 0.0008035650497546995, - -0.0005836318265664195, - 0.0003135611435773604, - -3.649849800074037e-17, - -0.0003477271314898191, - 0.0007177736996011124, - -0.0010960583702874734, - 0.0014666830951029159, - -0.0018124745987316703, - 0.0021156355406508768, - -0.0023584853377038536, - 0.002524264795160409, - -0.0025979735902941564, - 0.0025672055778467244, - -0.002422944105830126, - 0.0021602782649567656, - -0.0017790014114850766, - 0.001284055499878637, - -0.0006857887606066072, - 7.209995999923543e-18, - 0.000752249860282943, - -0.0015450696726810457, - 0.0023484142653889145, - -0.003128995352023552, - 0.00385140677724417, - -0.004479433137231434, - 0.004977500880593015, - -0.005312222191564676, - 0.005453974732678532, - -0.005378455081534177, - 0.005068140772503758, - -0.004513595500789128, - 0.003714554414731469, - -0.0026807315526779464, - 0.0014322992948101853, - -8.297338214945322e-17, - -0.001575137531433456, - 0.003242489907858635, - -0.004942936992960944, - 0.006610347331237228, - -0.008173416627546943, - 0.009557809308226558, - -0.010688539065884867, - 0.011492512118343826, - -0.011901147266026046, - 0.01185298019699458, - -0.011296156240663045, - 0.01019071615665419, - -0.00851058366772981, - 0.006245171256720193, - -0.0034005320447053696, - 1.0063905996254467e-16, - 0.003915722194550309, - -0.008289049174613953, - 0.013046573943979376, - -0.01810068406300195, - 0.023351692368405477, - -0.02869042197225499, - 0.03400116752010972, - -0.03916493952679325, - 0.044062886688337265, - -0.04857978290438118, - 0.052607461706064784, - -0.056048081078209105, - 0.058817106327538726, - -0.06084590754218613, - 0.06208388100251895, - 0.9375003025680546, - 0.06208388100251895, - -0.06084590754218613, - 0.058817106327538726, - -0.056048081078209105, - 0.052607461706064784, - -0.04857978290438118, - 0.044062886688337265, - -0.03916493952679325, - 0.03400116752010972, - -0.02869042197225499, - 0.023351692368405477, - -0.01810068406300195, - 0.013046573943979376, - -0.008289049174613953, - 0.003915722194550309, - 1.0063905996254467e-16, - -0.0034005320447053696, - 0.006245171256720193, - -0.00851058366772981, - 0.01019071615665419, - -0.011296156240663045, - 0.01185298019699458, - -0.011901147266026046, - 0.011492512118343826, - -0.010688539065884867, - 0.009557809308226558, - -0.008173416627546943, - 0.006610347331237228, - -0.004942936992960944, - 0.003242489907858635, - -0.001575137531433456, - -8.297338214945322e-17, - 0.0014322992948101853, - -0.0026807315526779464, - 0.003714554414731469, - -0.004513595500789128, - 0.005068140772503758, - -0.005378455081534177, - 0.005453974732678532, - -0.005312222191564676, - 0.004977500880593015, - -0.004479433137231434, - 0.00385140677724417, - -0.003128995352023552, - 0.0023484142653889145, - -0.0015450696726810457, - 0.000752249860282943, - 7.209995999923543e-18, - -0.0006857887606066072, - 0.001284055499878637, - -0.0017790014114850766, - 0.0021602782649567656, - -0.002422944105830126, - 0.0025672055778467244, - -0.0025979735902941564, - 0.002524264795160409, - -0.0023584853377038536, - 0.0021156355406508768, - -0.0018124745987316703, - 0.0014666830951029159, - -0.0010960583702874734, - 0.0007177736996011124, - -0.0003477271314898191, - -3.649849800074037e-17, - 0.0003135611435773604, - -0.0005836318265664195, - 0.0008035650497546995, - -0.0009694194473815131, - 0.0010798700171927062, - -0.0011360155279515375, - 0.0011411000481453986, - -0.001100168333253743, - 0.0010196760363510072, - -0.0009070758950716863, - 0.0007704002699025563, - -0.0006178587763843014, - 0.00045746741021428363, - -0.00029672267470682895, - 0.00014233096258373752, - 8.736848542712518e-18, - -0.00012570429408715404, - 0.00023143548325566652, - -0.0003150817831073127, - 0.00037572428338265103, - -0.00041354588242148774, - 0.0004297001408507999, - -0.00042615022958145474, - 0.00040548854166410335, - -0.00037074739096535606, - 0.00032521058924288055, - -0.00027223465216542913, - 0.00021508702286534775, - -0.00015680711547266284, - 0.0001000942700094669, - -4.7224970562679786e-05, - -8.761722338284784e-19, - 4.027933539700687e-05, - -7.280811540502643e-05, - 9.725114087319391e-05, - -0.00011369679289372431, - 0.00012259602869291528, - -0.0001246913509069703, - 0.00012094053924787859, - -0.00011243964464523357, - 0.0001003492559243388, - -8.582740473543781e-05, - 6.997172608513909e-05, - -5.37726902353467e-05, - 3.80789154370508e-05, - -2.357480377576126e-05, - 1.0770051522629687e-05, - -2.4187562985455694e-19, - -8.564666558168402e-06, - 1.490069206906713e-05, - -1.9109487313809015e-05, - 2.1390883855947267e-05, - -2.201537812656286e-05, - 2.1296664705444228e-05, - -1.956600058898423e-05, - 1.7149627991629572e-05, - -1.4350136921309217e-05, - 1.1432294466108613e-05, - -8.613527303281799e-06, - 6.05893601403254e-06, - -3.880458963785995e-06, - 2.1395999264221267e-06, - -8.529928242661527e-07, - -1.4287799853519804e-19, +pub const HZ48000_COEFFICIENTS: [f64; 441] = [ + -8.771146084517915e-23, + 1.0570942208391173e-09, + -3.0666660745656922e-09, + 6.298083919549404e-09, + -1.1009494492149948e-08, + 1.7421987235394415e-08, + -2.5689109092988082e-08, + 3.5862674329115807e-08, + -4.7856266645529607e-08, + 6.140828902806317e-08, + -7.604685422793451e-08, + 9.105919616127095e-08, + -1.0546858823253603e-07, + 1.1802194634247329e-07, + -1.271913407105633e-07, + 1.3119251294318607e-07, + -1.280231690229821e-07, + 1.1552327779416195e-07, + -9.145883941617693e-08, + 5.362960091161619e-08, + -7.7217226107300735e-22, + -7.114875946245203e-08, + 1.6104911667095417e-07, + -2.7026527132156615e-07, + 3.985058614410556e-07, + -5.444396250307578e-07, + 7.055238955116609e-07, + -8.778569687571143e-07, + 1.0560662317828128e-06, + -1.2332443646663861e-06, + 1.4009458289943576e-06, + -1.5492551604686838e-06, + 1.6669372292816672e-06, + -1.7416775803496403e-06, + 1.760418204028636e-06, + -1.7097906350117925e-06, + 1.5766441855366919e-06, + -1.3486624782617763e-06, + 1.015056381074019e-06, + -5.673161244592637e-07, + 9.792562157691215e-20, + 6.884681756425475e-07, + -1.4950254408593126e-06, + 2.4112566283065856e-06, + -3.4227817199744546e-06, + 4.5087853828418386e-06, + -5.641730870612593e-06, + 6.787299186897442e-06, + -7.904591223857538e-06, + 8.946625393428547e-06, + -9.861155999917786e-06, + 1.059182828621353e-05, + -1.1079674832069601e-05, + 1.126494499675449e-05, + -1.1089244681251284e-05, + 1.0497948234446287e-05, + -9.442828331471874e-06, + 7.884833680573385e-06, + -5.796929105544363e-06, + 3.1668985925331214e-06, + -1.0267057094301908e-18, + -3.678648971030143e-06, + 7.82207956681534e-06, + -1.2360003045077198e-05, + 1.719798834414252e-05, + -2.221740239139365e-05, + 2.727621538905652e-05, + -3.221075420642931e-05, + 3.6838462372847274e-05, + -4.096169540744633e-05, + 4.4372545871397646e-05, + -4.6858654348383316e-05, + 4.8209921539750954e-05, + -4.8225994005681544e-05, + 4.6724353186234967e-05, + -4.354879574733261e-05, + 3.857805468230202e-05, + -3.1734276690489895e-05, + 2.2991043887561944e-05, + -1.2380608558550801e-05, + 1.0714892504045419e-18, + 1.3984336126251783e-05, + -2.933369198803881e-05, + 4.5736344959148374e-05, + -6.280915808675244e-05, + 8.010174059784683e-05, + -9.710330972590319e-05, + 0.00011325231780323752, + -0.00012794882111289484, + 0.00014056947148065392, + -0.00015048491054933942, + 0.00015707924308034133, + -0.0001597711628045901, + 0.00015803620591136328, + -0.0001514295170121378, + 0.00013960843420358319, + -0.0001223541374673828, + 9.95915616676161e-05, + -7.140675507600406e-05, + 3.806086941676025e-05, + 5.522292755457904e-18, + -4.213984402529341e-05, + 8.753326840947647e-05, + -0.00013517203082831053, + 0.0001838779481747696, + -0.00023232236376993098, + 0.00027905217036022, + -0.00032252217407439016, + 0.0003611333624045614, + -0.0003932764120704118, + 0.00041737954732315945, + -0.0004319596433444126, + 0.00043567527068183264, + -0.00042738020297625895, + 0.00040617576920780796, + -0.00037146033048375895, + 0.00032297410642885815, + -0.0002608375729289383, + 0.00018558170547350593, + -9.816845330568487e-05, + 6.397895727529846e-19, + 0.00010708440490237048, + -0.00022082497375172826, + 0.00033857186283733177, + -0.00045732982316766367, + 0.0005738168187053221, + -0.000684535969656674, + 0.0007858597238898489, + -0.0008741247047765545, + 0.000945735242783811, + -0.0009972731855902538, + 0.0010256112120681994, + -0.0010280265635201971, + 0.0010023118646185563, + -0.000946879548805299, + 0.0008608563389349595, + -0.0007441642719534753, + 0.0005975849020744005, + -0.00042280357297843214, + 0.00022243101557540356, + -3.474391490098325e-17, + -0.00024006465851531805, + 0.0004925037811490474, + -0.0007513095024493709, + 0.0010098384606174067, + -0.0012609511916318978, + 0.001497175575304658, + -0.0017108913477184788, + 0.00189453189460518, + -0.002040798799462615, + 0.0021428839636620438, + -0.002194693567447552, + 0.002191067722720476, + -0.0021279894003546954, + 0.0020027761127039494, + -0.001814247908162659, + 0.0015628654968098813, + -0.0012508327770007735, + 0.0008821586696520896, + -0.00046267398178344456, + -3.3039584853563575e-17, + 0.0004965333609844947, + -0.0010160207840665381, + 0.0015461657408802655, + -0.0020735138415665724, + 0.002583732114598345, + -0.0030619295805850155, + 0.0034930131304211584, + -0.0038620714583133555, + 0.004154778673084419, + -0.004357808253637993, + 0.004459247260332142, + -0.004449000192806269, + 0.004319171620808093, + -0.004064416725949453, + 0.003682249190367868, + -0.00317329645683304, + 0.0025414932599423837, + -0.0017942054777252078, + 0.0009422777573359167, + -7.350185540376589e-17, + -0.0010150103856962798, + 0.002082011577823492, + -0.0031774248180713193, + 0.00427520317347509, + -0.005347276351155804, + 0.006364064256720479, + -0.0072950500213230345, + 0.008109401423325797, + -0.008776628049842111, + 0.00926726022866957, + -0.009553534753966662, + 0.009610071764202605, + -0.009414526835002754, + 0.008948202440030514, + -0.008196603417779811, + 0.007149921958698993, + -0.005803438882712496, + 0.00415782958901113, + -0.0022193649952320592, + 8.812638424710787e-17, + 0.00248265554875482, + -0.005205485774200194, + 0.008140137239069204, + -0.011253403840824383, + 0.014507722254727249, + -0.017861769381184747, + 0.02127115074947851, + -0.024689166551051196, + 0.02806763998183295, + -0.031357790917889976, + 0.03451113667723182, + -0.0374804007687864, + 0.04022041012286698, + -0.04268896135026697, + 0.04484763709216705, + -0.04666255449128448, + 0.048105029215477144, + -0.04915214026651778, + 0.049787182966797176, + 0.9499999996921324, + 0.049787182966797176, + -0.04915214026651778, + 0.048105029215477144, + -0.04666255449128448, + 0.04484763709216705, + -0.04268896135026697, + 0.04022041012286698, + -0.0374804007687864, + 0.03451113667723182, + -0.031357790917889976, + 0.02806763998183295, + -0.024689166551051196, + 0.02127115074947851, + -0.017861769381184747, + 0.014507722254727249, + -0.011253403840824383, + 0.008140137239069204, + -0.005205485774200194, + 0.00248265554875482, + 8.812638424710787e-17, + -0.0022193649952320592, + 0.00415782958901113, + -0.005803438882712496, + 0.007149921958698993, + -0.008196603417779811, + 0.008948202440030514, + -0.009414526835002754, + 0.009610071764202605, + -0.009553534753966662, + 0.00926726022866957, + -0.008776628049842111, + 0.008109401423325797, + -0.0072950500213230345, + 0.006364064256720479, + -0.005347276351155804, + 0.00427520317347509, + -0.0031774248180713193, + 0.002082011577823492, + -0.0010150103856962798, + -7.350185540376589e-17, + 0.0009422777573359167, + -0.0017942054777252078, + 0.0025414932599423837, + -0.00317329645683304, + 0.003682249190367868, + -0.004064416725949453, + 0.004319171620808093, + -0.004449000192806269, + 0.004459247260332142, + -0.004357808253637993, + 0.004154778673084419, + -0.0038620714583133555, + 0.0034930131304211584, + -0.0030619295805850155, + 0.002583732114598345, + -0.0020735138415665724, + 0.0015461657408802655, + -0.0010160207840665381, + 0.0004965333609844947, + -3.3039584853563575e-17, + -0.00046267398178344456, + 0.0008821586696520896, + -0.0012508327770007735, + 0.0015628654968098813, + -0.001814247908162659, + 0.0020027761127039494, + -0.0021279894003546954, + 0.002191067722720476, + -0.002194693567447552, + 0.0021428839636620438, + -0.002040798799462615, + 0.00189453189460518, + -0.0017108913477184788, + 0.001497175575304658, + -0.0012609511916318978, + 0.0010098384606174067, + -0.0007513095024493709, + 0.0004925037811490474, + -0.00024006465851531805, + -3.474391490098325e-17, + 0.00022243101557540356, + -0.00042280357297843214, + 0.0005975849020744005, + -0.0007441642719534753, + 0.0008608563389349595, + -0.000946879548805299, + 0.0010023118646185563, + -0.0010280265635201971, + 0.0010256112120681994, + -0.0009972731855902538, + 0.000945735242783811, + -0.0008741247047765545, + 0.0007858597238898489, + -0.000684535969656674, + 0.0005738168187053221, + -0.00045732982316766367, + 0.00033857186283733177, + -0.00022082497375172826, + 0.00010708440490237048, + 6.397895727529846e-19, + -9.816845330568487e-05, + 0.00018558170547350593, + -0.0002608375729289383, + 0.00032297410642885815, + -0.00037146033048375895, + 0.00040617576920780796, + -0.00042738020297625895, + 0.00043567527068183264, + -0.0004319596433444126, + 0.00041737954732315945, + -0.0003932764120704118, + 0.0003611333624045614, + -0.00032252217407439016, + 0.00027905217036022, + -0.00023232236376993098, + 0.0001838779481747696, + -0.00013517203082831053, + 8.753326840947647e-05, + -4.213984402529341e-05, + 5.522292755457904e-18, + 3.806086941676025e-05, + -7.140675507600406e-05, + 9.95915616676161e-05, + -0.0001223541374673828, + 0.00013960843420358319, + -0.0001514295170121378, + 0.00015803620591136328, + -0.0001597711628045901, + 0.00015707924308034133, + -0.00015048491054933942, + 0.00014056947148065392, + -0.00012794882111289484, + 0.00011325231780323752, + -9.710330972590319e-05, + 8.010174059784683e-05, + -6.280915808675244e-05, + 4.5736344959148374e-05, + -2.933369198803881e-05, + 1.3984336126251783e-05, + 1.0714892504045419e-18, + -1.2380608558550801e-05, + 2.2991043887561944e-05, + -3.1734276690489895e-05, + 3.857805468230202e-05, + -4.354879574733261e-05, + 4.6724353186234967e-05, + -4.8225994005681544e-05, + 4.8209921539750954e-05, + -4.6858654348383316e-05, + 4.4372545871397646e-05, + -4.096169540744633e-05, + 3.6838462372847274e-05, + -3.221075420642931e-05, + 2.727621538905652e-05, + -2.221740239139365e-05, + 1.719798834414252e-05, + -1.2360003045077198e-05, + 7.82207956681534e-06, + -3.678648971030143e-06, + -1.0267057094301908e-18, + 3.1668985925331214e-06, + -5.796929105544363e-06, + 7.884833680573385e-06, + -9.442828331471874e-06, + 1.0497948234446287e-05, + -1.1089244681251284e-05, + 1.126494499675449e-05, + -1.1079674832069601e-05, + 1.059182828621353e-05, + -9.861155999917786e-06, + 8.946625393428547e-06, + -7.904591223857538e-06, + 6.787299186897442e-06, + -5.641730870612593e-06, + 4.5087853828418386e-06, + -3.4227817199744546e-06, + 2.4112566283065856e-06, + -1.4950254408593126e-06, + 6.884681756425475e-07, + 9.792562157691215e-20, + -5.673161244592637e-07, + 1.015056381074019e-06, + -1.3486624782617763e-06, + 1.5766441855366919e-06, + -1.7097906350117925e-06, + 1.760418204028636e-06, + -1.7416775803496403e-06, + 1.6669372292816672e-06, + -1.5492551604686838e-06, + 1.4009458289943576e-06, + -1.2332443646663861e-06, + 1.0560662317828128e-06, + -8.778569687571143e-07, + 7.055238955116609e-07, + -5.444396250307578e-07, + 3.985058614410556e-07, + -2.7026527132156615e-07, + 1.6104911667095417e-07, + -7.114875946245203e-08, + -7.7217226107300735e-22, + 5.362960091161619e-08, + -9.145883941617693e-08, + 1.1552327779416195e-07, + -1.280231690229821e-07, + 1.3119251294318607e-07, + -1.271913407105633e-07, + 1.1802194634247329e-07, + -1.0546858823253603e-07, + 9.105919616127095e-08, + -7.604685422793451e-08, + 6.140828902806317e-08, + -4.7856266645529607e-08, + 3.5862674329115807e-08, + -2.5689109092988082e-08, + 1.7421987235394415e-08, + -1.1009494492149948e-08, + 6.298083919549404e-09, + -3.0666660745656922e-09, + 1.0570942208391173e-09, + -8.771146084517915e-23, ]; #[allow(clippy::excessive_precision)] -pub const HZ88200_HIGH: [f64; 257] = [ - -2.7177973650537016e-06, - 2.615116391456718e-06, - 4.371253470079787e-06, - -4.527642205236769e-06, - -6.343166732321034e-06, - 7.206853357278462e-06, - 8.608531809529578e-06, - -1.0831207319855358e-05, - -1.11168738348255e-05, - 1.5597004565971875e-05, - 1.3787248972132889e-05, - -2.1716246512168148e-05, - -1.6503125021559895e-05, - 2.9413629227200937e-05, - 1.9107254724178818e-05, - -3.8922617925675015e-05, - -2.139666772922098e-05, - 5.04805630067818e-05, - 2.3117918532840763e-05, - -6.432283649476294e-05, - -2.396273660981686e-05, - 8.067598787603222e-05, - 2.3564231002785538e-05, - -9.974994011030785e-05, - -2.149380441843341e-05, - 0.00012172926954179013, - 1.7258931076241913e-05, - -0.00014676363699943706, - -1.030194790401862e-05, - 0.0001749574609349626, - 1.8686890713852827e-19, - -0.0002063589463295164, - 1.4333731499066454e-05, - 0.00024094860458611302, - -3.344740787382722e-05, - -0.0002786274189628284, - 5.814642716712084e-05, - 0.00031920482651865724, - -8.928776855240112e-05, - -0.0003623867002575848, - 0.0001277727488249831, - 0.0004077635233859557, - -0.00017453818403045162, - -0.00045479895057210033, - 0.0002305459825657413, - 0.000502818948057389, - -0.00029677123118056025, - -0.000551001694669649, - 0.00037418887145730905, - 0.0005983684084941591, - -0.00046375910077735565, - -0.0006437752384124257, - 0.0005664116676635973, - 0.0006859063251323009, - -0.0006830292658687915, - -0.0007232680918226466, - 0.0008144302637888361, - 0.000754184768991671, - -0.0009613510348663591, - -0.0007767950905092509, - 0.0011244281797910076, - 0.0007890500159662434, - -0.0013041809517326277, - -0.000788711236573936, - 0.0015009942108703117, - 0.00077335010432126, - -0.00171510224350985, - -0.0007403464826028671, - 0.0019465737836333772, - 0.0006868868445776879, - -0.002195298570445892, - -0.0006099607339225542, - 0.002460975764167073, - 0.0005063544381987515, - -0.002743104523907686, - -0.00037264038864942025, - 0.003040977026074337, - 0.00020516036410315268, - -0.0033536741696310896, - -1.7263210195899785e-18, - 0.0036800641761425457, - -0.0002470486740043912, - -0.004018804248416292, - 0.0005405410133353366, - 0.004368345402486541, - -0.000885455697808869, - -0.004726940534504569, - 0.0012873008119620815, - 0.005092655727810584, - -0.001752261157027223, - -0.005463384747140825, - 0.0022874024955170562, - 0.005836866607731414, - -0.002900955402436162, - -0.006210706048228085, - 0.0036027121698355103, - 0.006582396679029297, - -0.0044045872407267005, - -0.006949346523208571, - 0.005321419255211435, - 0.007308905616676035, - -0.00637213881767667, - -0.007658395288879785, - 0.007581505281049055, - 0.00799513870616462, - -0.008982757034954967, - -0.008316492227824646, - 0.010621782037025395, - 0.00861987710070591, - -0.012563926080135948, - -0.008902811002566763, - 0.01490560790319659, - 0.0091629389377553, - -0.017795223882502632, - -0.009398062991380806, - 0.021473342249052296, - 0.009606170460125666, - -0.026356732255292163, - -0.009785459899039199, - 0.033234410282523816, - 0.009934364653757865, - -0.04379972993086113, - -0.010051573486085123, - 0.06245943058441552, - 0.01013604794705137, - -0.1053787714300839, - -0.010187036204577683, - 0.31806791600342954, - 0.5102041545837825, - 0.31806791600342954, - -0.010187036204577683, - -0.1053787714300839, - 0.01013604794705137, - 0.06245943058441552, - -0.010051573486085123, - -0.04379972993086113, - 0.009934364653757865, - 0.033234410282523816, - -0.009785459899039199, - -0.026356732255292163, - 0.009606170460125666, - 0.021473342249052296, - -0.009398062991380806, - -0.017795223882502632, - 0.0091629389377553, - 0.01490560790319659, - -0.008902811002566763, - -0.012563926080135948, - 0.00861987710070591, - 0.010621782037025395, - -0.008316492227824646, - -0.008982757034954967, - 0.00799513870616462, - 0.007581505281049055, - -0.007658395288879785, - -0.00637213881767667, - 0.007308905616676035, - 0.005321419255211435, - -0.006949346523208571, - -0.0044045872407267005, - 0.006582396679029297, - 0.0036027121698355103, - -0.006210706048228085, - -0.002900955402436162, - 0.005836866607731414, - 0.0022874024955170562, - -0.005463384747140825, - -0.001752261157027223, - 0.005092655727810584, - 0.0012873008119620815, - -0.004726940534504569, - -0.000885455697808869, - 0.004368345402486541, - 0.0005405410133353366, - -0.004018804248416292, - -0.0002470486740043912, - 0.0036800641761425457, - -1.7263210195899785e-18, - -0.0033536741696310896, - 0.00020516036410315268, - 0.003040977026074337, - -0.00037264038864942025, - -0.002743104523907686, - 0.0005063544381987515, - 0.002460975764167073, - -0.0006099607339225542, - -0.002195298570445892, - 0.0006868868445776879, - 0.0019465737836333772, - -0.0007403464826028671, - -0.00171510224350985, - 0.00077335010432126, - 0.0015009942108703117, - -0.000788711236573936, - -0.0013041809517326277, - 0.0007890500159662434, - 0.0011244281797910076, - -0.0007767950905092509, - -0.0009613510348663591, - 0.000754184768991671, - 0.0008144302637888361, - -0.0007232680918226466, - -0.0006830292658687915, - 0.0006859063251323009, - 0.0005664116676635973, - -0.0006437752384124257, - -0.00046375910077735565, - 0.0005983684084941591, - 0.00037418887145730905, - -0.000551001694669649, - -0.00029677123118056025, - 0.000502818948057389, - 0.0002305459825657413, - -0.00045479895057210033, - -0.00017453818403045162, - 0.0004077635233859557, - 0.0001277727488249831, - -0.0003623867002575848, - -8.928776855240112e-05, - 0.00031920482651865724, - 5.814642716712084e-05, - -0.0002786274189628284, - -3.344740787382722e-05, - 0.00024094860458611302, - 1.4333731499066454e-05, - -0.0002063589463295164, - 1.8686890713852827e-19, - 0.0001749574609349626, - -1.030194790401862e-05, - -0.00014676363699943706, - 1.7258931076241913e-05, - 0.00012172926954179013, - -2.149380441843341e-05, - -9.974994011030785e-05, - 2.3564231002785538e-05, - 8.067598787603222e-05, - -2.396273660981686e-05, - -6.432283649476294e-05, - 2.3117918532840763e-05, - 5.04805630067818e-05, - -2.139666772922098e-05, - -3.8922617925675015e-05, - 1.9107254724178818e-05, - 2.9413629227200937e-05, - -1.6503125021559895e-05, - -2.1716246512168148e-05, - 1.3787248972132889e-05, - 1.5597004565971875e-05, - -1.11168738348255e-05, - -1.0831207319855358e-05, - 8.608531809529578e-06, - 7.206853357278462e-06, - -6.343166732321034e-06, - -4.527642205236769e-06, - 4.371253470079787e-06, - 2.615116391456718e-06, - -2.7177973650537016e-06, +pub const HZ88200_COEFFICIENTS: [f64; 441] = [ + -3.0915687919410175e-09, + -4.380355015683147e-09, + 7.889301672202866e-09, + 7.814782506112341e-09, + -1.601722660647997e-08, + -1.162897576000204e-08, + 2.875448571604994e-08, + 1.510435454511938e-08, + -4.758343509079679e-08, + -1.7052240013897604e-08, + 7.414020115596361e-08, + 1.5685867044913693e-08, + -1.1013103295646797e-07, + -8.486685443549004e-09, + 1.5720816822680103e-07, + -7.927830161980731e-09, + -2.1679956837762108e-07, + 3.792557598966923e-08, + 2.898880068451338e-07, + -8.695906085997518e-08, + -3.767366685630587e-07, + 1.6162916242945288e-07, + 4.765606699845109e-07, + -2.6969832218324916e-07, + -5.871467509278004e-07, + 4.200367275533767e-07, + 7.044268061921138e-07, + -6.224841713408436e-07, + -8.220148697645299e-07, + 8.876100914738924e-07, + 9.307215563684733e-07, + -1.2263549285013004e-06, + -1.0180646862659517e-06, + 1.6495375341784678e-06, + 1.0677997143447814e-06, + -2.1672160434902636e-06, + -1.0594984616938665e-06, + 2.7878934786218487e-06, + 9.682092811742628e-07, + -3.5175644448463682e-06, + -7.642359224979942e-07, + 4.3586056187007376e-06, + 4.130757186506121e-07, + -5.3085202796190045e-06, + 1.2443999883255955e-07, + 6.358555800084345e-06, + -8.917594302752569e-07, + -7.492222625290795e-06, + 1.9359346976965215e-06, + 8.683753613501706e-06, + -3.3066166723901634e-06, + -9.896553377686571e-06, + 5.054684641484621e-06, + 1.1081698107413262e-05, + -7.230482378766363e-06, + -1.2176556837151924e-05, + 9.881641808207685e-06, + 1.310361479118067e-05, + -1.3050487570176674e-05, + -1.3769587763932494e-05, + 1.6771030367931327e-05, + 1.4064922949971825e-05, + -2.1065573831506833e-05, + -1.386378567354253e-05, + 2.5940978523130324e-05, + 1.3024632549176766e-05, + -3.138464724410613e-05, + -1.1391469231232117e-05, + 3.7360317493302477e-05, + 8.795884637183773e-06, + -4.380376916620311e-05, + -5.059942993990692e-06, + 5.061857766137688e-05, + -1.2729279960739985e-18, + -5.767206367871742e-05, + 6.568510314697647e-06, + 6.479161027901487e-05, + -1.482529579321305e-05, + -7.176153430630762e-05, + 2.493843998881951e-05, + 7.832071210975769e-05, + -3.705668910231432e-05, + -8.416116770617441e-05, + 5.1300867944157375e-05, + 8.892783420621756e-05, + -6.77545401257115e-05, + -9.221969567445976e-05, + 8.645406887217791e-05, + 9.359250590922007e-05, + -0.00010737827686101525, + -9.256326236908447e-05, + 0.0001304379450839715, + 8.861658728324014e-05, + -0.0001554654305418504, + -8.121313372668574e-05, + 0.0001822047191488953, + 6.980009223001803e-05, + -0.00021030226203261508, + -5.382382370758039e-05, + 0.00023929896894150197, + 3.274458779064277e-05, + -0.00026862375023307836, + -6.0532729964936305e-06, + 0.00029758900751817864, + -2.671003221043526e-05, + -0.000325388471242336, + 6.593585899246278e-05, + 0.0003510977702523597, + -0.00011192387190357726, + -0.0003736780929427704, + 0.0001648604519001771, + 0.000391983261423784, + -0.00022479579247089605, + -0.00040477048916979987, + 0.00029162101662982327, + 0.000410715029047172, + -0.00036504587911318116, + -0.00040842884314578015, + 0.0004445776651260253, + 0.0003964833395366939, + -0.0005295019321669618, + -0.00037343612546606575, + 0.0006188657627433, + 0.00033786162350484585, + -0.0007114642013347918, + -0.0002883852891386307, + 0.0008058305372427186, + 0.00022372105788811238, + -0.0009002310648224352, + -0.0001427115403024383, + 0.0009926649033332297, + 4.437037731172248e-05, + -0.0010808693900394488, + 7.207393010339128e-05, + 0.0011623314725868622, + -0.00020713349063999724, + -0.0012343054209503327, + 0.0003610166387724715, + 0.0012938370568711898, + -0.0005335845995545083, + -0.0013377945617159535, + 0.0007243101385594058, + 0.0013629057746815654, + -0.0009322391431135004, + -0.0013658017353411673, + 0.0011559560634399873, + 0.001343066061215347, + -0.0013935540949750406, + -0.0012912895862815387, + 0.0016426109036876274, + 0.0012071295242966488, + -0.001900170582736454, + -0.0010873722658800765, + 0.0021627323794196534, + 0.0009289987749149286, + -0.0024262465443003586, + -0.0007292514223482997, + 0.002686117427541705, + 0.00048570098808220843, + -0.0029372136780933438, + -0.0001963124782057012, + 0.0031738850853730077, + -0.0001404916512597725, + -0.003389985234223304, + 0.0005257722987911238, + 0.003578898712425241, + -0.0009600199781197914, + -0.003733571100688918, + 0.001443107923088798, + 0.0038465393642386826, + -0.001974253349587081, + -0.003909959516535164, + 0.0025519880785917614, + 0.003915627483496994, + -0.003174139582456504, + -0.003854987874872023, + 0.0038378233438448386, + 0.0037191237352354268, + -0.0045394472161245205, + -0.003498718093020069, + 0.0052747282494331775, + 0.0031839749242818863, + -0.006038722202602122, + -0.0027644824727652566, + 0.006825865702928783, + 0.0022289948598659135, + -0.007630030749218701, + -0.0015650971422069776, + 0.008444590984764183, + 0.0007587019748704784, + -0.009262498902418221, + 0.00020670150584601254, + 0.010076372890169986, + -0.0013511718972066557, + -0.010878592789031707, + 0.002700017220606699, + 0.0116614024217388, + -0.004286419502573899, + -0.01241701736642861, + 0.0061555897334271996, + 0.013137736099170549, + -0.008371571243092978, + -0.013816052517290636, + 0.01102886093018991, + 0.014444767785294677, + -0.014273331179705539, + -0.015017099419278979, + 0.01834245227603654, + 0.015526785545393338, + -0.023649360357418405, + -0.01596818233340632, + 0.030978952287937712, + 0.016336352716813843, + -0.04202003363176658, + -0.016627144664162638, + 0.061174433061502255, + 0.01683725745918051, + -0.10460220813936734, + -0.01696429467574168, + 0.3178080921167005, + 0.5170068048932956, + 0.3178080921167005, + -0.01696429467574168, + -0.10460220813936734, + 0.01683725745918051, + 0.061174433061502255, + -0.016627144664162638, + -0.04202003363176658, + 0.016336352716813843, + 0.030978952287937712, + -0.01596818233340632, + -0.023649360357418405, + 0.015526785545393338, + 0.01834245227603654, + -0.015017099419278979, + -0.014273331179705539, + 0.014444767785294677, + 0.01102886093018991, + -0.013816052517290636, + -0.008371571243092978, + 0.013137736099170549, + 0.0061555897334271996, + -0.01241701736642861, + -0.004286419502573899, + 0.0116614024217388, + 0.002700017220606699, + -0.010878592789031707, + -0.0013511718972066557, + 0.010076372890169986, + 0.00020670150584601254, + -0.009262498902418221, + 0.0007587019748704784, + 0.008444590984764183, + -0.0015650971422069776, + -0.007630030749218701, + 0.0022289948598659135, + 0.006825865702928783, + -0.0027644824727652566, + -0.006038722202602122, + 0.0031839749242818863, + 0.0052747282494331775, + -0.003498718093020069, + -0.0045394472161245205, + 0.0037191237352354268, + 0.0038378233438448386, + -0.003854987874872023, + -0.003174139582456504, + 0.003915627483496994, + 0.0025519880785917614, + -0.003909959516535164, + -0.001974253349587081, + 0.0038465393642386826, + 0.001443107923088798, + -0.003733571100688918, + -0.0009600199781197914, + 0.003578898712425241, + 0.0005257722987911238, + -0.003389985234223304, + -0.0001404916512597725, + 0.0031738850853730077, + -0.0001963124782057012, + -0.0029372136780933438, + 0.00048570098808220843, + 0.002686117427541705, + -0.0007292514223482997, + -0.0024262465443003586, + 0.0009289987749149286, + 0.0021627323794196534, + -0.0010873722658800765, + -0.001900170582736454, + 0.0012071295242966488, + 0.0016426109036876274, + -0.0012912895862815387, + -0.0013935540949750406, + 0.001343066061215347, + 0.0011559560634399873, + -0.0013658017353411673, + -0.0009322391431135004, + 0.0013629057746815654, + 0.0007243101385594058, + -0.0013377945617159535, + -0.0005335845995545083, + 0.0012938370568711898, + 0.0003610166387724715, + -0.0012343054209503327, + -0.00020713349063999724, + 0.0011623314725868622, + 7.207393010339128e-05, + -0.0010808693900394488, + 4.437037731172248e-05, + 0.0009926649033332297, + -0.0001427115403024383, + -0.0009002310648224352, + 0.00022372105788811238, + 0.0008058305372427186, + -0.0002883852891386307, + -0.0007114642013347918, + 0.00033786162350484585, + 0.0006188657627433, + -0.00037343612546606575, + -0.0005295019321669618, + 0.0003964833395366939, + 0.0004445776651260253, + -0.00040842884314578015, + -0.00036504587911318116, + 0.000410715029047172, + 0.00029162101662982327, + -0.00040477048916979987, + -0.00022479579247089605, + 0.000391983261423784, + 0.0001648604519001771, + -0.0003736780929427704, + -0.00011192387190357726, + 0.0003510977702523597, + 6.593585899246278e-05, + -0.000325388471242336, + -2.671003221043526e-05, + 0.00029758900751817864, + -6.0532729964936305e-06, + -0.00026862375023307836, + 3.274458779064277e-05, + 0.00023929896894150197, + -5.382382370758039e-05, + -0.00021030226203261508, + 6.980009223001803e-05, + 0.0001822047191488953, + -8.121313372668574e-05, + -0.0001554654305418504, + 8.861658728324014e-05, + 0.0001304379450839715, + -9.256326236908447e-05, + -0.00010737827686101525, + 9.359250590922007e-05, + 8.645406887217791e-05, + -9.221969567445976e-05, + -6.77545401257115e-05, + 8.892783420621756e-05, + 5.1300867944157375e-05, + -8.416116770617441e-05, + -3.705668910231432e-05, + 7.832071210975769e-05, + 2.493843998881951e-05, + -7.176153430630762e-05, + -1.482529579321305e-05, + 6.479161027901487e-05, + 6.568510314697647e-06, + -5.767206367871742e-05, + -1.2729279960739985e-18, + 5.061857766137688e-05, + -5.059942993990692e-06, + -4.380376916620311e-05, + 8.795884637183773e-06, + 3.7360317493302477e-05, + -1.1391469231232117e-05, + -3.138464724410613e-05, + 1.3024632549176766e-05, + 2.5940978523130324e-05, + -1.386378567354253e-05, + -2.1065573831506833e-05, + 1.4064922949971825e-05, + 1.6771030367931327e-05, + -1.3769587763932494e-05, + -1.3050487570176674e-05, + 1.310361479118067e-05, + 9.881641808207685e-06, + -1.2176556837151924e-05, + -7.230482378766363e-06, + 1.1081698107413262e-05, + 5.054684641484621e-06, + -9.896553377686571e-06, + -3.3066166723901634e-06, + 8.683753613501706e-06, + 1.9359346976965215e-06, + -7.492222625290795e-06, + -8.917594302752569e-07, + 6.358555800084345e-06, + 1.2443999883255955e-07, + -5.3085202796190045e-06, + 4.130757186506121e-07, + 4.3586056187007376e-06, + -7.642359224979942e-07, + -3.5175644448463682e-06, + 9.682092811742628e-07, + 2.7878934786218487e-06, + -1.0594984616938665e-06, + -2.1672160434902636e-06, + 1.0677997143447814e-06, + 1.6495375341784678e-06, + -1.0180646862659517e-06, + -1.2263549285013004e-06, + 9.307215563684733e-07, + 8.876100914738924e-07, + -8.220148697645299e-07, + -6.224841713408436e-07, + 7.044268061921138e-07, + 4.200367275533767e-07, + -5.871467509278004e-07, + -2.6969832218324916e-07, + 4.765606699845109e-07, + 1.6162916242945288e-07, + -3.767366685630587e-07, + -8.695906085997518e-08, + 2.898880068451338e-07, + 3.792557598966923e-08, + -2.1679956837762108e-07, + -7.927830161980731e-09, + 1.5720816822680103e-07, + -8.486685443549004e-09, + -1.1013103295646797e-07, + 1.5685867044913693e-08, + 7.414020115596361e-08, + -1.7052240013897604e-08, + -4.758343509079679e-08, + 1.510435454511938e-08, + 2.875448571604994e-08, + -1.162897576000204e-08, + -1.601722660647997e-08, + 7.814782506112341e-09, + 7.889301672202866e-09, + -4.380355015683147e-09, + -3.0915687919410175e-09, ]; #[allow(clippy::excessive_precision)] -pub const HZ96000_HIGH: [f64; 257] = [ - -7.143923102926616e-20, - -4.351257283515671e-06, - -1.0907621221693655e-06, - 6.683906965798109e-06, - 3.2790831797631692e-06, - -9.13620584774856e-06, - -6.874774126129371e-06, - 1.1310163453885296e-05, - 1.2126657588758059e-05, - -1.265575645173113e-05, - -1.9166554046744026e-05, - 1.248152779539428e-05, - 2.794862730250771e-05, - -9.984713045871162e-06, - -3.8189337778906546e-05, - 4.303067618516991e-06, - 4.931445698739215e-05, - 5.411099152784563e-06, - -6.042042478905683e-05, - -1.989624512124905e-05, - 7.025763351163002e-05, - 3.967018140695091e-05, - -7.72428741451272e-05, - -6.490829524996992e-05, - 7.9507093138109e-05, - 9.532015440656505e-05, - -7.498274957040692e-05, - -0.00013003529388340405, - 6.153246053554203e-05, - 0.0001675104888314477, - -3.711737577180626e-05, - -0.00020547154270862667, - 4.380875381487169e-19, - 0.0002409023748839593, - 5.102778188775271e-05, - -0.0002700928372585857, - -0.00011640463560427953, - 0.00028875415767545045, - 0.00019556435946416691, - -0.0002922072182944144, - -0.0002867246276901985, - 0.0002756441457161496, - 0.0003867211644368446, - -0.0002344581317859229, - -0.0004909090262693411, - 0.00016463032935658855, - 0.0005931514994811955, - -6.315646659694906e-05, - -0.0006859142317553148, - -7.151005261521953e-05, - 0.0007604775938269121, - 0.0002390268636629881, - -0.0008072740136891249, - -0.00043677525441936056, - 0.0008163493865283938, - 0.0006595508119830226, - -0.0007779390126639911, - -0.0008993661964713373, - 0.0006831393454389286, - 0.00114539774688185, - -0.0005246477263799965, - -0.00138410277847316, - 0.00029753389026130304, - 0.0015995271835870914, - -1.8249308204396214e-17, - -0.001773813531049159, - -0.0003659190459608399, - 0.0018879086841156133, - 0.0007937657463383715, - -0.0019224576000959562, - -0.0012722307423329708, - 0.0018588571537189837, - 0.0017849305448029394, - -0.0016804313624166863, - -0.002310431475418928, - 0.0013736781639519724, - 0.0028225487316737353, - -0.0009295287890346657, - -0.0032909363527835883, - 0.0003445546173767534, - 0.003681968512252244, - 0.0003779460639632543, - -0.00395989577856263, - -0.0012270471817312566, - 0.004088242707216325, - 0.0021835391818022633, - -0.004031396395209965, - -0.003219566441111553, - 0.0037563205210019465, - 0.004298589347141008, - -0.003234316955594173, - -0.005375681266525964, - 0.0024427482073774184, - 0.00639814422962896, - -0.0013666295279113297, - -0.007306395272526002, - 4.1486825665423373e-17, - 0.008035036709816487, - 0.0016530123829845687, - -0.008513975101161425, - -0.0035775058023473096, - 0.008669388760192265, - 0.005747558403911614, - -0.008424248812489233, - -0.008126459615440715, - 0.007697946272157984, - 0.01066743495767241, - -0.006404309173724075, - -0.013314855511628901, - 0.004446782604876781, - 0.016005897813639956, - -0.00170849842523587, - -0.018672595414079837, - -0.001967340732302961, - 0.021244201096702293, - 0.006816838930484501, - -0.02364976015970634, - -0.013239145641295004, - 0.02582078137402188, - 0.021992767160156825, - -0.027693884168079493, - -0.03472848053143565, - 0.02921329883114049, - 0.05579974177257601, - -0.03033310130622086, - -0.10130968278740993, - 0.03101907530302571, - 0.3167001312508388, - 0.46875167199845, - 0.3167001312508388, - 0.03101907530302571, - -0.10130968278740993, - -0.03033310130622086, - 0.05579974177257601, - 0.02921329883114049, - -0.03472848053143565, - -0.027693884168079493, - 0.021992767160156825, - 0.02582078137402188, - -0.013239145641295004, - -0.02364976015970634, - 0.006816838930484501, - 0.021244201096702293, - -0.001967340732302961, - -0.018672595414079837, - -0.00170849842523587, - 0.016005897813639956, - 0.004446782604876781, - -0.013314855511628901, - -0.006404309173724075, - 0.01066743495767241, - 0.007697946272157984, - -0.008126459615440715, - -0.008424248812489233, - 0.005747558403911614, - 0.008669388760192265, - -0.0035775058023473096, - -0.008513975101161425, - 0.0016530123829845687, - 0.008035036709816487, - 4.1486825665423373e-17, - -0.007306395272526002, - -0.0013666295279113297, - 0.00639814422962896, - 0.0024427482073774184, - -0.005375681266525964, - -0.003234316955594173, - 0.004298589347141008, - 0.0037563205210019465, - -0.003219566441111553, - -0.004031396395209965, - 0.0021835391818022633, - 0.004088242707216325, - -0.0012270471817312566, - -0.00395989577856263, - 0.0003779460639632543, - 0.003681968512252244, - 0.0003445546173767534, - -0.0032909363527835883, - -0.0009295287890346657, - 0.0028225487316737353, - 0.0013736781639519724, - -0.002310431475418928, - -0.0016804313624166863, - 0.0017849305448029394, - 0.0018588571537189837, - -0.0012722307423329708, - -0.0019224576000959562, - 0.0007937657463383715, - 0.0018879086841156133, - -0.0003659190459608399, - -0.001773813531049159, - -1.8249308204396214e-17, - 0.0015995271835870914, - 0.00029753389026130304, - -0.00138410277847316, - -0.0005246477263799965, - 0.00114539774688185, - 0.0006831393454389286, - -0.0008993661964713373, - -0.0007779390126639911, - 0.0006595508119830226, - 0.0008163493865283938, - -0.00043677525441936056, - -0.0008072740136891249, - 0.0002390268636629881, - 0.0007604775938269121, - -7.151005261521953e-05, - -0.0006859142317553148, - -6.315646659694906e-05, - 0.0005931514994811955, - 0.00016463032935658855, - -0.0004909090262693411, - -0.0002344581317859229, - 0.0003867211644368446, - 0.0002756441457161496, - -0.0002867246276901985, - -0.0002922072182944144, - 0.00019556435946416691, - 0.00028875415767545045, - -0.00011640463560427953, - -0.0002700928372585857, - 5.102778188775271e-05, - 0.0002409023748839593, - 4.380875381487169e-19, - -0.00020547154270862667, - -3.711737577180626e-05, - 0.0001675104888314477, - 6.153246053554203e-05, - -0.00013003529388340405, - -7.498274957040692e-05, - 9.532015440656505e-05, - 7.9507093138109e-05, - -6.490829524996992e-05, - -7.72428741451272e-05, - 3.967018140695091e-05, - 7.025763351163002e-05, - -1.989624512124905e-05, - -6.042042478905683e-05, - 5.411099152784563e-06, - 4.931445698739215e-05, - 4.303067618516991e-06, - -3.8189337778906546e-05, - -9.984713045871162e-06, - 2.794862730250771e-05, - 1.248152779539428e-05, - -1.9166554046744026e-05, - -1.265575645173113e-05, - 1.2126657588758059e-05, - 1.1310163453885296e-05, - -6.874774126129371e-06, - -9.13620584774856e-06, - 3.2790831797631692e-06, - 6.683906965798109e-06, - -1.0907621221693655e-06, - -4.351257283515671e-06, - -7.143923102926616e-20, -]; - -#[allow(clippy::excessive_precision)] -pub const HZ48000_LOW: [f64; 129] = [ - -1.4287804154623814e-19, - -2.181517823792544e-06, - 6.5581470578797384e-06, - -1.3749507785319245e-05, - 2.4253243796448185e-05, - -3.8332995273522084e-05, - 5.589709009118714e-05, - -7.637845076415545e-05, - 9.862862369540776e-05, - -0.00012084049392574903, - 0.00014051485346619586, - -0.0001544852936160124, - 0.00015901371827411808, - -0.0001499650577703289, - 0.00012306455887295043, - -7.423453305983806e-05, - 8.761724975855263e-19, - 0.00010205526341099535, - -0.0002328085860166881, - 0.00039112756777908054, - -0.0005734475676350329, - 0.0007734400525193427, - -0.000981815162903983, - 0.001186299507498468, - -0.0013718244260180216, - 0.0015209507112593692, - -0.0016145432755263242, - 0.0016326939677845523, - -0.0015558734461504878, - 0.001366274669719045, - -0.0010492923645293963, - 0.000595066029150748, - -3.649850898800486e-17, - -0.0007318359380147118, - 0.0015875268203384464, - -0.0025444539959421624, - 0.0035698505829804923, - -0.004620849350959662, - 0.005645080848996707, - -0.006581853334149319, - 0.007363915351356501, - -0.007919768248015761, - 0.008176461349835438, - -0.008062769060436804, - 0.007512618931198147, - -0.006468614873048888, - 0.004885482036021499, - -0.002733251011439905, - 8.297340712720699e-17, - 0.0033060150358527928, - -0.007154990546444822, - 0.011495082976006515, - -0.016252871396153546, - 0.021334807123689837, - -0.026629632648107943, - 0.032011701411864754, - -0.03734508091577928, - 0.042488277143797916, - -0.047299381109978895, - 0.051641410759341526, - -0.05538760532182361, - 0.05842642570422467, - -0.06066602406289918, - 0.06203796801866447, - 0.9375005847868952, - 0.06203796801866447, - -0.06066602406289918, - 0.05842642570422467, - -0.05538760532182361, - 0.051641410759341526, - -0.047299381109978895, - 0.042488277143797916, - -0.03734508091577928, - 0.032011701411864754, - -0.026629632648107943, - 0.021334807123689837, - -0.016252871396153546, - 0.011495082976006515, - -0.007154990546444822, - 0.0033060150358527928, - 8.297340712720699e-17, - -0.002733251011439905, - 0.004885482036021499, - -0.006468614873048888, - 0.007512618931198147, - -0.008062769060436804, - 0.008176461349835438, - -0.007919768248015761, - 0.007363915351356501, - -0.006581853334149319, - 0.005645080848996707, - -0.004620849350959662, - 0.0035698505829804923, - -0.0025444539959421624, - 0.0015875268203384464, - -0.0007318359380147118, - -3.649850898800486e-17, - 0.000595066029150748, - -0.0010492923645293963, - 0.001366274669719045, - -0.0015558734461504878, - 0.0016326939677845523, - -0.0016145432755263242, - 0.0015209507112593692, - -0.0013718244260180216, - 0.001186299507498468, - -0.000981815162903983, - 0.0007734400525193427, - -0.0005734475676350329, - 0.00039112756777908054, - -0.0002328085860166881, - 0.00010205526341099535, - 8.761724975855263e-19, - -7.423453305983806e-05, - 0.00012306455887295043, - -0.0001499650577703289, - 0.00015901371827411808, - -0.0001544852936160124, - 0.00014051485346619586, - -0.00012084049392574903, - 9.862862369540776e-05, - -7.637845076415545e-05, - 5.589709009118714e-05, - -3.8332995273522084e-05, - 2.4253243796448185e-05, - -1.3749507785319245e-05, - 6.5581470578797384e-06, - -2.181517823792544e-06, - -1.4287804154623814e-19, -]; - -#[allow(clippy::excessive_precision)] -pub const HZ88200_LOW: [f64; 129] = [ - 5.875800344814529e-06, - 4.851699044013074e-06, - -1.5670438235895964e-05, - -9.287225758882044e-06, - 3.2188588517572685e-05, - 1.452725442412174e-05, - -5.80015310113526e-05, - -1.975318175268538e-05, - 9.615523116437566e-05, - 2.3552114164532385e-05, - -0.00015014174552603567, - -2.375916580017386e-05, - 0.00022383889492568076, - 1.729437074025395e-05, - -0.00032141607023776947, - -1.8686789504441635e-19, - 0.00044720587729591056, - -3.351608912018519e-05, - -0.0006055434276587542, - 9.002640046744051e-05, - 0.0008005773235174634, - -0.00017781632159122524, - -0.0010360586296973909, - 0.00030680368023040425, - 0.0013151162221446207, - -0.0004886505249968164, - -0.0016400286962743556, - 0.0007368790790811487, - 0.0020120043638640903, - -0.0010670133701345262, - -0.0024309816366330315, - 0.0014967816965620656, - 0.002895462190517508, - -0.002046435147324143, - -0.0034023886674580135, - 0.002739267989293373, - 0.003947077286631264, - -0.003602474524851333, - -0.0045232136384107564, - 0.0046685585578356776, - 0.005122917204805467, - -0.005977654559924746, - -0.005736876918489128, - 0.007581382917565772, - 0.006354556507022021, - -0.009549372598113655, - -0.006964464667634032, - 0.011980635507338782, - 0.007554481497059819, - -0.015024288868017965, - -0.008112229280669358, - 0.018919636740583255, - 0.008625472935527727, - -0.024080164100849143, - -0.009082533288527662, - 0.031289629556764974, - 0.009472695101363785, - -0.042234282967316725, - -0.009786591428611496, - 0.06131211363458383, - 0.01001654655736988, - -0.10467821599139523, - -0.010156861410646928, - 0.31783087768940993, - 0.5102013912850153, - 0.31783087768940993, - -0.010156861410646928, - -0.10467821599139523, - 0.01001654655736988, - 0.06131211363458383, - -0.009786591428611496, - -0.042234282967316725, - 0.009472695101363785, - 0.031289629556764974, - -0.009082533288527662, - -0.024080164100849143, - 0.008625472935527727, - 0.018919636740583255, - -0.008112229280669358, - -0.015024288868017965, - 0.007554481497059819, - 0.011980635507338782, - -0.006964464667634032, - -0.009549372598113655, - 0.006354556507022021, - 0.007581382917565772, - -0.005736876918489128, - -0.005977654559924746, - 0.005122917204805467, - 0.0046685585578356776, - -0.0045232136384107564, - -0.003602474524851333, - 0.003947077286631264, - 0.002739267989293373, - -0.0034023886674580135, - -0.002046435147324143, - 0.002895462190517508, - 0.0014967816965620656, - -0.0024309816366330315, - -0.0010670133701345262, - 0.0020120043638640903, - 0.0007368790790811487, - -0.0016400286962743556, - -0.0004886505249968164, - 0.0013151162221446207, - 0.00030680368023040425, - -0.0010360586296973909, - -0.00017781632159122524, - 0.0008005773235174634, - 9.002640046744051e-05, - -0.0006055434276587542, - -3.351608912018519e-05, - 0.00044720587729591056, - -1.8686789504441635e-19, - -0.00032141607023776947, - 1.729437074025395e-05, - 0.00022383889492568076, - -2.375916580017386e-05, - -0.00015014174552603567, - 2.3552114164532385e-05, - 9.615523116437566e-05, - -1.975318175268538e-05, - -5.80015310113526e-05, - 1.452725442412174e-05, - 3.2188588517572685e-05, - -9.287225758882044e-06, - -1.5670438235895964e-05, - 4.851699044013074e-06, - 5.875800344814529e-06, -]; - -#[allow(clippy::excessive_precision)] -pub const HZ96000_LOW: [f64; 129] = [ - -7.143944801213435e-20, - -1.1128313185646072e-05, - -3.3433343718178e-06, - 2.368294142133405e-05, - 1.3125839456769717e-05, - -4.065919588349948e-05, - -3.361363034503314e-05, - 6.0198389114399714e-05, - 6.974138571798226e-05, - -7.816273123033217e-05, - -0.000126460783407638, - 8.758503947913682e-05, - 0.0002077626777155509, - -7.835700358335798e-05, - -0.0003154059452699033, - 3.729708416324331e-05, - 0.0004474410769117699, - 5.1274839240568415e-05, - -0.0005966722898292008, - -0.00020436483462002328, - 0.0007492498350107482, - 0.0004384998464839666, - -0.0008836155865344651, - -0.0007673289520005847, - 0.000970032155449486, - 0.0011987515041766601, - -0.0009709031498968383, - -0.0017317724164334631, - 0.0008420376771297366, - 0.0023533499143115976, - -0.0005349278077261196, - -0.00303553840647302, - 1.824936363314565e-17, - 0.003733226210629766, - 0.0008093189947979181, - -0.004382713612447862, - -0.0019320007678197645, - 0.004901261108562161, - 0.0033946608064293073, - -0.00518754915999129, - -0.005207105621787521, - 0.0051227092580119855, - 0.007358664812266444, - -0.004571166160481191, - -0.009815768367302802, - 0.003379862524796346, - 0.012521152261727227, - -0.0013732462347513066, - -0.015394948058705999, - -0.0016610156480375167, - 0.018337745467632928, - 0.006006200853277362, - -0.021235526836812676, - -0.012095713970371423, - 0.02396617954234677, - 0.020705989626446524, - -0.02640711788557473, - -0.03348753234339166, - 0.028443411089665006, - 0.05477521964516673, - -0.029975735264560804, - -0.10063702926794785, - 0.030927455828749166, - 0.3164667874739216, - 0.4687530957411302, - 0.3164667874739216, - 0.030927455828749166, - -0.10063702926794785, - -0.029975735264560804, - 0.05477521964516673, - 0.028443411089665006, - -0.03348753234339166, - -0.02640711788557473, - 0.020705989626446524, - 0.02396617954234677, - -0.012095713970371423, - -0.021235526836812676, - 0.006006200853277362, - 0.018337745467632928, - -0.0016610156480375167, - -0.015394948058705999, - -0.0013732462347513066, - 0.012521152261727227, - 0.003379862524796346, - -0.009815768367302802, - -0.004571166160481191, - 0.007358664812266444, - 0.0051227092580119855, - -0.005207105621787521, - -0.00518754915999129, - 0.0033946608064293073, - 0.004901261108562161, - -0.0019320007678197645, - -0.004382713612447862, - 0.0008093189947979181, - 0.003733226210629766, - 1.824936363314565e-17, - -0.00303553840647302, - -0.0005349278077261196, - 0.0023533499143115976, - 0.0008420376771297366, - -0.0017317724164334631, - -0.0009709031498968383, - 0.0011987515041766601, - 0.000970032155449486, - -0.0007673289520005847, - -0.0008836155865344651, - 0.0004384998464839666, - 0.0007492498350107482, - -0.00020436483462002328, - -0.0005966722898292008, - 5.1274839240568415e-05, - 0.0004474410769117699, - 3.729708416324331e-05, - -0.0003154059452699033, - -7.835700358335798e-05, - 0.0002077626777155509, - 8.758503947913682e-05, - -0.000126460783407638, - -7.816273123033217e-05, - 6.974138571798226e-05, - 6.0198389114399714e-05, - -3.361363034503314e-05, - -4.065919588349948e-05, - 1.3125839456769717e-05, - 2.368294142133405e-05, - -3.3433343718178e-06, - -1.1128313185646072e-05, - -7.143944801213435e-20, +pub const HZ96000_COEFFICIENTS: [f64; 441] = [ + 5.560249704317343e-10, + 6.7457053197573024e-09, + 1.2990035898564e-10, + -1.3822536813261148e-08, + -2.9300911640564656e-09, + 2.399478082083703e-08, + 9.41622844952465e-09, + -3.7481047434174274e-08, + -2.1662912144899056e-08, + 5.404635607782416e-08, + 4.224936834873014e-08, + -7.277372749908042e-08, + -7.420406557781993e-08, + 9.180574659487064e-08, + 1.2087511596227733e-07, + -1.0806955116527926e-07, + -1.8571016370140034e-07, + 1.170047184434655e-07, + 2.7193189671565557e-07, + -1.1231972250589951e-07, + -3.820995948332229e-07, + 8.580858719281314e-08, + 5.175534033235693e-07, + -2.7264542219779906e-08, + -6.777463371709023e-07, + -7.546867930879655e-08, + 8.594792841000288e-07, + 2.362654075465788e-07, + -1.0560662274062185e-06, + -4.702647143542115e-07, + 1.2564701211818615e-06, + 7.931573753480084e-07, + -1.444463705583346e-06, + -1.2201828893460145e-06, + 1.5978832544551455e-06, + 1.7648179657738676e-06, + -1.6880558459176943e-06, + -2.4371537290850187e-06, + 1.6794911507065441e-06, + 3.241978858347619e-06, + -1.5299357680554498e-06, + -4.176609629091859e-06, + 1.1908906002040628e-06, + 5.228534691612452e-06, + -6.086884800383554e-07, + -6.372971415214212e-06, + -2.7378079220115605e-07, + 7.570460432970104e-06, + 1.5146285260962556e-06, + -8.764654009459012e-06, + -3.1697646987478756e-06, + 9.880480130834774e-06, + 5.288925533796424e-06, + -1.0822885686373e-05, + -7.91096385725646e-06, + 1.147637656202352e-05, + 1.1058484433166778e-05, + -1.1705577680692975e-05, + -1.4731961794014652e-05, + 1.1357029900154213e-05, + 1.8903537311698937e-05, + -1.02624213729694e-05, + -2.3510752894245287e-05, + 8.243417040440697e-06, + 2.845053771367662e-05, + -5.118200477547931e-06, + -3.357381830826675e-05, + 7.097771141127904e-07, + 3.868116747816418e-05, + 5.1439924827157876e-06, + -4.3519939664326893e-05, + -1.2578754584543908e-05, + 4.778335604530135e-05, + 2.1690650088703207e-05, + -5.1111997713789026e-05, + -3.25222717208439e-05, + 5.3098136771621236e-05, + 4.504805078593748e-05, + -5.329328046531237e-05, + -5.9159648408755305e-05, + 5.1219220953428783e-05, + 7.465203338939947e-05, + -4.638277246361614e-05, + -9.121102194406923e-05, + 3.829423929087409e-05, + 0.00010840312702721423, + -2.6489494595043767e-05, + -0.00012566860999063735, + 1.0555365102241566e-05, + 0.0001423186387411039, + 9.84218401105256e-06, + -0.00015753742873676115, + -3.492777968059588e-05, + 0.00017039017161439265, + 6.478521332693489e-05, + -0.0001798374378895852, + -9.932598355762823e-05, + 0.00018475657369415775, + 0.00013825912105014366, + -0.00018397039760952743, + -0.00018106376125474611, + 0.0001762832452577541, + 0.00022696604405721683, + -0.00016052411172711115, + -0.0002749219824710103, + 0.0001355963128374769, + 0.0003236079454914325, + -0.0001005327357148728, + -0.0003714203356632149, + 5.4555389294439867e-05, + 0.00041648590394768506, + 2.8623898269992334e-06, + -0.00045668392990309446, + -7.193305595729272e-05, + 0.0004896812037153272, + 0.00015249357603317176, + -0.0005129803811882822, + -0.00024394827954764191, + 0.0005239818498263345, + 0.0003452185393899377, + -0.0005200587535404849, + -0.0004547020805523807, + 0.0004986442886477423, + 0.0005702447116739937, + -0.00045732982127237766, + -0.000689127166061252, + 0.00039397180535415474, + 0.0008080695174294933, + -0.0003068049228110276, + -0.0009232552984091209, + 0.00019455834610855686, + 0.0010303769985912112, + -5.657156236266569e-05, + -0.0011247040600814201, + -0.00010709417927966556, + 0.0012011738336718703, + 0.00029555300653979545, + -0.001254505224223492, + -0.0005070045038801027, + 0.0012793339607298146, + 0.0007386610901853346, + -0.001270367600067128, + -0.0009866968976081866, + 0.0012225575424289717, + 0.0012462220382388606, + -0.0011312845324039525, + -0.0015112856634668026, + 0.000992553375801394, + 0.001774910578349497, + -0.0008031919524797967, + -0.002029161380794138, + 0.0005610490828306219, + 0.0022652471702039635, + -0.00026518544171678135, + -0.0024736588359997344, + -8.394846280610072e-05, + 0.0026443398223918325, + 0.0004843531929226606, + -0.0027668881061983677, + -0.0009323458453637817, + 0.0028307859575470943, + 0.0014224552392333313, + -0.0028256529198113544, + -0.001947351215488067, + 0.002741516387210494, + 0.002497811396668082, + -0.002569093217952415, + -0.003062727446644023, + 0.002300075037489875, + 0.003629151287117882, + -0.0019274092967394656, + -0.0041823798600841, + 0.0014455677851960128, + 0.004706074850303062, + -0.0008507941834454292, + -0.005182411251550035, + 0.00014132239052928864, + 0.0055922456849282496, + 0.0006824422129566309, + -0.00591529179532096, + -0.0016177857065913586, + 0.006130285582602136, + 0.0026595989567600935, + -0.006215117688556044, + -0.0038003463068190125, + 0.006146901643802061, + 0.005030093197407957, + -0.0059019354869432146, + -0.006336594699928613, + 0.00545549657976398, + 0.00770544523750543, + -0.004781381564722846, + -0.00912028798641521, + 0.003851057421363559, + 0.010563080653771224, + -0.0026322107431178856, + -0.012014412573237022, + 0.001086341830368365, + 0.013453866409008227, + 0.0008352110916833665, + -0.014860416266574179, + -0.0031979417347285668, + 0.01621285273010614, + 0.006095714026154995, + -0.017490224327851422, + -0.009671655092655702, + 0.01867228420781883, + 0.014158404619215172, + -0.019739930415957174, + -0.0199622658168629, + 0.020675628126808922, + 0.027859437853963814, + -0.021463802489478597, + -0.03952835273006745, + 0.022091191414822978, + 0.05935902511250348, + -0.02254714862612816, + -0.10349853375753784, + 0.022823888596670217, + 0.31743777574504695, + 0.47708333120157576, + 0.31743777574504695, + 0.022823888596670217, + -0.10349853375753784, + -0.02254714862612816, + 0.05935902511250348, + 0.022091191414822978, + -0.03952835273006745, + -0.021463802489478597, + 0.027859437853963814, + 0.020675628126808922, + -0.0199622658168629, + -0.019739930415957174, + 0.014158404619215172, + 0.01867228420781883, + -0.009671655092655702, + -0.017490224327851422, + 0.006095714026154995, + 0.01621285273010614, + -0.0031979417347285668, + -0.014860416266574179, + 0.0008352110916833665, + 0.013453866409008227, + 0.001086341830368365, + -0.012014412573237022, + -0.0026322107431178856, + 0.010563080653771224, + 0.003851057421363559, + -0.00912028798641521, + -0.004781381564722846, + 0.00770544523750543, + 0.00545549657976398, + -0.006336594699928613, + -0.0059019354869432146, + 0.005030093197407957, + 0.006146901643802061, + -0.0038003463068190125, + -0.006215117688556044, + 0.0026595989567600935, + 0.006130285582602136, + -0.0016177857065913586, + -0.00591529179532096, + 0.0006824422129566309, + 0.0055922456849282496, + 0.00014132239052928864, + -0.005182411251550035, + -0.0008507941834454292, + 0.004706074850303062, + 0.0014455677851960128, + -0.0041823798600841, + -0.0019274092967394656, + 0.003629151287117882, + 0.002300075037489875, + -0.003062727446644023, + -0.002569093217952415, + 0.002497811396668082, + 0.002741516387210494, + -0.001947351215488067, + -0.0028256529198113544, + 0.0014224552392333313, + 0.0028307859575470943, + -0.0009323458453637817, + -0.0027668881061983677, + 0.0004843531929226606, + 0.0026443398223918325, + -8.394846280610072e-05, + -0.0024736588359997344, + -0.00026518544171678135, + 0.0022652471702039635, + 0.0005610490828306219, + -0.002029161380794138, + -0.0008031919524797967, + 0.001774910578349497, + 0.000992553375801394, + -0.0015112856634668026, + -0.0011312845324039525, + 0.0012462220382388606, + 0.0012225575424289717, + -0.0009866968976081866, + -0.001270367600067128, + 0.0007386610901853346, + 0.0012793339607298146, + -0.0005070045038801027, + -0.001254505224223492, + 0.00029555300653979545, + 0.0012011738336718703, + -0.00010709417927966556, + -0.0011247040600814201, + -5.657156236266569e-05, + 0.0010303769985912112, + 0.00019455834610855686, + -0.0009232552984091209, + -0.0003068049228110276, + 0.0008080695174294933, + 0.00039397180535415474, + -0.000689127166061252, + -0.00045732982127237766, + 0.0005702447116739937, + 0.0004986442886477423, + -0.0004547020805523807, + -0.0005200587535404849, + 0.0003452185393899377, + 0.0005239818498263345, + -0.00024394827954764191, + -0.0005129803811882822, + 0.00015249357603317176, + 0.0004896812037153272, + -7.193305595729272e-05, + -0.00045668392990309446, + 2.8623898269992334e-06, + 0.00041648590394768506, + 5.4555389294439867e-05, + -0.0003714203356632149, + -0.0001005327357148728, + 0.0003236079454914325, + 0.0001355963128374769, + -0.0002749219824710103, + -0.00016052411172711115, + 0.00022696604405721683, + 0.0001762832452577541, + -0.00018106376125474611, + -0.00018397039760952743, + 0.00013825912105014366, + 0.00018475657369415775, + -9.932598355762823e-05, + -0.0001798374378895852, + 6.478521332693489e-05, + 0.00017039017161439265, + -3.492777968059588e-05, + -0.00015753742873676115, + 9.84218401105256e-06, + 0.0001423186387411039, + 1.0555365102241566e-05, + -0.00012566860999063735, + -2.6489494595043767e-05, + 0.00010840312702721423, + 3.829423929087409e-05, + -9.121102194406923e-05, + -4.638277246361614e-05, + 7.465203338939947e-05, + 5.1219220953428783e-05, + -5.9159648408755305e-05, + -5.329328046531237e-05, + 4.504805078593748e-05, + 5.3098136771621236e-05, + -3.25222717208439e-05, + -5.1111997713789026e-05, + 2.1690650088703207e-05, + 4.778335604530135e-05, + -1.2578754584543908e-05, + -4.3519939664326893e-05, + 5.1439924827157876e-06, + 3.868116747816418e-05, + 7.097771141127904e-07, + -3.357381830826675e-05, + -5.118200477547931e-06, + 2.845053771367662e-05, + 8.243417040440697e-06, + -2.3510752894245287e-05, + -1.02624213729694e-05, + 1.8903537311698937e-05, + 1.1357029900154213e-05, + -1.4731961794014652e-05, + -1.1705577680692975e-05, + 1.1058484433166778e-05, + 1.147637656202352e-05, + -7.91096385725646e-06, + -1.0822885686373e-05, + 5.288925533796424e-06, + 9.880480130834774e-06, + -3.1697646987478756e-06, + -8.764654009459012e-06, + 1.5146285260962556e-06, + 7.570460432970104e-06, + -2.7378079220115605e-07, + -6.372971415214212e-06, + -6.086884800383554e-07, + 5.228534691612452e-06, + 1.1908906002040628e-06, + -4.176609629091859e-06, + -1.5299357680554498e-06, + 3.241978858347619e-06, + 1.6794911507065441e-06, + -2.4371537290850187e-06, + -1.6880558459176943e-06, + 1.7648179657738676e-06, + 1.5978832544551455e-06, + -1.2201828893460145e-06, + -1.444463705583346e-06, + 7.931573753480084e-07, + 1.2564701211818615e-06, + -4.702647143542115e-07, + -1.0560662274062185e-06, + 2.362654075465788e-07, + 8.594792841000288e-07, + -7.546867930879655e-08, + -6.777463371709023e-07, + -2.7264542219779906e-08, + 5.175534033235693e-07, + 8.580858719281314e-08, + -3.820995948332229e-07, + -1.1231972250589951e-07, + 2.7193189671565557e-07, + 1.170047184434655e-07, + -1.8571016370140034e-07, + -1.0806955116527926e-07, + 1.2087511596227733e-07, + 9.180574659487064e-08, + -7.420406557781993e-08, + -7.277372749908042e-08, + 4.224936834873014e-08, + 5.404635607782416e-08, + -2.1662912144899056e-08, + -3.7481047434174274e-08, + 9.41622844952465e-09, + 2.399478082083703e-08, + -2.9300911640564656e-09, + -1.3822536813261148e-08, + 1.2990035898564e-10, + 6.7457053197573024e-09, + 5.560249704317343e-10, ]; diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 25c3a749a..fe82d4845 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -7,9 +7,8 @@ use std::{ }; use crate::{ - config::{InterpolationQuality, SampleRate}, - player::PLAYER_COUNTER, - RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE, + config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE, + SAMPLE_RATE as SOURCE_SAMPLE_RATE, }; struct DelayLine { @@ -88,27 +87,27 @@ struct MonoSincResampler { } impl MonoSincResampler { - fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self { - let spec = sample_rate.get_resample_spec(); + fn new(sample_rate: SampleRate) -> Self { + let coefficients = sample_rate + .get_interpolation_coefficients() + .unwrap_or_default(); - let coefficients = match interpolation_quality { - InterpolationQuality::Low => spec.low_coefficients, - InterpolationQuality::High => spec.high_coefficients, - }; + let resample_factor_reciprocal = sample_rate + .get_resample_factor_reciprocal() + .unwrap_or_default(); - let delay_line_latency = - (coefficients.len() as f64 * spec.resample_factor_reciprocal) as u64; + let interpolation_output_size = sample_rate + .get_interpolation_output_size() + .unwrap_or_default(); - Self { - interpolator: ConvolutionFilter::new( - interpolation_quality - .get_interpolation_coefficients(coefficients, spec.resample_factor_reciprocal), - ), + let delay_line_latency = (coefficients.len() as f64 * resample_factor_reciprocal) as u64; + Self { + interpolator: ConvolutionFilter::new(coefficients), input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize), - resample_factor_reciprocal: spec.resample_factor_reciprocal, + resample_factor_reciprocal, delay_line_latency, - interpolation_output_size: spec.interpolation_output_size, + interpolation_output_size, } } @@ -281,7 +280,7 @@ pub struct StereoInterleavedResampler { } impl StereoInterleavedResampler { - pub fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self { + pub fn new(sample_rate: SampleRate) -> Self { debug!("Sample Rate: {sample_rate}"); let resampler = match sample_rate { @@ -292,7 +291,7 @@ impl StereoInterleavedResampler { Resampler::Bypass } _ => { - debug!("Interpolation Quality: {interpolation_quality}"); + 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); @@ -300,12 +299,15 @@ impl StereoInterleavedResampler { let left_thread_name = format!("resampler:{player_id}:left"); let right_thread_name = format!("resampler:{player_id}:right"); - 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), + left_resampler: ResampleWorker::new( + MonoSincResampler::new(sample_rate), + left_thread_name, + ), + right_resampler: ResampleWorker::new( + MonoSincResampler::new(sample_rate), + right_thread_name, + ), } } }; diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index bae88279d..53bbc29f3 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -23,8 +23,7 @@ impl SamplePipeline { sink: Box, volume_getter: Box, ) -> Self { - let resampler = - StereoInterleavedResampler::new(config.sample_rate, config.interpolation_quality); + let resampler = StereoInterleavedResampler::new(config.sample_rate); let normaliser = Normaliser::new(config, volume_getter); let converter = Converter::new(config.ditherer); diff --git a/src/main.rs b/src/main.rs index 231b24518..f959267a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,8 +24,8 @@ use librespot::{ playback::{ audio_backend::{self, SinkBuilder, BACKENDS}, config::{ - AudioFormat, Bitrate, InterpolationQuality, NormalisationMethod, NormalisationType, - PlayerConfig, SampleRate, VolumeCtrl, + AudioFormat, Bitrate, NormalisationMethod, NormalisationType, PlayerConfig, SampleRate, + VolumeCtrl, }, dither, mixer::{self, MixerConfig, MixerFn}, @@ -240,7 +240,6 @@ fn get_setup() -> Setup { const VOLUME_RANGE: &str = "volume-range"; const ZEROCONF_PORT: &str = "zeroconf-port"; const ZEROCONF_INTERFACE: &str = "zeroconf-interface"; - const INTERPOLATION_QUALITY: &str = "interpolation-quality"; const SAMPLE_RATE: &str = "sample-rate"; // Mostly arbitrary. @@ -579,11 +578,6 @@ fn get_setup() -> Setup { ZEROCONF_INTERFACE, "Comma-separated interface IP addresses on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD.", "IP" - ).optopt( - "", - INTERPOLATION_QUALITY, - "Interpolation Quality to use if Resampling {Low|High}. Defaults to High.", - "QUALITY" ).optopt( "", SAMPLE_RATE, @@ -800,32 +794,6 @@ fn get_setup() -> Setup { }) .unwrap_or_default(); - let interpolation_quality = opt_str(INTERPOLATION_QUALITY) - .as_deref() - .map(|interpolation_quality| match sample_rate { - SampleRate::Hz44100 => { - warn!( - "--{} has no effect with a sample rate of {sample_rate}.", - INTERPOLATION_QUALITY - ); - - InterpolationQuality::default() - } - _ => InterpolationQuality::from_str(interpolation_quality).unwrap_or_else(|_| { - let default_value = &format!("{}", InterpolationQuality::default()); - invalid_error_msg( - INTERPOLATION_QUALITY, - "", - interpolation_quality, - "Low, High", - default_value, - ); - - exit(1); - }), - }) - .unwrap_or_default(); - let format = opt_str(FORMAT) .as_deref() .map(|format| { @@ -1671,7 +1639,6 @@ fn get_setup() -> Setup { bitrate, gapless, passthrough, - interpolation_quality, sample_rate, normalisation, normalisation_type, From a331729f0ebd1e125b54f1848a7a2761d0d871fc Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Thu, 6 Jul 2023 17:58:42 -0500 Subject: [PATCH 51/53] small clean up --- playback/src/config.rs | 15 +++++++++------ playback/src/resampler.rs | 7 ++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index e2fafda29..6f9eb2564 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -180,17 +180,20 @@ impl SampleRate { mut coefficients: Vec, resample_factor_reciprocal: f64, ) -> Vec { - let mut coefficient_sum = 0.0; + let mut coefficients_sum = 0.0; - for (index, coefficient) in coefficients.iter_mut().enumerate() { - *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract()); + coefficients + .iter_mut() + .enumerate() + .for_each(|(index, coefficient)| { + *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract()); - coefficient_sum += *coefficient; - } + coefficients_sum += *coefficient; + }); coefficients .iter_mut() - .for_each(|coefficient| *coefficient /= coefficient_sum); + .for_each(|coefficient| *coefficient /= coefficients_sum); coefficients } diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index fe82d4845..7642e44fe 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -296,17 +296,14 @@ impl StereoInterleavedResampler { // The player increments the player id when it gets it... let player_id = PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1); - let left_thread_name = format!("resampler:{player_id}:left"); - let right_thread_name = format!("resampler:{player_id}:right"); - Resampler::Worker { left_resampler: ResampleWorker::new( MonoSincResampler::new(sample_rate), - left_thread_name, + format!("resampler:{player_id}:left"), ), right_resampler: ResampleWorker::new( MonoSincResampler::new(sample_rate), - right_thread_name, + format!("resampler:{player_id}:right"), ), } } From 8aaab0a210e616a0a2ff7b6f40cf7f3510886dd1 Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Sun, 3 Sep 2023 17:32:55 -0500 Subject: [PATCH 52/53] More touch ups --- playback/src/config.rs | 29 +- playback/src/filter_coefficients.rs | 2510 +++++++++++++-------------- playback/src/normaliser.rs | 61 +- playback/src/resampler.rs | 230 ++- playback/src/sample_pipeline.rs | 130 +- 5 files changed, 1497 insertions(+), 1463 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 6f9eb2564..20f5a1f5c 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -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 { + 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 { use SampleRate::*; diff --git a/playback/src/filter_coefficients.rs b/playback/src/filter_coefficients.rs index dee8a64bf..1f89764b7 100644 --- a/playback/src/filter_coefficients.rs +++ b/playback/src/filter_coefficients.rs @@ -1,1344 +1,1188 @@ -// All Windows calculated with pyfda (Python Filter Design Analysis Tool) +// All coefficients calculated with pyfda (Python Filter Design Analysis Tool) // https://github.com/chipmuenk/pyfda // Window = Kaiser -// beta = 15 -// fc = 22.8kHz -// -140dB well before 24kHz -// The assumption is that the input -// is anti-aliased properly, -// We just want to make sure interpolation -// doesn't add any artifacts. +// Ripple = 0.00017371779276643043 + +// β = 20.8 +// fc = 20.66 +// -195dB before 22kHz #[allow(clippy::excessive_precision)] -pub const HZ48000_COEFFICIENTS: [f64; 441] = [ - -8.771146084517915e-23, - 1.0570942208391173e-09, - -3.0666660745656922e-09, - 6.298083919549404e-09, - -1.1009494492149948e-08, - 1.7421987235394415e-08, - -2.5689109092988082e-08, - 3.5862674329115807e-08, - -4.7856266645529607e-08, - 6.140828902806317e-08, - -7.604685422793451e-08, - 9.105919616127095e-08, - -1.0546858823253603e-07, - 1.1802194634247329e-07, - -1.271913407105633e-07, - 1.3119251294318607e-07, - -1.280231690229821e-07, - 1.1552327779416195e-07, - -9.145883941617693e-08, - 5.362960091161619e-08, - -7.7217226107300735e-22, - -7.114875946245203e-08, - 1.6104911667095417e-07, - -2.7026527132156615e-07, - 3.985058614410556e-07, - -5.444396250307578e-07, - 7.055238955116609e-07, - -8.778569687571143e-07, - 1.0560662317828128e-06, - -1.2332443646663861e-06, - 1.4009458289943576e-06, - -1.5492551604686838e-06, - 1.6669372292816672e-06, - -1.7416775803496403e-06, - 1.760418204028636e-06, - -1.7097906350117925e-06, - 1.5766441855366919e-06, - -1.3486624782617763e-06, - 1.015056381074019e-06, - -5.673161244592637e-07, - 9.792562157691215e-20, - 6.884681756425475e-07, - -1.4950254408593126e-06, - 2.4112566283065856e-06, - -3.4227817199744546e-06, - 4.5087853828418386e-06, - -5.641730870612593e-06, - 6.787299186897442e-06, - -7.904591223857538e-06, - 8.946625393428547e-06, - -9.861155999917786e-06, - 1.059182828621353e-05, - -1.1079674832069601e-05, - 1.126494499675449e-05, - -1.1089244681251284e-05, - 1.0497948234446287e-05, - -9.442828331471874e-06, - 7.884833680573385e-06, - -5.796929105544363e-06, - 3.1668985925331214e-06, - -1.0267057094301908e-18, - -3.678648971030143e-06, - 7.82207956681534e-06, - -1.2360003045077198e-05, - 1.719798834414252e-05, - -2.221740239139365e-05, - 2.727621538905652e-05, - -3.221075420642931e-05, - 3.6838462372847274e-05, - -4.096169540744633e-05, - 4.4372545871397646e-05, - -4.6858654348383316e-05, - 4.8209921539750954e-05, - -4.8225994005681544e-05, - 4.6724353186234967e-05, - -4.354879574733261e-05, - 3.857805468230202e-05, - -3.1734276690489895e-05, - 2.2991043887561944e-05, - -1.2380608558550801e-05, - 1.0714892504045419e-18, - 1.3984336126251783e-05, - -2.933369198803881e-05, - 4.5736344959148374e-05, - -6.280915808675244e-05, - 8.010174059784683e-05, - -9.710330972590319e-05, - 0.00011325231780323752, - -0.00012794882111289484, - 0.00014056947148065392, - -0.00015048491054933942, - 0.00015707924308034133, - -0.0001597711628045901, - 0.00015803620591136328, - -0.0001514295170121378, - 0.00013960843420358319, - -0.0001223541374673828, - 9.95915616676161e-05, - -7.140675507600406e-05, - 3.806086941676025e-05, - 5.522292755457904e-18, - -4.213984402529341e-05, - 8.753326840947647e-05, - -0.00013517203082831053, - 0.0001838779481747696, - -0.00023232236376993098, - 0.00027905217036022, - -0.00032252217407439016, - 0.0003611333624045614, - -0.0003932764120704118, - 0.00041737954732315945, - -0.0004319596433444126, - 0.00043567527068183264, - -0.00042738020297625895, - 0.00040617576920780796, - -0.00037146033048375895, - 0.00032297410642885815, - -0.0002608375729289383, - 0.00018558170547350593, - -9.816845330568487e-05, - 6.397895727529846e-19, - 0.00010708440490237048, - -0.00022082497375172826, - 0.00033857186283733177, - -0.00045732982316766367, - 0.0005738168187053221, - -0.000684535969656674, - 0.0007858597238898489, - -0.0008741247047765545, - 0.000945735242783811, - -0.0009972731855902538, - 0.0010256112120681994, - -0.0010280265635201971, - 0.0010023118646185563, - -0.000946879548805299, - 0.0008608563389349595, - -0.0007441642719534753, - 0.0005975849020744005, - -0.00042280357297843214, - 0.00022243101557540356, - -3.474391490098325e-17, - -0.00024006465851531805, - 0.0004925037811490474, - -0.0007513095024493709, - 0.0010098384606174067, - -0.0012609511916318978, - 0.001497175575304658, - -0.0017108913477184788, - 0.00189453189460518, - -0.002040798799462615, - 0.0021428839636620438, - -0.002194693567447552, - 0.002191067722720476, - -0.0021279894003546954, - 0.0020027761127039494, - -0.001814247908162659, - 0.0015628654968098813, - -0.0012508327770007735, - 0.0008821586696520896, - -0.00046267398178344456, - -3.3039584853563575e-17, - 0.0004965333609844947, - -0.0010160207840665381, - 0.0015461657408802655, - -0.0020735138415665724, - 0.002583732114598345, - -0.0030619295805850155, - 0.0034930131304211584, - -0.0038620714583133555, - 0.004154778673084419, - -0.004357808253637993, - 0.004459247260332142, - -0.004449000192806269, - 0.004319171620808093, - -0.004064416725949453, - 0.003682249190367868, - -0.00317329645683304, - 0.0025414932599423837, - -0.0017942054777252078, - 0.0009422777573359167, - -7.350185540376589e-17, - -0.0010150103856962798, - 0.002082011577823492, - -0.0031774248180713193, - 0.00427520317347509, - -0.005347276351155804, - 0.006364064256720479, - -0.0072950500213230345, - 0.008109401423325797, - -0.008776628049842111, - 0.00926726022866957, - -0.009553534753966662, - 0.009610071764202605, - -0.009414526835002754, - 0.008948202440030514, - -0.008196603417779811, - 0.007149921958698993, - -0.005803438882712496, - 0.00415782958901113, - -0.0022193649952320592, - 8.812638424710787e-17, - 0.00248265554875482, - -0.005205485774200194, - 0.008140137239069204, - -0.011253403840824383, - 0.014507722254727249, - -0.017861769381184747, - 0.02127115074947851, - -0.024689166551051196, - 0.02806763998183295, - -0.031357790917889976, - 0.03451113667723182, - -0.0374804007687864, - 0.04022041012286698, - -0.04268896135026697, - 0.04484763709216705, - -0.04666255449128448, - 0.048105029215477144, - -0.04915214026651778, - 0.049787182966797176, - 0.9499999996921324, - 0.049787182966797176, - -0.04915214026651778, - 0.048105029215477144, - -0.04666255449128448, - 0.04484763709216705, - -0.04268896135026697, - 0.04022041012286698, - -0.0374804007687864, - 0.03451113667723182, - -0.031357790917889976, - 0.02806763998183295, - -0.024689166551051196, - 0.02127115074947851, - -0.017861769381184747, - 0.014507722254727249, - -0.011253403840824383, - 0.008140137239069204, - -0.005205485774200194, - 0.00248265554875482, - 8.812638424710787e-17, - -0.0022193649952320592, - 0.00415782958901113, - -0.005803438882712496, - 0.007149921958698993, - -0.008196603417779811, - 0.008948202440030514, - -0.009414526835002754, - 0.009610071764202605, - -0.009553534753966662, - 0.00926726022866957, - -0.008776628049842111, - 0.008109401423325797, - -0.0072950500213230345, - 0.006364064256720479, - -0.005347276351155804, - 0.00427520317347509, - -0.0031774248180713193, - 0.002082011577823492, - -0.0010150103856962798, - -7.350185540376589e-17, - 0.0009422777573359167, - -0.0017942054777252078, - 0.0025414932599423837, - -0.00317329645683304, - 0.003682249190367868, - -0.004064416725949453, - 0.004319171620808093, - -0.004449000192806269, - 0.004459247260332142, - -0.004357808253637993, - 0.004154778673084419, - -0.0038620714583133555, - 0.0034930131304211584, - -0.0030619295805850155, - 0.002583732114598345, - -0.0020735138415665724, - 0.0015461657408802655, - -0.0010160207840665381, - 0.0004965333609844947, - -3.3039584853563575e-17, - -0.00046267398178344456, - 0.0008821586696520896, - -0.0012508327770007735, - 0.0015628654968098813, - -0.001814247908162659, - 0.0020027761127039494, - -0.0021279894003546954, - 0.002191067722720476, - -0.002194693567447552, - 0.0021428839636620438, - -0.002040798799462615, - 0.00189453189460518, - -0.0017108913477184788, - 0.001497175575304658, - -0.0012609511916318978, - 0.0010098384606174067, - -0.0007513095024493709, - 0.0004925037811490474, - -0.00024006465851531805, - -3.474391490098325e-17, - 0.00022243101557540356, - -0.00042280357297843214, - 0.0005975849020744005, - -0.0007441642719534753, - 0.0008608563389349595, - -0.000946879548805299, - 0.0010023118646185563, - -0.0010280265635201971, - 0.0010256112120681994, - -0.0009972731855902538, - 0.000945735242783811, - -0.0008741247047765545, - 0.0007858597238898489, - -0.000684535969656674, - 0.0005738168187053221, - -0.00045732982316766367, - 0.00033857186283733177, - -0.00022082497375172826, - 0.00010708440490237048, - 6.397895727529846e-19, - -9.816845330568487e-05, - 0.00018558170547350593, - -0.0002608375729289383, - 0.00032297410642885815, - -0.00037146033048375895, - 0.00040617576920780796, - -0.00042738020297625895, - 0.00043567527068183264, - -0.0004319596433444126, - 0.00041737954732315945, - -0.0003932764120704118, - 0.0003611333624045614, - -0.00032252217407439016, - 0.00027905217036022, - -0.00023232236376993098, - 0.0001838779481747696, - -0.00013517203082831053, - 8.753326840947647e-05, - -4.213984402529341e-05, - 5.522292755457904e-18, - 3.806086941676025e-05, - -7.140675507600406e-05, - 9.95915616676161e-05, - -0.0001223541374673828, - 0.00013960843420358319, - -0.0001514295170121378, - 0.00015803620591136328, - -0.0001597711628045901, - 0.00015707924308034133, - -0.00015048491054933942, - 0.00014056947148065392, - -0.00012794882111289484, - 0.00011325231780323752, - -9.710330972590319e-05, - 8.010174059784683e-05, - -6.280915808675244e-05, - 4.5736344959148374e-05, - -2.933369198803881e-05, - 1.3984336126251783e-05, - 1.0714892504045419e-18, - -1.2380608558550801e-05, - 2.2991043887561944e-05, - -3.1734276690489895e-05, - 3.857805468230202e-05, - -4.354879574733261e-05, - 4.6724353186234967e-05, - -4.8225994005681544e-05, - 4.8209921539750954e-05, - -4.6858654348383316e-05, - 4.4372545871397646e-05, - -4.096169540744633e-05, - 3.6838462372847274e-05, - -3.221075420642931e-05, - 2.727621538905652e-05, - -2.221740239139365e-05, - 1.719798834414252e-05, - -1.2360003045077198e-05, - 7.82207956681534e-06, - -3.678648971030143e-06, - -1.0267057094301908e-18, - 3.1668985925331214e-06, - -5.796929105544363e-06, - 7.884833680573385e-06, - -9.442828331471874e-06, - 1.0497948234446287e-05, - -1.1089244681251284e-05, - 1.126494499675449e-05, - -1.1079674832069601e-05, - 1.059182828621353e-05, - -9.861155999917786e-06, - 8.946625393428547e-06, - -7.904591223857538e-06, - 6.787299186897442e-06, - -5.641730870612593e-06, - 4.5087853828418386e-06, - -3.4227817199744546e-06, - 2.4112566283065856e-06, - -1.4950254408593126e-06, - 6.884681756425475e-07, - 9.792562157691215e-20, - -5.673161244592637e-07, - 1.015056381074019e-06, - -1.3486624782617763e-06, - 1.5766441855366919e-06, - -1.7097906350117925e-06, - 1.760418204028636e-06, - -1.7416775803496403e-06, - 1.6669372292816672e-06, - -1.5492551604686838e-06, - 1.4009458289943576e-06, - -1.2332443646663861e-06, - 1.0560662317828128e-06, - -8.778569687571143e-07, - 7.055238955116609e-07, - -5.444396250307578e-07, - 3.985058614410556e-07, - -2.7026527132156615e-07, - 1.6104911667095417e-07, - -7.114875946245203e-08, - -7.7217226107300735e-22, - 5.362960091161619e-08, - -9.145883941617693e-08, - 1.1552327779416195e-07, - -1.280231690229821e-07, - 1.3119251294318607e-07, - -1.271913407105633e-07, - 1.1802194634247329e-07, - -1.0546858823253603e-07, - 9.105919616127095e-08, - -7.604685422793451e-08, - 6.140828902806317e-08, - -4.7856266645529607e-08, - 3.5862674329115807e-08, - -2.5689109092988082e-08, - 1.7421987235394415e-08, - -1.1009494492149948e-08, - 6.298083919549404e-09, - -3.0666660745656922e-09, - 1.0570942208391173e-09, - -8.771146084517915e-23, +pub const HZ48000_COEFFICIENTS: [f64; 241] = [ + -2.258118962333164e-11, + 1.0448101000984154e-10, + -2.579276986417335e-10, + 4.3213325781961056e-10, + -4.5843163381727067e-10, + 2.430239393368342e-11, + 1.2790201415889041e-09, + -3.792077258698514e-09, + 7.493499387692452e-09, + -1.1626845054841438e-08, + 1.4380896605407448e-08, + -1.2833471192711628e-08, + 3.3983200532147757e-09, + 1.7048569784995365e-08, + -4.9373422503539266e-08, + 9.003364640559896e-08, + -1.2924579989482033e-07, + 1.5044668040182147e-07, + -1.3210217109716104e-07, + 5.2624736306518187e-08, + 1.0159667858088225e-07, + -3.2622696353370664e-07, + 5.896182325418432e-07, + -8.277448145932576e-07, + 9.475531903328232e-07, + -8.420069483888789e-07, + 4.1772688158879863e-07, + 3.676938974884631e-07, + -1.4662320604642053e-06, + 2.70957271705655e-06, + -3.802817981364587e-06, + 4.354432548140361e-06, + -3.949196046553083e-06, + 2.260451641101642e-06, + 8.156542880169858e-07, + -5.034945638176245e-06, + 9.729638811991692e-06, + -1.3824155754916521e-05, + 1.5973180290174572e-05, + -1.4828839170824971e-05, + 9.409279173382038e-06, + 4.97915044540138e-07, + -1.3997250778589253e-05, + 2.8927821383294266e-05, + -4.199180492418264e-05, + 4.92301628886413e-05, + -4.683190451509506e-05, + 3.216863473381749e-05, + -4.851687701631952e-06, + -3.245913549120116e-05, + 7.378212902653717e-05, + -0.00011030659888783692, + 0.0001317501695922293, + -0.00012848221714262343, + 9.409014122219963e-05, + -2.7862808366416616e-05, + -6.345544705962209e-05, + 0.00016527387065770158, + -0.00025658320129024414, + 0.00031331992887743433, + -0.0003133321268094909, + 0.0002421329206487914, + -9.823590003163207e-05, + -0.0001033189088939493, + 0.0003305257920305843, + -0.000537884656631541, + 0.0006738503239522973, + -0.0006913872260666551, + 0.0005598234351094426, + -0.00027549921950280733, + -0.0001315408318317103, + 0.0005972048317626658, + -0.0010307540796842497, + 0.0013298141762067902, + -0.0014010675688317755, + 0.001183005538332482, + -0.0006659459018028209, + -9.575731141327346e-05, + 0.0009835467015551942, + -0.001828807023851628, + 0.0024407594224301493, + -0.002644336904102565, + 0.002321370370940948, + -0.0014465226885409, + 0.00010912299971391861, + 0.0014863005385165546, + -0.0030444757114376, + 0.004227621393143003, + -0.004720858410663518, + 0.00430340749485201, + -0.0029111568371450493, + 0.0006758266942872845, + 0.002071261954870286, + -0.004837052438450331, + 0.007044582477270947, + -0.008140448727362718, + 0.0077138932032073445, + -0.005604540990581585, + 0.0019749656455550968, + 0.0026718056262489383, + -0.007541245521216586, + 0.011662297443732754, + -0.014054752766974423, + 0.0139234284401599, + -0.010845235097865776, + 0.0049126327506936645, + 0.0031988445349823255, + -0.01226053329472526, + 0.020623711066876563, + -0.026443075826064425, + 0.027960344694893176, + -0.023804280659778104, + 0.01325702377313349, + 0.0035612135716636354, + -0.025626116624367066, + 0.05108081411605677, + -0.07744152221841791, + 0.10191100680419664, + -0.12175444572282804, + 0.1346803352883561, + 0.8608333333333897, + 0.1346803352883561, + -0.12175444572282804, + 0.10191100680419664, + -0.07744152221841791, + 0.05108081411605677, + -0.025626116624367066, + 0.0035612135716636354, + 0.01325702377313349, + -0.023804280659778104, + 0.027960344694893176, + -0.026443075826064425, + 0.020623711066876563, + -0.01226053329472526, + 0.0031988445349823255, + 0.0049126327506936645, + -0.010845235097865776, + 0.0139234284401599, + -0.014054752766974423, + 0.011662297443732754, + -0.007541245521216586, + 0.0026718056262489383, + 0.0019749656455550968, + -0.005604540990581585, + 0.0077138932032073445, + -0.008140448727362718, + 0.007044582477270947, + -0.004837052438450331, + 0.002071261954870286, + 0.0006758266942872845, + -0.0029111568371450493, + 0.00430340749485201, + -0.004720858410663518, + 0.004227621393143003, + -0.0030444757114376, + 0.0014863005385165546, + 0.00010912299971391861, + -0.0014465226885409, + 0.002321370370940948, + -0.002644336904102565, + 0.0024407594224301493, + -0.001828807023851628, + 0.0009835467015551942, + -9.575731141327346e-05, + -0.0006659459018028209, + 0.001183005538332482, + -0.0014010675688317755, + 0.0013298141762067902, + -0.0010307540796842497, + 0.0005972048317626658, + -0.0001315408318317103, + -0.00027549921950280733, + 0.0005598234351094426, + -0.0006913872260666551, + 0.0006738503239522973, + -0.000537884656631541, + 0.0003305257920305843, + -0.0001033189088939493, + -9.823590003163207e-05, + 0.0002421329206487914, + -0.0003133321268094909, + 0.00031331992887743433, + -0.00025658320129024414, + 0.00016527387065770158, + -6.345544705962209e-05, + -2.7862808366416616e-05, + 9.409014122219963e-05, + -0.00012848221714262343, + 0.0001317501695922293, + -0.00011030659888783692, + 7.378212902653717e-05, + -3.245913549120116e-05, + -4.851687701631952e-06, + 3.216863473381749e-05, + -4.683190451509506e-05, + 4.92301628886413e-05, + -4.199180492418264e-05, + 2.8927821383294266e-05, + -1.3997250778589253e-05, + 4.97915044540138e-07, + 9.409279173382038e-06, + -1.4828839170824971e-05, + 1.5973180290174572e-05, + -1.3824155754916521e-05, + 9.729638811991692e-06, + -5.034945638176245e-06, + 8.156542880169858e-07, + 2.260451641101642e-06, + -3.949196046553083e-06, + 4.354432548140361e-06, + -3.802817981364587e-06, + 2.70957271705655e-06, + -1.4662320604642053e-06, + 3.676938974884631e-07, + 4.1772688158879863e-07, + -8.420069483888789e-07, + 9.475531903328232e-07, + -8.277448145932576e-07, + 5.896182325418432e-07, + -3.2622696353370664e-07, + 1.0159667858088225e-07, + 5.2624736306518187e-08, + -1.3210217109716104e-07, + 1.5044668040182147e-07, + -1.2924579989482033e-07, + 9.003364640559896e-08, + -4.9373422503539266e-08, + 1.7048569784995365e-08, + 3.3983200532147757e-09, + -1.2833471192711628e-08, + 1.4380896605407448e-08, + -1.1626845054841438e-08, + 7.493499387692452e-09, + -3.792077258698514e-09, + 1.2790201415889041e-09, + 2.430239393368342e-11, + -4.5843163381727067e-10, + 4.3213325781961056e-10, + -2.579276986417335e-10, + 1.0448101000984154e-10, + -2.258118962333164e-11, ]; +// β = 20.6 +// fc = 20.6 +// -195dB before 22kHz #[allow(clippy::excessive_precision)] pub const HZ88200_COEFFICIENTS: [f64; 441] = [ - -3.0915687919410175e-09, - -4.380355015683147e-09, - 7.889301672202866e-09, - 7.814782506112341e-09, - -1.601722660647997e-08, - -1.162897576000204e-08, - 2.875448571604994e-08, - 1.510435454511938e-08, - -4.758343509079679e-08, - -1.7052240013897604e-08, - 7.414020115596361e-08, - 1.5685867044913693e-08, - -1.1013103295646797e-07, - -8.486685443549004e-09, - 1.5720816822680103e-07, - -7.927830161980731e-09, - -2.1679956837762108e-07, - 3.792557598966923e-08, - 2.898880068451338e-07, - -8.695906085997518e-08, - -3.767366685630587e-07, - 1.6162916242945288e-07, - 4.765606699845109e-07, - -2.6969832218324916e-07, - -5.871467509278004e-07, - 4.200367275533767e-07, - 7.044268061921138e-07, - -6.224841713408436e-07, - -8.220148697645299e-07, - 8.876100914738924e-07, - 9.307215563684733e-07, - -1.2263549285013004e-06, - -1.0180646862659517e-06, - 1.6495375341784678e-06, - 1.0677997143447814e-06, - -2.1672160434902636e-06, - -1.0594984616938665e-06, - 2.7878934786218487e-06, - 9.682092811742628e-07, - -3.5175644448463682e-06, - -7.642359224979942e-07, - 4.3586056187007376e-06, - 4.130757186506121e-07, - -5.3085202796190045e-06, - 1.2443999883255955e-07, - 6.358555800084345e-06, - -8.917594302752569e-07, - -7.492222625290795e-06, - 1.9359346976965215e-06, - 8.683753613501706e-06, - -3.3066166723901634e-06, - -9.896553377686571e-06, - 5.054684641484621e-06, - 1.1081698107413262e-05, - -7.230482378766363e-06, - -1.2176556837151924e-05, - 9.881641808207685e-06, - 1.310361479118067e-05, - -1.3050487570176674e-05, - -1.3769587763932494e-05, - 1.6771030367931327e-05, - 1.4064922949971825e-05, - -2.1065573831506833e-05, - -1.386378567354253e-05, - 2.5940978523130324e-05, - 1.3024632549176766e-05, - -3.138464724410613e-05, - -1.1391469231232117e-05, - 3.7360317493302477e-05, - 8.795884637183773e-06, - -4.380376916620311e-05, - -5.059942993990692e-06, - 5.061857766137688e-05, - -1.2729279960739985e-18, - -5.767206367871742e-05, - 6.568510314697647e-06, - 6.479161027901487e-05, - -1.482529579321305e-05, - -7.176153430630762e-05, - 2.493843998881951e-05, - 7.832071210975769e-05, - -3.705668910231432e-05, - -8.416116770617441e-05, - 5.1300867944157375e-05, - 8.892783420621756e-05, - -6.77545401257115e-05, - -9.221969567445976e-05, - 8.645406887217791e-05, - 9.359250590922007e-05, - -0.00010737827686101525, - -9.256326236908447e-05, - 0.0001304379450839715, - 8.861658728324014e-05, - -0.0001554654305418504, - -8.121313372668574e-05, - 0.0001822047191488953, - 6.980009223001803e-05, - -0.00021030226203261508, - -5.382382370758039e-05, - 0.00023929896894150197, - 3.274458779064277e-05, - -0.00026862375023307836, - -6.0532729964936305e-06, - 0.00029758900751817864, - -2.671003221043526e-05, - -0.000325388471242336, - 6.593585899246278e-05, - 0.0003510977702523597, - -0.00011192387190357726, - -0.0003736780929427704, - 0.0001648604519001771, - 0.000391983261423784, - -0.00022479579247089605, - -0.00040477048916979987, - 0.00029162101662982327, - 0.000410715029047172, - -0.00036504587911318116, - -0.00040842884314578015, - 0.0004445776651260253, - 0.0003964833395366939, - -0.0005295019321669618, - -0.00037343612546606575, - 0.0006188657627433, - 0.00033786162350484585, - -0.0007114642013347918, - -0.0002883852891386307, - 0.0008058305372427186, - 0.00022372105788811238, - -0.0009002310648224352, - -0.0001427115403024383, - 0.0009926649033332297, - 4.437037731172248e-05, - -0.0010808693900394488, - 7.207393010339128e-05, - 0.0011623314725868622, - -0.00020713349063999724, - -0.0012343054209503327, - 0.0003610166387724715, - 0.0012938370568711898, - -0.0005335845995545083, - -0.0013377945617159535, - 0.0007243101385594058, - 0.0013629057746815654, - -0.0009322391431135004, - -0.0013658017353411673, - 0.0011559560634399873, - 0.001343066061215347, - -0.0013935540949750406, - -0.0012912895862815387, - 0.0016426109036876274, - 0.0012071295242966488, - -0.001900170582736454, - -0.0010873722658800765, - 0.0021627323794196534, - 0.0009289987749149286, - -0.0024262465443003586, - -0.0007292514223482997, - 0.002686117427541705, - 0.00048570098808220843, - -0.0029372136780933438, - -0.0001963124782057012, - 0.0031738850853730077, - -0.0001404916512597725, - -0.003389985234223304, - 0.0005257722987911238, - 0.003578898712425241, - -0.0009600199781197914, - -0.003733571100688918, - 0.001443107923088798, - 0.0038465393642386826, - -0.001974253349587081, - -0.003909959516535164, - 0.0025519880785917614, - 0.003915627483496994, - -0.003174139582456504, - -0.003854987874872023, - 0.0038378233438448386, - 0.0037191237352354268, - -0.0045394472161245205, - -0.003498718093020069, - 0.0052747282494331775, - 0.0031839749242818863, - -0.006038722202602122, - -0.0027644824727652566, - 0.006825865702928783, - 0.0022289948598659135, - -0.007630030749218701, - -0.0015650971422069776, - 0.008444590984764183, - 0.0007587019748704784, - -0.009262498902418221, - 0.00020670150584601254, - 0.010076372890169986, - -0.0013511718972066557, - -0.010878592789031707, - 0.002700017220606699, - 0.0116614024217388, - -0.004286419502573899, - -0.01241701736642861, - 0.0061555897334271996, - 0.013137736099170549, - -0.008371571243092978, - -0.013816052517290636, - 0.01102886093018991, - 0.014444767785294677, - -0.014273331179705539, - -0.015017099419278979, - 0.01834245227603654, - 0.015526785545393338, - -0.023649360357418405, - -0.01596818233340632, - 0.030978952287937712, - 0.016336352716813843, - -0.04202003363176658, - -0.016627144664162638, - 0.061174433061502255, - 0.01683725745918051, - -0.10460220813936734, - -0.01696429467574168, - 0.3178080921167005, - 0.5170068048932956, - 0.3178080921167005, - -0.01696429467574168, - -0.10460220813936734, - 0.01683725745918051, - 0.061174433061502255, - -0.016627144664162638, - -0.04202003363176658, - 0.016336352716813843, - 0.030978952287937712, - -0.01596818233340632, - -0.023649360357418405, - 0.015526785545393338, - 0.01834245227603654, - -0.015017099419278979, - -0.014273331179705539, - 0.014444767785294677, - 0.01102886093018991, - -0.013816052517290636, - -0.008371571243092978, - 0.013137736099170549, - 0.0061555897334271996, - -0.01241701736642861, - -0.004286419502573899, - 0.0116614024217388, - 0.002700017220606699, - -0.010878592789031707, - -0.0013511718972066557, - 0.010076372890169986, - 0.00020670150584601254, - -0.009262498902418221, - 0.0007587019748704784, - 0.008444590984764183, - -0.0015650971422069776, - -0.007630030749218701, - 0.0022289948598659135, - 0.006825865702928783, - -0.0027644824727652566, - -0.006038722202602122, - 0.0031839749242818863, - 0.0052747282494331775, - -0.003498718093020069, - -0.0045394472161245205, - 0.0037191237352354268, - 0.0038378233438448386, - -0.003854987874872023, - -0.003174139582456504, - 0.003915627483496994, - 0.0025519880785917614, - -0.003909959516535164, - -0.001974253349587081, - 0.0038465393642386826, - 0.001443107923088798, - -0.003733571100688918, - -0.0009600199781197914, - 0.003578898712425241, - 0.0005257722987911238, - -0.003389985234223304, - -0.0001404916512597725, - 0.0031738850853730077, - -0.0001963124782057012, - -0.0029372136780933438, - 0.00048570098808220843, - 0.002686117427541705, - -0.0007292514223482997, - -0.0024262465443003586, - 0.0009289987749149286, - 0.0021627323794196534, - -0.0010873722658800765, - -0.001900170582736454, - 0.0012071295242966488, - 0.0016426109036876274, - -0.0012912895862815387, - -0.0013935540949750406, - 0.001343066061215347, - 0.0011559560634399873, - -0.0013658017353411673, - -0.0009322391431135004, - 0.0013629057746815654, - 0.0007243101385594058, - -0.0013377945617159535, - -0.0005335845995545083, - 0.0012938370568711898, - 0.0003610166387724715, - -0.0012343054209503327, - -0.00020713349063999724, - 0.0011623314725868622, - 7.207393010339128e-05, - -0.0010808693900394488, - 4.437037731172248e-05, - 0.0009926649033332297, - -0.0001427115403024383, - -0.0009002310648224352, - 0.00022372105788811238, - 0.0008058305372427186, - -0.0002883852891386307, - -0.0007114642013347918, - 0.00033786162350484585, - 0.0006188657627433, - -0.00037343612546606575, - -0.0005295019321669618, - 0.0003964833395366939, - 0.0004445776651260253, - -0.00040842884314578015, - -0.00036504587911318116, - 0.000410715029047172, - 0.00029162101662982327, - -0.00040477048916979987, - -0.00022479579247089605, - 0.000391983261423784, - 0.0001648604519001771, - -0.0003736780929427704, - -0.00011192387190357726, - 0.0003510977702523597, - 6.593585899246278e-05, - -0.000325388471242336, - -2.671003221043526e-05, - 0.00029758900751817864, - -6.0532729964936305e-06, - -0.00026862375023307836, - 3.274458779064277e-05, - 0.00023929896894150197, - -5.382382370758039e-05, - -0.00021030226203261508, - 6.980009223001803e-05, - 0.0001822047191488953, - -8.121313372668574e-05, - -0.0001554654305418504, - 8.861658728324014e-05, - 0.0001304379450839715, - -9.256326236908447e-05, - -0.00010737827686101525, - 9.359250590922007e-05, - 8.645406887217791e-05, - -9.221969567445976e-05, - -6.77545401257115e-05, - 8.892783420621756e-05, - 5.1300867944157375e-05, - -8.416116770617441e-05, - -3.705668910231432e-05, - 7.832071210975769e-05, - 2.493843998881951e-05, - -7.176153430630762e-05, - -1.482529579321305e-05, - 6.479161027901487e-05, - 6.568510314697647e-06, - -5.767206367871742e-05, - -1.2729279960739985e-18, - 5.061857766137688e-05, - -5.059942993990692e-06, - -4.380376916620311e-05, - 8.795884637183773e-06, - 3.7360317493302477e-05, - -1.1391469231232117e-05, - -3.138464724410613e-05, - 1.3024632549176766e-05, - 2.5940978523130324e-05, - -1.386378567354253e-05, - -2.1065573831506833e-05, - 1.4064922949971825e-05, - 1.6771030367931327e-05, - -1.3769587763932494e-05, - -1.3050487570176674e-05, - 1.310361479118067e-05, - 9.881641808207685e-06, - -1.2176556837151924e-05, - -7.230482378766363e-06, - 1.1081698107413262e-05, - 5.054684641484621e-06, - -9.896553377686571e-06, - -3.3066166723901634e-06, - 8.683753613501706e-06, - 1.9359346976965215e-06, - -7.492222625290795e-06, - -8.917594302752569e-07, - 6.358555800084345e-06, - 1.2443999883255955e-07, - -5.3085202796190045e-06, - 4.130757186506121e-07, - 4.3586056187007376e-06, - -7.642359224979942e-07, - -3.5175644448463682e-06, - 9.682092811742628e-07, - 2.7878934786218487e-06, - -1.0594984616938665e-06, - -2.1672160434902636e-06, - 1.0677997143447814e-06, - 1.6495375341784678e-06, - -1.0180646862659517e-06, - -1.2263549285013004e-06, - 9.307215563684733e-07, - 8.876100914738924e-07, - -8.220148697645299e-07, - -6.224841713408436e-07, - 7.044268061921138e-07, - 4.200367275533767e-07, - -5.871467509278004e-07, - -2.6969832218324916e-07, - 4.765606699845109e-07, - 1.6162916242945288e-07, - -3.767366685630587e-07, - -8.695906085997518e-08, - 2.898880068451338e-07, - 3.792557598966923e-08, - -2.1679956837762108e-07, - -7.927830161980731e-09, - 1.5720816822680103e-07, - -8.486685443549004e-09, - -1.1013103295646797e-07, - 1.5685867044913693e-08, - 7.414020115596361e-08, - -1.7052240013897604e-08, - -4.758343509079679e-08, - 1.510435454511938e-08, - 2.875448571604994e-08, - -1.162897576000204e-08, - -1.601722660647997e-08, - 7.814782506112341e-09, - 7.889301672202866e-09, - -4.380355015683147e-09, - -3.0915687919410175e-09, + 1.2391850466535611e-11, + 3.333531231852631e-11, + -3.818076213800401e-11, + -1.1509190451828784e-10, + 6.213231728454743e-11, + 2.87564799120199e-10, + -4.83714488602796e-11, + -5.98140269952324e-10, + -7.613942071856133e-11, + 1.091658461057221e-09, + 4.366508439461712e-10, + -1.7901478075133797e-09, + -1.2235803551276082e-09, + 2.6606550552703873e-09, + 2.6983221513529074e-09, + -3.5701268624480448e-09, + -5.1850938604085815e-09, + 4.22849061463688e-09, + 9.040594409322398e-09, + -4.124314021234486e-09, + -1.4592660607406648e-08, + 2.4616283588031036e-09, + 2.2039870818100458e-08, + 1.8885900693913606e-09, + -3.1306686778954656e-08, + -1.0403682851560303e-08, + 4.185370804820422e-08, + 2.4868193950736736e-08, + -5.2450215719700035e-08, + -4.725031763559409e-08, + 6.092638395586248e-08, + 7.946970063915712e-08, + -6.393490946981877e-08, + -1.2303428788774302e-07, + 5.676541482012926e-08, + 1.78533026093471e-07, + -3.326832582637974e-08, + -2.449864750294829e-07, + -1.4044042441359084e-08, + 3.1907908092442563e-07, + 9.36452494218834e-08, + -3.9432463332063344e-07, + -2.143188256204172e-07, + 4.6024886815334354e-07, + 3.8409805762714097e-07, + -5.017077715653631e-07, + -6.087855952658568e-07, + 4.984931642791297e-07, + 8.900563561995833e-07, + -4.2540377575602484e-07, + -1.22320149064785e-06, + 2.529745550676697e-07, + 1.5946390442210958e-06, + 5.0946720741292064e-08, + -1.9793949347642123e-06, + -5.18614345326223e-07, + 2.3388400822855868e-06, + 1.178686943365455e-06, + -2.6190477406863324e-06, + -2.051402354112695e-06, + 2.750198862065714e-06, + 3.1427325820583206e-06, + -2.6475006778025306e-06, + -4.437778022762077e-06, + 2.2140819263549915e-06, + 5.893834031499378e-06, + -1.346275173199573e-06, + -7.433746697465689e-06, + -5.84176093744815e-08, + 8.940351623169533e-06, + 2.0905619376415587e-06, + -1.0252938719737726e-05, + -4.815366354286811e-06, + 1.116678371934488e-05, + 8.257045673101373e-06, + -1.1436808304058493e-05, + -1.2381782138946336e-05, + 1.07863516162757e-05, + 1.7080481116289375e-05, + -8.921836494731473e-06, + -2.2152897980823273e-05, + 5.553781010971955e-06, + 2.7295047456388915e-05, + -4.241366082350153e-07, + -3.20920511104106e-05, + -6.660662180398171e-06, + 3.6018686158196044e-05, + 1.579225072211783e-05, + -3.8449823912780616e-05, + -2.6920566641892563e-05, + 3.86826504349648e-05, + 3.9814608385366115e-05, + -3.5972018152061004e-05, + -5.40272758493732e-05, + 2.957947522797144e-05, + 6.886841768724772e-05, + -1.883547055403238e-05, + -8.339055426463937e-05, + 3.2129726777802045e-06, + 9.639178287766639e-05, + 1.7590665981469e-05, + -0.00010644003002621123, + -4.356823270696307e-05, + 0.00011192203972004702, + 7.43196068089881e-05, + -0.00011111924915469956, + -0.00010897840035651321, + 0.0001023110102270036, + 0.0001461564332782211, + -8.39035143510593e-05, + -0.0001839145994539007, + 5.458036000311854e-05, + 0.00021976848452479796, + -1.3468102824193484e-05, + -0.0002507361886566276, + -3.969247678935454e-05, + 0.00027343412277189825, + 0.00010438105445408068, + -0.00028422407111677975, + -0.00017915202264934046, + 0.00027941159473480933, + 0.0002615232577530964, + -0.0002554920062562159, + -0.0003479147183809301, + 0.00020943586336383046, + 0.0004336515130111242, + -0.00013900145970572364, + -0.0005130441646730521, + 4.305744764222408e-05, + 0.0005795556035604514, + 7.810514286236892e-05, + -0.000626059960741494, + -0.00022249525192315596, + 0.0006451926445289425, + 0.0003862040895990157, + -0.0006297846881076496, + -0.000563273268175783, + 0.0005733672979368042, + 0.0007456731567251603, + -0.00047072533356158625, + -0.0009234124120507005, + 0.00031847160782443723, + 0.0010847942203041586, + -0.00011560795133020693, + -0.001216827481451714, + -0.00013596551747171158, + 0.0013057922370346962, + 0.000431034022287387, + -0.0013379484836784292, + -0.0007607859194021795, + 0.0013003666729946694, + 0.0011127033514844527, + -0.0011818473308014906, + -0.0014706590334826724, + 0.0009738870733597434, + 0.0018152441480513153, + -0.0006716396297363814, + -0.00212434140340482, + 0.00027481405517449135, + 0.0023739441826708192, + 0.00021155117442863655, + -0.002539208026359171, + -0.0007764995060906656, + 0.002595705252002013, + 0.0014028105594797902, + -0.002520838280105065, + -0.002066946890004278, + 0.002295353243766825, + 0.0027393311085735996, + -0.0019048837776677375, + -0.0033849759136069676, + 0.0013414465376334745, + 0.003964471427659919, + -0.0006048059155471451, + -0.0044353140878159725, + -0.0002963736582894608, + 0.004753540316566857, + 0.0013436629459418286, + -0.004875607545075603, + -0.002508353422240745, + 0.004760446192255864, + 0.0037513374964797752, + -0.004371590227561371, + -0.005023403561028252, + 0.003679282168147092, + 0.006265935796833347, + -0.0026624417988238744, + -0.0074119723035414265, + 0.001310387304196292, + 0.008387531541080585, + 0.0003757967194566891, + -0.009113063498733195, + -0.002382337716262893, + 0.009504812205198838, + 0.004682375201032287, + -0.0094757769641619, + -0.007236133980982176, + 0.008935803592365966, + 0.00999169454693342, + -0.00779006602885168, + -0.012886354290132159, + 0.005934686330738007, + 0.015848541869091636, + -0.0032471953026167945, + -0.018800216515377825, + -0.00043276438591991135, + 0.021659656151486406, + 0.00534258783499553, + -0.024344514621636986, + -0.011880324982220983, + 0.026775010617614767, + 0.02080098573194893, + -0.028877100210586794, + -0.033749565270108174, + 0.03058548206942095, + 0.05507167446179364, + -0.03184628978971992, + -0.10086083835694314, + 0.03261933911867418, + 0.3165475195411714, + 0.46712018140067035, + 0.3165475195411714, + 0.03261933911867418, + -0.10086083835694314, + -0.03184628978971992, + 0.05507167446179364, + 0.03058548206942095, + -0.033749565270108174, + -0.028877100210586794, + 0.02080098573194893, + 0.026775010617614767, + -0.011880324982220983, + -0.024344514621636986, + 0.00534258783499553, + 0.021659656151486406, + -0.00043276438591991135, + -0.018800216515377825, + -0.0032471953026167945, + 0.015848541869091636, + 0.005934686330738007, + -0.012886354290132159, + -0.00779006602885168, + 0.00999169454693342, + 0.008935803592365966, + -0.007236133980982176, + -0.0094757769641619, + 0.004682375201032287, + 0.009504812205198838, + -0.002382337716262893, + -0.009113063498733195, + 0.0003757967194566891, + 0.008387531541080585, + 0.001310387304196292, + -0.0074119723035414265, + -0.0026624417988238744, + 0.006265935796833347, + 0.003679282168147092, + -0.005023403561028252, + -0.004371590227561371, + 0.0037513374964797752, + 0.004760446192255864, + -0.002508353422240745, + -0.004875607545075603, + 0.0013436629459418286, + 0.004753540316566857, + -0.0002963736582894608, + -0.0044353140878159725, + -0.0006048059155471451, + 0.003964471427659919, + 0.0013414465376334745, + -0.0033849759136069676, + -0.0019048837776677375, + 0.0027393311085735996, + 0.002295353243766825, + -0.002066946890004278, + -0.002520838280105065, + 0.0014028105594797902, + 0.002595705252002013, + -0.0007764995060906656, + -0.002539208026359171, + 0.00021155117442863655, + 0.0023739441826708192, + 0.00027481405517449135, + -0.00212434140340482, + -0.0006716396297363814, + 0.0018152441480513153, + 0.0009738870733597434, + -0.0014706590334826724, + -0.0011818473308014906, + 0.0011127033514844527, + 0.0013003666729946694, + -0.0007607859194021795, + -0.0013379484836784292, + 0.000431034022287387, + 0.0013057922370346962, + -0.00013596551747171158, + -0.001216827481451714, + -0.00011560795133020693, + 0.0010847942203041586, + 0.00031847160782443723, + -0.0009234124120507005, + -0.00047072533356158625, + 0.0007456731567251603, + 0.0005733672979368042, + -0.000563273268175783, + -0.0006297846881076496, + 0.0003862040895990157, + 0.0006451926445289425, + -0.00022249525192315596, + -0.000626059960741494, + 7.810514286236892e-05, + 0.0005795556035604514, + 4.305744764222408e-05, + -0.0005130441646730521, + -0.00013900145970572364, + 0.0004336515130111242, + 0.00020943586336383046, + -0.0003479147183809301, + -0.0002554920062562159, + 0.0002615232577530964, + 0.00027941159473480933, + -0.00017915202264934046, + -0.00028422407111677975, + 0.00010438105445408068, + 0.00027343412277189825, + -3.969247678935454e-05, + -0.0002507361886566276, + -1.3468102824193484e-05, + 0.00021976848452479796, + 5.458036000311854e-05, + -0.0001839145994539007, + -8.39035143510593e-05, + 0.0001461564332782211, + 0.0001023110102270036, + -0.00010897840035651321, + -0.00011111924915469956, + 7.43196068089881e-05, + 0.00011192203972004702, + -4.356823270696307e-05, + -0.00010644003002621123, + 1.7590665981469e-05, + 9.639178287766639e-05, + 3.2129726777802045e-06, + -8.339055426463937e-05, + -1.883547055403238e-05, + 6.886841768724772e-05, + 2.957947522797144e-05, + -5.40272758493732e-05, + -3.5972018152061004e-05, + 3.9814608385366115e-05, + 3.86826504349648e-05, + -2.6920566641892563e-05, + -3.8449823912780616e-05, + 1.579225072211783e-05, + 3.6018686158196044e-05, + -6.660662180398171e-06, + -3.20920511104106e-05, + -4.241366082350153e-07, + 2.7295047456388915e-05, + 5.553781010971955e-06, + -2.2152897980823273e-05, + -8.921836494731473e-06, + 1.7080481116289375e-05, + 1.07863516162757e-05, + -1.2381782138946336e-05, + -1.1436808304058493e-05, + 8.257045673101373e-06, + 1.116678371934488e-05, + -4.815366354286811e-06, + -1.0252938719737726e-05, + 2.0905619376415587e-06, + 8.940351623169533e-06, + -5.84176093744815e-08, + -7.433746697465689e-06, + -1.346275173199573e-06, + 5.893834031499378e-06, + 2.2140819263549915e-06, + -4.437778022762077e-06, + -2.6475006778025306e-06, + 3.1427325820583206e-06, + 2.750198862065714e-06, + -2.051402354112695e-06, + -2.6190477406863324e-06, + 1.178686943365455e-06, + 2.3388400822855868e-06, + -5.18614345326223e-07, + -1.9793949347642123e-06, + 5.0946720741292064e-08, + 1.5946390442210958e-06, + 2.529745550676697e-07, + -1.22320149064785e-06, + -4.2540377575602484e-07, + 8.900563561995833e-07, + 4.984931642791297e-07, + -6.087855952658568e-07, + -5.017077715653631e-07, + 3.8409805762714097e-07, + 4.6024886815334354e-07, + -2.143188256204172e-07, + -3.9432463332063344e-07, + 9.36452494218834e-08, + 3.1907908092442563e-07, + -1.4044042441359084e-08, + -2.449864750294829e-07, + -3.326832582637974e-08, + 1.78533026093471e-07, + 5.676541482012926e-08, + -1.2303428788774302e-07, + -6.393490946981877e-08, + 7.946970063915712e-08, + 6.092638395586248e-08, + -4.725031763559409e-08, + -5.2450215719700035e-08, + 2.4868193950736736e-08, + 4.185370804820422e-08, + -1.0403682851560303e-08, + -3.1306686778954656e-08, + 1.8885900693913606e-09, + 2.2039870818100458e-08, + 2.4616283588031036e-09, + -1.4592660607406648e-08, + -4.124314021234486e-09, + 9.040594409322398e-09, + 4.22849061463688e-09, + -5.1850938604085815e-09, + -3.5701268624480448e-09, + 2.6983221513529074e-09, + 2.6606550552703873e-09, + -1.2235803551276082e-09, + -1.7901478075133797e-09, + 4.366508439461712e-10, + 1.091658461057221e-09, + -7.613942071856133e-11, + -5.98140269952324e-10, + -4.83714488602796e-11, + 2.87564799120199e-10, + 6.213231728454743e-11, + -1.1509190451828784e-10, + -3.818076213800401e-11, + 3.333531231852631e-11, + 1.2391850466535611e-11, ]; +// β = 20.6 +// fc = 20.65 +// -195dB before 22kHz #[allow(clippy::excessive_precision)] -pub const HZ96000_COEFFICIENTS: [f64; 441] = [ - 5.560249704317343e-10, - 6.7457053197573024e-09, - 1.2990035898564e-10, - -1.3822536813261148e-08, - -2.9300911640564656e-09, - 2.399478082083703e-08, - 9.41622844952465e-09, - -3.7481047434174274e-08, - -2.1662912144899056e-08, - 5.404635607782416e-08, - 4.224936834873014e-08, - -7.277372749908042e-08, - -7.420406557781993e-08, - 9.180574659487064e-08, - 1.2087511596227733e-07, - -1.0806955116527926e-07, - -1.8571016370140034e-07, - 1.170047184434655e-07, - 2.7193189671565557e-07, - -1.1231972250589951e-07, - -3.820995948332229e-07, - 8.580858719281314e-08, - 5.175534033235693e-07, - -2.7264542219779906e-08, - -6.777463371709023e-07, - -7.546867930879655e-08, - 8.594792841000288e-07, - 2.362654075465788e-07, - -1.0560662274062185e-06, - -4.702647143542115e-07, - 1.2564701211818615e-06, - 7.931573753480084e-07, - -1.444463705583346e-06, - -1.2201828893460145e-06, - 1.5978832544551455e-06, - 1.7648179657738676e-06, - -1.6880558459176943e-06, - -2.4371537290850187e-06, - 1.6794911507065441e-06, - 3.241978858347619e-06, - -1.5299357680554498e-06, - -4.176609629091859e-06, - 1.1908906002040628e-06, - 5.228534691612452e-06, - -6.086884800383554e-07, - -6.372971415214212e-06, - -2.7378079220115605e-07, - 7.570460432970104e-06, - 1.5146285260962556e-06, - -8.764654009459012e-06, - -3.1697646987478756e-06, - 9.880480130834774e-06, - 5.288925533796424e-06, - -1.0822885686373e-05, - -7.91096385725646e-06, - 1.147637656202352e-05, - 1.1058484433166778e-05, - -1.1705577680692975e-05, - -1.4731961794014652e-05, - 1.1357029900154213e-05, - 1.8903537311698937e-05, - -1.02624213729694e-05, - -2.3510752894245287e-05, - 8.243417040440697e-06, - 2.845053771367662e-05, - -5.118200477547931e-06, - -3.357381830826675e-05, - 7.097771141127904e-07, - 3.868116747816418e-05, - 5.1439924827157876e-06, - -4.3519939664326893e-05, - -1.2578754584543908e-05, - 4.778335604530135e-05, - 2.1690650088703207e-05, - -5.1111997713789026e-05, - -3.25222717208439e-05, - 5.3098136771621236e-05, - 4.504805078593748e-05, - -5.329328046531237e-05, - -5.9159648408755305e-05, - 5.1219220953428783e-05, - 7.465203338939947e-05, - -4.638277246361614e-05, - -9.121102194406923e-05, - 3.829423929087409e-05, - 0.00010840312702721423, - -2.6489494595043767e-05, - -0.00012566860999063735, - 1.0555365102241566e-05, - 0.0001423186387411039, - 9.84218401105256e-06, - -0.00015753742873676115, - -3.492777968059588e-05, - 0.00017039017161439265, - 6.478521332693489e-05, - -0.0001798374378895852, - -9.932598355762823e-05, - 0.00018475657369415775, - 0.00013825912105014366, - -0.00018397039760952743, - -0.00018106376125474611, - 0.0001762832452577541, - 0.00022696604405721683, - -0.00016052411172711115, - -0.0002749219824710103, - 0.0001355963128374769, - 0.0003236079454914325, - -0.0001005327357148728, - -0.0003714203356632149, - 5.4555389294439867e-05, - 0.00041648590394768506, - 2.8623898269992334e-06, - -0.00045668392990309446, - -7.193305595729272e-05, - 0.0004896812037153272, - 0.00015249357603317176, - -0.0005129803811882822, - -0.00024394827954764191, - 0.0005239818498263345, - 0.0003452185393899377, - -0.0005200587535404849, - -0.0004547020805523807, - 0.0004986442886477423, - 0.0005702447116739937, - -0.00045732982127237766, - -0.000689127166061252, - 0.00039397180535415474, - 0.0008080695174294933, - -0.0003068049228110276, - -0.0009232552984091209, - 0.00019455834610855686, - 0.0010303769985912112, - -5.657156236266569e-05, - -0.0011247040600814201, - -0.00010709417927966556, - 0.0012011738336718703, - 0.00029555300653979545, - -0.001254505224223492, - -0.0005070045038801027, - 0.0012793339607298146, - 0.0007386610901853346, - -0.001270367600067128, - -0.0009866968976081866, - 0.0012225575424289717, - 0.0012462220382388606, - -0.0011312845324039525, - -0.0015112856634668026, - 0.000992553375801394, - 0.001774910578349497, - -0.0008031919524797967, - -0.002029161380794138, - 0.0005610490828306219, - 0.0022652471702039635, - -0.00026518544171678135, - -0.0024736588359997344, - -8.394846280610072e-05, - 0.0026443398223918325, - 0.0004843531929226606, - -0.0027668881061983677, - -0.0009323458453637817, - 0.0028307859575470943, - 0.0014224552392333313, - -0.0028256529198113544, - -0.001947351215488067, - 0.002741516387210494, - 0.002497811396668082, - -0.002569093217952415, - -0.003062727446644023, - 0.002300075037489875, - 0.003629151287117882, - -0.0019274092967394656, - -0.0041823798600841, - 0.0014455677851960128, - 0.004706074850303062, - -0.0008507941834454292, - -0.005182411251550035, - 0.00014132239052928864, - 0.0055922456849282496, - 0.0006824422129566309, - -0.00591529179532096, - -0.0016177857065913586, - 0.006130285582602136, - 0.0026595989567600935, - -0.006215117688556044, - -0.0038003463068190125, - 0.006146901643802061, - 0.005030093197407957, - -0.0059019354869432146, - -0.006336594699928613, - 0.00545549657976398, - 0.00770544523750543, - -0.004781381564722846, - -0.00912028798641521, - 0.003851057421363559, - 0.010563080653771224, - -0.0026322107431178856, - -0.012014412573237022, - 0.001086341830368365, - 0.013453866409008227, - 0.0008352110916833665, - -0.014860416266574179, - -0.0031979417347285668, - 0.01621285273010614, - 0.006095714026154995, - -0.017490224327851422, - -0.009671655092655702, - 0.01867228420781883, - 0.014158404619215172, - -0.019739930415957174, - -0.0199622658168629, - 0.020675628126808922, - 0.027859437853963814, - -0.021463802489478597, - -0.03952835273006745, - 0.022091191414822978, - 0.05935902511250348, - -0.02254714862612816, - -0.10349853375753784, - 0.022823888596670217, - 0.31743777574504695, - 0.47708333120157576, - 0.31743777574504695, - 0.022823888596670217, - -0.10349853375753784, - -0.02254714862612816, - 0.05935902511250348, - 0.022091191414822978, - -0.03952835273006745, - -0.021463802489478597, - 0.027859437853963814, - 0.020675628126808922, - -0.0199622658168629, - -0.019739930415957174, - 0.014158404619215172, - 0.01867228420781883, - -0.009671655092655702, - -0.017490224327851422, - 0.006095714026154995, - 0.01621285273010614, - -0.0031979417347285668, - -0.014860416266574179, - 0.0008352110916833665, - 0.013453866409008227, - 0.001086341830368365, - -0.012014412573237022, - -0.0026322107431178856, - 0.010563080653771224, - 0.003851057421363559, - -0.00912028798641521, - -0.004781381564722846, - 0.00770544523750543, - 0.00545549657976398, - -0.006336594699928613, - -0.0059019354869432146, - 0.005030093197407957, - 0.006146901643802061, - -0.0038003463068190125, - -0.006215117688556044, - 0.0026595989567600935, - 0.006130285582602136, - -0.0016177857065913586, - -0.00591529179532096, - 0.0006824422129566309, - 0.0055922456849282496, - 0.00014132239052928864, - -0.005182411251550035, - -0.0008507941834454292, - 0.004706074850303062, - 0.0014455677851960128, - -0.0041823798600841, - -0.0019274092967394656, - 0.003629151287117882, - 0.002300075037489875, - -0.003062727446644023, - -0.002569093217952415, - 0.002497811396668082, - 0.002741516387210494, - -0.001947351215488067, - -0.0028256529198113544, - 0.0014224552392333313, - 0.0028307859575470943, - -0.0009323458453637817, - -0.0027668881061983677, - 0.0004843531929226606, - 0.0026443398223918325, - -8.394846280610072e-05, - -0.0024736588359997344, - -0.00026518544171678135, - 0.0022652471702039635, - 0.0005610490828306219, - -0.002029161380794138, - -0.0008031919524797967, - 0.001774910578349497, - 0.000992553375801394, - -0.0015112856634668026, - -0.0011312845324039525, - 0.0012462220382388606, - 0.0012225575424289717, - -0.0009866968976081866, - -0.001270367600067128, - 0.0007386610901853346, - 0.0012793339607298146, - -0.0005070045038801027, - -0.001254505224223492, - 0.00029555300653979545, - 0.0012011738336718703, - -0.00010709417927966556, - -0.0011247040600814201, - -5.657156236266569e-05, - 0.0010303769985912112, - 0.00019455834610855686, - -0.0009232552984091209, - -0.0003068049228110276, - 0.0008080695174294933, - 0.00039397180535415474, - -0.000689127166061252, - -0.00045732982127237766, - 0.0005702447116739937, - 0.0004986442886477423, - -0.0004547020805523807, - -0.0005200587535404849, - 0.0003452185393899377, - 0.0005239818498263345, - -0.00024394827954764191, - -0.0005129803811882822, - 0.00015249357603317176, - 0.0004896812037153272, - -7.193305595729272e-05, - -0.00045668392990309446, - 2.8623898269992334e-06, - 0.00041648590394768506, - 5.4555389294439867e-05, - -0.0003714203356632149, - -0.0001005327357148728, - 0.0003236079454914325, - 0.0001355963128374769, - -0.0002749219824710103, - -0.00016052411172711115, - 0.00022696604405721683, - 0.0001762832452577541, - -0.00018106376125474611, - -0.00018397039760952743, - 0.00013825912105014366, - 0.00018475657369415775, - -9.932598355762823e-05, - -0.0001798374378895852, - 6.478521332693489e-05, - 0.00017039017161439265, - -3.492777968059588e-05, - -0.00015753742873676115, - 9.84218401105256e-06, - 0.0001423186387411039, - 1.0555365102241566e-05, - -0.00012566860999063735, - -2.6489494595043767e-05, - 0.00010840312702721423, - 3.829423929087409e-05, - -9.121102194406923e-05, - -4.638277246361614e-05, - 7.465203338939947e-05, - 5.1219220953428783e-05, - -5.9159648408755305e-05, - -5.329328046531237e-05, - 4.504805078593748e-05, - 5.3098136771621236e-05, - -3.25222717208439e-05, - -5.1111997713789026e-05, - 2.1690650088703207e-05, - 4.778335604530135e-05, - -1.2578754584543908e-05, - -4.3519939664326893e-05, - 5.1439924827157876e-06, - 3.868116747816418e-05, - 7.097771141127904e-07, - -3.357381830826675e-05, - -5.118200477547931e-06, - 2.845053771367662e-05, - 8.243417040440697e-06, - -2.3510752894245287e-05, - -1.02624213729694e-05, - 1.8903537311698937e-05, - 1.1357029900154213e-05, - -1.4731961794014652e-05, - -1.1705577680692975e-05, - 1.1058484433166778e-05, - 1.147637656202352e-05, - -7.91096385725646e-06, - -1.0822885686373e-05, - 5.288925533796424e-06, - 9.880480130834774e-06, - -3.1697646987478756e-06, - -8.764654009459012e-06, - 1.5146285260962556e-06, - 7.570460432970104e-06, - -2.7378079220115605e-07, - -6.372971415214212e-06, - -6.086884800383554e-07, - 5.228534691612452e-06, - 1.1908906002040628e-06, - -4.176609629091859e-06, - -1.5299357680554498e-06, - 3.241978858347619e-06, - 1.6794911507065441e-06, - -2.4371537290850187e-06, - -1.6880558459176943e-06, - 1.7648179657738676e-06, - 1.5978832544551455e-06, - -1.2201828893460145e-06, - -1.444463705583346e-06, - 7.931573753480084e-07, - 1.2564701211818615e-06, - -4.702647143542115e-07, - -1.0560662274062185e-06, - 2.362654075465788e-07, - 8.594792841000288e-07, - -7.546867930879655e-08, - -6.777463371709023e-07, - -2.7264542219779906e-08, - 5.175534033235693e-07, - 8.580858719281314e-08, - -3.820995948332229e-07, - -1.1231972250589951e-07, - 2.7193189671565557e-07, - 1.170047184434655e-07, - -1.8571016370140034e-07, - -1.0806955116527926e-07, - 1.2087511596227733e-07, - 9.180574659487064e-08, - -7.420406557781993e-08, - -7.277372749908042e-08, - 4.224936834873014e-08, - 5.404635607782416e-08, - -2.1662912144899056e-08, - -3.7481047434174274e-08, - 9.41622844952465e-09, - 2.399478082083703e-08, - -2.9300911640564656e-09, - -1.3822536813261148e-08, - 1.2990035898564e-10, - 6.7457053197573024e-09, - 5.560249704317343e-10, +pub const HZ96000_COEFFICIENTS: [f64; 481] = [ + -1.1994395146732941e-11, + 1.915913091651547e-11, + 5.960745176448492e-11, + -1.3061824926945334e-11, + -1.5601965248971414e-10, + -7.018946259546669e-11, + 2.809141662660533e-10, + 3.0701859865581745e-10, + -3.4623720597839356e-10, + -7.560570063834295e-10, + 1.7447498568802657e-10, + 1.3876779389493582e-09, + 4.841122743309274e-10, + -1.9950759509148982e-09, + -1.8677373375643354e-09, + 2.1241568247563375e-09, + 4.040333248197303e-09, + -1.0805250278628995e-09, + -6.6709623778697e-09, + -1.9223082282338022e-09, + 8.824396176308693e-09, + 7.432242319481839e-09, + -8.879743628810473e-09, + -1.526391589543894e-08, + 4.7187712854981555e-09, + 2.395097795739618e-08, + 5.699370544081556e-09, + -3.035967368747228e-08, + -2.3354970601865938e-08, + 2.9753058833130447e-08, + 4.6919535092840925e-08, + -1.6591644718097893e-08, + -7.159335536018259e-08, + -1.377054465796504e-08, + 8.856657748781657e-08, + 6.260407964576534e-08, + -8.57196191379699e-08, + -1.2502708392121218e-07, + 5.005664742272042e-08, + 1.8782810133829837e-07, + 2.8016936501450825e-08, + -2.2906722734906596e-07, + -1.4909601103428738e-07, + 2.206254163789919e-07, + 2.991551330257851e-07, + -1.3442648724143923e-07, + -4.4590734890745537e-07, + -4.7879991080474856e-08, + 5.394825425816262e-07, + 3.2331554237412893e-07, + -5.194446675053159e-07, + -6.57027401634456e-07, + 3.289904476631279e-07, + 9.768751569312674e-07, + 6.484665244473348e-08, + -1.1774482315206333e-06, + -6.487149967509684e-07, + 1.1367035368094803e-06, + 1.3443762821484977e-06, + -7.457595423462837e-07, + -2.0017613173009023e-06, + -5.1597883890098977e-08, + 2.411048558044751e-06, + 1.2177127242482222e-06, + -2.3383632432586653e-06, + -2.5901102398864627e-06, + 1.584294923048922e-06, + 3.874557757285263e-06, + -5.710332117813373e-08, + -4.67416428974752e-06, + -2.1552409835570375e-06, + 4.560474161361854e-06, + 4.735928837508751e-06, + -3.1823115172240006e-06, + -7.136437289370632e-06, + 3.9537838003464527e-07, + 8.638487741992554e-06, + 3.6168030625091555e-06, + -8.487457353378934e-06, + -8.268279687162828e-06, + 6.08570579554361e-06, + 1.2580315824988063e-05, + -1.2131040701919644e-06, + -1.5302661405843096e-05, + -5.777021855417445e-06, + 1.5151846270420658e-05, + 1.384911600179759e-05, + -1.1141109862235736e-05, + -2.1323121189248238e-05, + 2.942823993601164e-06, + 2.6097190687324947e-05, + 8.803876391769033e-06, + -2.605572654342193e-05, + -2.2340476276265842e-05, + 1.9612964695179654e-05, + 3.488121193630334e-05, + -6.290426820769221e-06, + -4.300164683848037e-05, + -1.2813427009008755e-05, + 4.3312083775684775e-05, + 3.48167368145463e-05, + -3.332549671812382e-05, + -5.5242609990036297e-05, + 1.2352369776983332e-05, + 6.866855793659725e-05, + 1.7799995928150835e-05, + -6.980212930301614e-05, + -5.2557793316981136e-05, + 5.482811512633073e-05, + 8.492850576208972e-05, + -2.2760842363500776e-05, + -0.00010654680234804103, + -2.3537583858735534e-05, + 0.00010934295961065633, + 7.701671223906466e-05, + -8.758109455240882e-05, + -0.00012703637978599055, + 3.985676013463422e-05, + 0.000160996958416934, + 2.9449694220786016e-05, + -0.0001668593951537981, + -0.00010975678946015862, + 0.00013615772393127433, + 0.00018525861849561634, + -6.68894489448072e-05, + -0.0002373927755966462, + -3.4446399696334056e-05, + 0.0002485555889307161, + 0.00015235556237131378, + -0.00020646049212939892, + -0.0002638742511127665, + 0.00010824229568279674, + 0.00034220774450222953, + 3.672879517702098e-05, + -0.00036208738952237034, + -0.00020627705332200324, + 0.0003059539182138257, + 0.00036771798854165044, + -0.000169686983664989, + -0.0004830949655477637, + -3.356087538554157e-05, + 0.000516747424579293, + 0.0002727180713288947, + -0.0004439276317579888, + -0.0005021408279491042, + 0.0002586774737622995, + 0.0006689840372751786, + 2.1005660832265722e-05, + -0.0007236944059030511, + -0.0003524392471042917, + 0.0006318239364446761, + 0.0006729912856795399, + -0.00038471232202596237, + -0.0009102437079813836, + 6.38666808859098e-06, + 0.0009962913814304114, + 0.0004455959415068065, + -0.000883700794702363, + -0.0008866682568559575, + 0.0005598267515351781, + 0.0012189997524931617, + -5.597052247416223e-05, + -0.0013506744814651686, + -0.0005515874903799232, + 0.0012169664404254664, + 0.0011503309637701427, + -0.0007993367464203114, + -0.0016097669927078522, + 0.00013754769531381425, + 0.0018067748745233803, + 0.0006689447112888635, + -0.0016536419135613023, + -0.0014724108324020095, + 0.0011230729782408402, + 0.002100681647505729, + -0.0002642101377135884, + -0.0023902085007997164, + -0.0007952746528221351, + 0.0022226419001671943, + 0.0018636837000276705, + -0.0015575734284970257, + -0.002715873624285176, + 0.00045379149820339325, + 0.003135840880826238, + 0.000927277958513202, + -0.0029640553852661746, + -0.002339401002118082, + 0.0021401998423146917, + 0.0034900692956516236, + -0.0007315871721489155, + -0.004094709676209372, + -0.0010608480645675196, + 0.003937529987755766, + 0.002923538240315551, + -0.0029273062430608095, + -0.004477830306334461, + 0.0011358453900638611, + 0.005348134434122349, + 0.0011912532792074859, + -0.005239696871558134, + -0.0036576629006305752, + 0.00401161436225257, + 0.005773326918770769, + -0.0017297944791563327, + -0.007038739381869608, + -0.0013133934970218752, + 0.007043643801584631, + 0.0046211349636550774, + -0.005562920332614075, + -0.007557142405019193, + 0.0026309665606692157, + 0.009446822141073024, + 0.0014221140453043691, + -0.009700340779953782, + -0.005983789053885521, + 0.007937816806243354, + 0.010225042584697239, + -0.004094712744425417, + -0.01321279387321101, + -0.0015125512082305491, + 0.014053552677719357, + 0.00817458594195583, + -0.012046130339034358, + -0.014834568635464904, + 0.006817826441535441, + 0.020182379994583177, + 0.0015804785111363736, + -0.02277241530222604, + -0.012639398046523115, + 0.021117172951004536, + 0.025426139972915516, + -0.013660357026856107, + -0.038688052580126614, + -0.001622620777002329, + 0.05101135603655969, + 0.02896082858633861, + -0.06101196868999017, + -0.0838338660597568, + 0.06752918327641895, + 0.3106351462916042, + 0.4302083333281181, + 0.3106351462916042, + 0.06752918327641895, + -0.0838338660597568, + -0.06101196868999017, + 0.02896082858633861, + 0.05101135603655969, + -0.001622620777002329, + -0.038688052580126614, + -0.013660357026856107, + 0.025426139972915516, + 0.021117172951004536, + -0.012639398046523115, + -0.02277241530222604, + 0.0015804785111363736, + 0.020182379994583177, + 0.006817826441535441, + -0.014834568635464904, + -0.012046130339034358, + 0.00817458594195583, + 0.014053552677719357, + -0.0015125512082305491, + -0.01321279387321101, + -0.004094712744425417, + 0.010225042584697239, + 0.007937816806243354, + -0.005983789053885521, + -0.009700340779953782, + 0.0014221140453043691, + 0.009446822141073024, + 0.0026309665606692157, + -0.007557142405019193, + -0.005562920332614075, + 0.0046211349636550774, + 0.007043643801584631, + -0.0013133934970218752, + -0.007038739381869608, + -0.0017297944791563327, + 0.005773326918770769, + 0.00401161436225257, + -0.0036576629006305752, + -0.005239696871558134, + 0.0011912532792074859, + 0.005348134434122349, + 0.0011358453900638611, + -0.004477830306334461, + -0.0029273062430608095, + 0.002923538240315551, + 0.003937529987755766, + -0.0010608480645675196, + -0.004094709676209372, + -0.0007315871721489155, + 0.0034900692956516236, + 0.0021401998423146917, + -0.002339401002118082, + -0.0029640553852661746, + 0.000927277958513202, + 0.003135840880826238, + 0.00045379149820339325, + -0.002715873624285176, + -0.0015575734284970257, + 0.0018636837000276705, + 0.0022226419001671943, + -0.0007952746528221351, + -0.0023902085007997164, + -0.0002642101377135884, + 0.002100681647505729, + 0.0011230729782408402, + -0.0014724108324020095, + -0.0016536419135613023, + 0.0006689447112888635, + 0.0018067748745233803, + 0.00013754769531381425, + -0.0016097669927078522, + -0.0007993367464203114, + 0.0011503309637701427, + 0.0012169664404254664, + -0.0005515874903799232, + -0.0013506744814651686, + -5.597052247416223e-05, + 0.0012189997524931617, + 0.0005598267515351781, + -0.0008866682568559575, + -0.000883700794702363, + 0.0004455959415068065, + 0.0009962913814304114, + 6.38666808859098e-06, + -0.0009102437079813836, + -0.00038471232202596237, + 0.0006729912856795399, + 0.0006318239364446761, + -0.0003524392471042917, + -0.0007236944059030511, + 2.1005660832265722e-05, + 0.0006689840372751786, + 0.0002586774737622995, + -0.0005021408279491042, + -0.0004439276317579888, + 0.0002727180713288947, + 0.000516747424579293, + -3.356087538554157e-05, + -0.0004830949655477637, + -0.000169686983664989, + 0.00036771798854165044, + 0.0003059539182138257, + -0.00020627705332200324, + -0.00036208738952237034, + 3.672879517702098e-05, + 0.00034220774450222953, + 0.00010824229568279674, + -0.0002638742511127665, + -0.00020646049212939892, + 0.00015235556237131378, + 0.0002485555889307161, + -3.4446399696334056e-05, + -0.0002373927755966462, + -6.68894489448072e-05, + 0.00018525861849561634, + 0.00013615772393127433, + -0.00010975678946015862, + -0.0001668593951537981, + 2.9449694220786016e-05, + 0.000160996958416934, + 3.985676013463422e-05, + -0.00012703637978599055, + -8.758109455240882e-05, + 7.701671223906466e-05, + 0.00010934295961065633, + -2.3537583858735534e-05, + -0.00010654680234804103, + -2.2760842363500776e-05, + 8.492850576208972e-05, + 5.482811512633073e-05, + -5.2557793316981136e-05, + -6.980212930301614e-05, + 1.7799995928150835e-05, + 6.866855793659725e-05, + 1.2352369776983332e-05, + -5.5242609990036297e-05, + -3.332549671812382e-05, + 3.48167368145463e-05, + 4.3312083775684775e-05, + -1.2813427009008755e-05, + -4.300164683848037e-05, + -6.290426820769221e-06, + 3.488121193630334e-05, + 1.9612964695179654e-05, + -2.2340476276265842e-05, + -2.605572654342193e-05, + 8.803876391769033e-06, + 2.6097190687324947e-05, + 2.942823993601164e-06, + -2.1323121189248238e-05, + -1.1141109862235736e-05, + 1.384911600179759e-05, + 1.5151846270420658e-05, + -5.777021855417445e-06, + -1.5302661405843096e-05, + -1.2131040701919644e-06, + 1.2580315824988063e-05, + 6.08570579554361e-06, + -8.268279687162828e-06, + -8.487457353378934e-06, + 3.6168030625091555e-06, + 8.638487741992554e-06, + 3.9537838003464527e-07, + -7.136437289370632e-06, + -3.1823115172240006e-06, + 4.735928837508751e-06, + 4.560474161361854e-06, + -2.1552409835570375e-06, + -4.67416428974752e-06, + -5.710332117813373e-08, + 3.874557757285263e-06, + 1.584294923048922e-06, + -2.5901102398864627e-06, + -2.3383632432586653e-06, + 1.2177127242482222e-06, + 2.411048558044751e-06, + -5.1597883890098977e-08, + -2.0017613173009023e-06, + -7.457595423462837e-07, + 1.3443762821484977e-06, + 1.1367035368094803e-06, + -6.487149967509684e-07, + -1.1774482315206333e-06, + 6.484665244473348e-08, + 9.768751569312674e-07, + 3.289904476631279e-07, + -6.57027401634456e-07, + -5.194446675053159e-07, + 3.2331554237412893e-07, + 5.394825425816262e-07, + -4.7879991080474856e-08, + -4.4590734890745537e-07, + -1.3442648724143923e-07, + 2.991551330257851e-07, + 2.206254163789919e-07, + -1.4909601103428738e-07, + -2.2906722734906596e-07, + 2.8016936501450825e-08, + 1.8782810133829837e-07, + 5.005664742272042e-08, + -1.2502708392121218e-07, + -8.57196191379699e-08, + 6.260407964576534e-08, + 8.856657748781657e-08, + -1.377054465796504e-08, + -7.159335536018259e-08, + -1.6591644718097893e-08, + 4.6919535092840925e-08, + 2.9753058833130447e-08, + -2.3354970601865938e-08, + -3.035967368747228e-08, + 5.699370544081556e-09, + 2.395097795739618e-08, + 4.7187712854981555e-09, + -1.526391589543894e-08, + -8.879743628810473e-09, + 7.432242319481839e-09, + 8.824396176308693e-09, + -1.9223082282338022e-09, + -6.6709623778697e-09, + -1.0805250278628995e-09, + 4.040333248197303e-09, + 2.1241568247563375e-09, + -1.8677373375643354e-09, + -1.9950759509148982e-09, + 4.841122743309274e-10, + 1.3876779389493582e-09, + 1.7447498568802657e-10, + -7.560570063834295e-10, + -3.4623720597839356e-10, + 3.0701859865581745e-10, + 2.809141662660533e-10, + -7.018946259546669e-11, + -1.5601965248971414e-10, + -1.3061824926945334e-11, + 5.960745176448492e-11, + 1.915913091651547e-11, + -1.1994395146732941e-11, ]; diff --git a/playback/src/normaliser.rs b/playback/src/normaliser.rs index 0807e4492..04b7ed32b 100644 --- a/playback/src/normaliser.rs +++ b/playback/src/normaliser.rs @@ -7,32 +7,6 @@ use crate::{ ratio_to_db, PCM_AT_0DBFS, }; -struct NoNormalisation; - -impl NoNormalisation { - fn normalise(mut samples: Vec, volume: f64) -> Vec { - if volume < 1.0 { - samples.iter_mut().for_each(|sample| *sample *= volume); - } - - samples - } -} - -struct BasicNormalisation; - -impl BasicNormalisation { - fn normalise(mut samples: Vec, volume: f64, factor: f64) -> Vec { - 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, volume: f64, factor: f64) -> Vec { + fn normalise(&mut self, mut samples: Vec, volume: f64, factor: f64) -> Vec { 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, diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs index 7642e44fe..17f141c35 100644 --- a/playback/src/resampler.rs +++ b/playback/src/resampler.rs @@ -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, -}; +use crate::{config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE}; -struct DelayLine { - buffer: VecDeque, +struct ConvolutionFilter { + coefficients: Vec, coefficients_length: usize, + delay_line: VecDeque, } -impl DelayLine { - fn new(coefficients_length: usize) -> DelayLine { +impl ConvolutionFilter { + fn new(coefficients: Vec) -> Self { + let coefficients_length = coefficients.len(); + let delay_line = VecDeque::with_capacity(coefficients_length); + Self { - buffer: VecDeque::with_capacity(coefficients_length), + coefficients, coefficients_length, + delay_line, } } - fn push(&mut self, sample: f64) { - self.buffer.push_back(sample); + 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 + }); - while self.buffer.len() > self.coefficients_length { - self.buffer.pop_front(); - } - } + self.delay_line.pop_front(); - fn clear(&mut self) { - self.buffer.clear(); + output_sample } -} -impl<'a> IntoIterator for &'a DelayLine { - type Item = &'a f64; - type IntoIter = vec_deque::Iter<'a, f64>; + fn convolute(&mut self, sample: f64) -> f64 { + self.delay_line.push_back(sample); - fn into_iter(self) -> Self::IntoIter { - self.buffer.iter() + if self.delay_line.len() == self.coefficients_length { + self.get_convoluted_sample() + } else { + 0.0 + } } -} -struct ConvolutionFilter { - coefficients: Vec, - delay_line: DelayLine, -} + fn drain(&mut self) -> Vec { + let delay_line_len = self.delay_line.len(); + let mut output = Vec::with_capacity(delay_line_len); -impl ConvolutionFilter { - fn new(coefficients: Vec) -> Self { - let delay_line = DelayLine::new(coefficients.len()); - - Self { - coefficients, - delay_line, + for _ in 0..delay_line_len { + output.push(self.get_convoluted_sample()); } - } - fn convolute(&mut self, sample: f64) -> f64 { - self.delay_line.push(sample); - - // Temporal convolution - self.coefficients - .iter() - .zip(&self.delay_line) - .fold(0.0, |acc, (coefficient, delay_line_sample)| { - acc + coefficient * delay_line_sample - }) + output } fn clear(&mut self) { @@ -81,6 +65,7 @@ impl ConvolutionFilter { struct MonoSincResampler { interpolator: ConvolutionFilter, input_buffer: Vec, + 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> { + fn drain(&mut self) -> (Option>, 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>, 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), } @@ -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) { 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> { + 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) -> Option> { 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>, 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 { // Re-interleave the resampled channels. let mut output = Vec::with_capacity(left_samples.len() + right_samples.len()); diff --git a/playback/src/sample_pipeline.rs b/playback/src/sample_pipeline.rs index 53bbc29f3..b8e438ef4 100644 --- a/playback/src/sample_pipeline.rs +++ b/playback/src/sample_pipeline.rs @@ -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, + volume_getter: Box, + ) -> 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, +} + +impl Bypass { + fn new(config: &PlayerConfig, sink: Box) -> 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, } -impl SamplePipeline { - pub fn new( +impl Pipeline { + fn new( config: &PlayerConfig, sink: Box, volume_getter: Box, @@ -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(()) From 96f5a4d54dd42c21aaa1787d39c562d531852fdc Mon Sep 17 00:00:00 2001 From: JasonLG1979 Date: Mon, 4 Sep 2023 10:40:22 -0500 Subject: [PATCH 53/53] Fix clippy lint and add back ALSA padding. --- examples/play.rs | 2 +- playback/src/audio_backend/alsa.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/play.rs b/examples/play.rs index a3196fcfd..fb615ac2e 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -41,7 +41,7 @@ async fn main() { exit(1); } - let mut player = Player::new(player_config, session, Box::new(NoOpVolume), move || { + let player = Player::new(player_config, session, Box::new(NoOpVolume), move || { backend(None, audio_format, sample_rate) }); diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 45bb8375b..3df47da25 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -52,6 +52,9 @@ enum AlsaError { #[error(" Device {device} May be Invalid, Busy, or Already in Use, {e}")] PcmSetUp { device: String, e: alsa::Error }, + #[error(" Failed to Drain PCM Buffer, {0}")] + DrainFailure(alsa::Error), + #[error(" {0}")] OnWrite(alsa::Error), @@ -236,8 +239,14 @@ impl Sink for AlsaSink { } fn stop(&mut self) -> SinkResult<()> { - self.period_buffer.clear(); - self.pcm = None; + // Zero fill the remainder of the period buffer and + // write any leftover data before draining the actual PCM buffer. + self.period_buffer.resize(self.period_buffer.capacity(), 0); + self.write_buf()?; + + if let Some(pcm) = self.pcm.take() { + pcm.drain().map_err(AlsaError::DrainFailure)?; + } Ok(()) }