New filter coeff's and only have one quality setting.

This commit is contained in:
JasonLG1979 2023-07-04 18:34:57 -05:00
parent e31293cc10
commit 3928d0775f
5 changed files with 1427 additions and 1345 deletions

View file

@ -4,9 +4,7 @@ pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer};
use crate::{ use crate::{
convert::i24, convert::i24,
filter_coefficients::{ filter_coefficients::{HZ48000_COEFFICIENTS, HZ88200_COEFFICIENTS, HZ96000_COEFFICIENTS},
HZ48000_HIGH, HZ48000_LOW, HZ88200_HIGH, HZ88200_LOW, HZ96000_HIGH, HZ96000_LOW,
},
RESAMPLER_INPUT_SIZE, SAMPLE_RATE, RESAMPLER_INPUT_SIZE, SAMPLE_RATE,
}; };
@ -33,69 +31,6 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize = const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize =
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize; (RESAMPLER_INPUT_SIZE as f64 * (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<Self, Self::Err> {
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<f64>,
resample_factor_reciprocal: f64,
) -> Vec<f64> {
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)] #[derive(Clone, Copy, Debug, Default)]
pub enum SampleRate { pub enum SampleRate {
#[default] #[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<f64>,
pub low_coefficients: Vec<f64>,
}
impl SampleRate { impl SampleRate {
pub fn as_u32(&self) -> u32 { pub fn as_u32(&self) -> u32 {
use SampleRate::*; use SampleRate::*;
@ -207,41 +134,73 @@ impl SampleRate {
} }
} }
pub fn get_resample_spec(&self) -> ResampleSpec { pub fn get_resample_factor_reciprocal(&self) -> Option<f64> {
use SampleRate::*; use SampleRate::*;
match self { match self {
// Dummy values to satisfy Hz44100 => None,
// the match statement. Hz48000 => Some(HZ48000_RESAMPLE_FACTOR_RECIPROCAL),
// 44.1kHz will be bypassed. Hz88200 => Some(HZ88200_RESAMPLE_FACTOR_RECIPROCAL),
Hz44100 => { Hz96000 => Some(HZ96000_RESAMPLE_FACTOR_RECIPROCAL),
warn!("Resampling 44.1kHz to 44.1kHz is just a really CPU intensive no-op, you should not be doing it"); }
}
ResampleSpec { pub fn get_interpolation_output_size(&self) -> Option<usize> {
resample_factor_reciprocal: 1.0, use SampleRate::*;
interpolation_output_size: RESAMPLER_INPUT_SIZE,
high_coefficients: vec![], match self {
low_coefficients: vec![], Hz44100 => None,
Hz48000 => Some(HZ48000_INTERPOLATION_OUTPUT_SIZE),
Hz88200 => Some(HZ88200_INTERPOLATION_OUTPUT_SIZE),
Hz96000 => Some(HZ96000_INTERPOLATION_OUTPUT_SIZE),
} }
} }
Hz48000 => ResampleSpec {
resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL, pub fn get_interpolation_coefficients(&self) -> Option<Vec<f64>> {
interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE, use SampleRate::*;
high_coefficients: HZ48000_HIGH.to_vec(),
low_coefficients: HZ48000_LOW.to_vec(), match self {
}, Hz44100 => None,
Hz88200 => ResampleSpec { Hz48000 => Some(Self::calculate_interpolation_coefficients(
resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL, HZ48000_COEFFICIENTS.to_vec(),
interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE, HZ48000_RESAMPLE_FACTOR_RECIPROCAL,
high_coefficients: HZ88200_HIGH.to_vec(), )),
low_coefficients: HZ88200_LOW.to_vec(), Hz88200 => Some(Self::calculate_interpolation_coefficients(
}, HZ88200_COEFFICIENTS.to_vec(),
Hz96000 => ResampleSpec { HZ88200_RESAMPLE_FACTOR_RECIPROCAL,
resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL, )),
interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE, Hz96000 => Some(Self::calculate_interpolation_coefficients(
high_coefficients: HZ96000_HIGH.to_vec(), HZ96000_COEFFICIENTS.to_vec(),
low_coefficients: HZ96000_LOW.to_vec(), HZ96000_RESAMPLE_FACTOR_RECIPROCAL,
}, )),
}
}
fn calculate_interpolation_coefficients(
mut coefficients: Vec<f64>,
resample_factor_reciprocal: f64,
) -> Vec<f64> {
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 gapless: bool,
pub passthrough: bool, pub passthrough: bool,
pub interpolation_quality: InterpolationQuality,
pub sample_rate: SampleRate, pub sample_rate: SampleRate,
pub normalisation: bool, pub normalisation: bool,
@ -384,7 +342,6 @@ impl Default for PlayerConfig {
bitrate: Bitrate::default(), bitrate: Bitrate::default(),
gapless: true, gapless: true,
normalisation: false, normalisation: false,
interpolation_quality: InterpolationQuality::default(),
sample_rate: SampleRate::default(), sample_rate: SampleRate::default(),
normalisation_type: NormalisationType::default(), normalisation_type: NormalisationType::default(),
normalisation_method: NormalisationMethod::default(), normalisation_method: NormalisationMethod::default(),

File diff suppressed because it is too large Load diff

View file

@ -7,9 +7,8 @@ use std::{
}; };
use crate::{ use crate::{
config::{InterpolationQuality, SampleRate}, config::SampleRate, player::PLAYER_COUNTER, RESAMPLER_INPUT_SIZE,
player::PLAYER_COUNTER, SAMPLE_RATE as SOURCE_SAMPLE_RATE,
RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE,
}; };
struct DelayLine { struct DelayLine {
@ -88,27 +87,27 @@ struct MonoSincResampler {
} }
impl MonoSincResampler { impl MonoSincResampler {
fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self { fn new(sample_rate: SampleRate) -> Self {
let spec = sample_rate.get_resample_spec(); let coefficients = sample_rate
.get_interpolation_coefficients()
.unwrap_or_default();
let coefficients = match interpolation_quality { let resample_factor_reciprocal = sample_rate
InterpolationQuality::Low => spec.low_coefficients, .get_resample_factor_reciprocal()
InterpolationQuality::High => spec.high_coefficients, .unwrap_or_default();
};
let delay_line_latency = let interpolation_output_size = sample_rate
(coefficients.len() as f64 * spec.resample_factor_reciprocal) as u64; .get_interpolation_output_size()
.unwrap_or_default();
let delay_line_latency = (coefficients.len() as f64 * resample_factor_reciprocal) as u64;
Self { Self {
interpolator: ConvolutionFilter::new( interpolator: ConvolutionFilter::new(coefficients),
interpolation_quality
.get_interpolation_coefficients(coefficients, spec.resample_factor_reciprocal),
),
input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize), input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
resample_factor_reciprocal: spec.resample_factor_reciprocal, resample_factor_reciprocal,
delay_line_latency, delay_line_latency,
interpolation_output_size: spec.interpolation_output_size, interpolation_output_size,
} }
} }
@ -281,7 +280,7 @@ pub struct StereoInterleavedResampler {
} }
impl 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}"); debug!("Sample Rate: {sample_rate}");
let resampler = match sample_rate { let resampler = match sample_rate {
@ -292,7 +291,7 @@ impl StereoInterleavedResampler {
Resampler::Bypass Resampler::Bypass
} }
_ => { _ => {
debug!("Interpolation Quality: {interpolation_quality}"); debug!("Interpolation Type: Windowed Sinc");
// The player increments the player id when it gets it... // The player increments the player id when it gets it...
let player_id = PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1); let player_id = PLAYER_COUNTER.load(Ordering::SeqCst).saturating_sub(1);
@ -300,12 +299,15 @@ impl StereoInterleavedResampler {
let left_thread_name = format!("resampler:{player_id}:left"); let left_thread_name = format!("resampler:{player_id}:left");
let right_thread_name = format!("resampler:{player_id}:right"); 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 { Resampler::Worker {
left_resampler: ResampleWorker::new(left, left_thread_name), left_resampler: ResampleWorker::new(
right_resampler: ResampleWorker::new(right, right_thread_name), MonoSincResampler::new(sample_rate),
left_thread_name,
),
right_resampler: ResampleWorker::new(
MonoSincResampler::new(sample_rate),
right_thread_name,
),
} }
} }
}; };

View file

@ -23,8 +23,7 @@ impl SamplePipeline {
sink: Box<dyn Sink>, sink: Box<dyn Sink>,
volume_getter: Box<dyn VolumeGetter>, volume_getter: Box<dyn VolumeGetter>,
) -> Self { ) -> Self {
let resampler = let resampler = StereoInterleavedResampler::new(config.sample_rate);
StereoInterleavedResampler::new(config.sample_rate, config.interpolation_quality);
let normaliser = Normaliser::new(config, volume_getter); let normaliser = Normaliser::new(config, volume_getter);
let converter = Converter::new(config.ditherer); let converter = Converter::new(config.ditherer);

View file

@ -24,8 +24,8 @@ use librespot::{
playback::{ playback::{
audio_backend::{self, SinkBuilder, BACKENDS}, audio_backend::{self, SinkBuilder, BACKENDS},
config::{ config::{
AudioFormat, Bitrate, InterpolationQuality, NormalisationMethod, NormalisationType, AudioFormat, Bitrate, NormalisationMethod, NormalisationType, PlayerConfig, SampleRate,
PlayerConfig, SampleRate, VolumeCtrl, VolumeCtrl,
}, },
dither, dither,
mixer::{self, MixerConfig, MixerFn}, mixer::{self, MixerConfig, MixerFn},
@ -240,7 +240,6 @@ fn get_setup() -> Setup {
const VOLUME_RANGE: &str = "volume-range"; const VOLUME_RANGE: &str = "volume-range";
const ZEROCONF_PORT: &str = "zeroconf-port"; const ZEROCONF_PORT: &str = "zeroconf-port";
const ZEROCONF_INTERFACE: &str = "zeroconf-interface"; const ZEROCONF_INTERFACE: &str = "zeroconf-interface";
const INTERPOLATION_QUALITY: &str = "interpolation-quality";
const SAMPLE_RATE: &str = "sample-rate"; const SAMPLE_RATE: &str = "sample-rate";
// Mostly arbitrary. // Mostly arbitrary.
@ -579,11 +578,6 @@ fn get_setup() -> Setup {
ZEROCONF_INTERFACE, ZEROCONF_INTERFACE,
"Comma-separated interface IP addresses on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD.", "Comma-separated interface IP addresses on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD.",
"IP" "IP"
).optopt(
"",
INTERPOLATION_QUALITY,
"Interpolation Quality to use if Resampling {Low|High}. Defaults to High.",
"QUALITY"
).optopt( ).optopt(
"", "",
SAMPLE_RATE, SAMPLE_RATE,
@ -800,32 +794,6 @@ fn get_setup() -> Setup {
}) })
.unwrap_or_default(); .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) let format = opt_str(FORMAT)
.as_deref() .as_deref()
.map(|format| { .map(|format| {
@ -1671,7 +1639,6 @@ fn get_setup() -> Setup {
bitrate, bitrate,
gapless, gapless,
passthrough, passthrough,
interpolation_quality,
sample_rate, sample_rate,
normalisation, normalisation,
normalisation_type, normalisation_type,