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::{
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<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)]
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<f64>,
pub low_coefficients: Vec<f64>,
}
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<f64> {
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");
Hz44100 => None,
Hz48000 => Some(HZ48000_RESAMPLE_FACTOR_RECIPROCAL),
Hz88200 => Some(HZ88200_RESAMPLE_FACTOR_RECIPROCAL),
Hz96000 => Some(HZ96000_RESAMPLE_FACTOR_RECIPROCAL),
}
}
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(),
},
pub fn get_interpolation_output_size(&self) -> Option<usize> {
use SampleRate::*;
match self {
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<Vec<f64>> {
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<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 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(),

File diff suppressed because it is too large Load diff

View file

@ -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();
let delay_line_latency = (coefficients.len() as f64 * resample_factor_reciprocal) as u64;
Self {
interpolator: ConvolutionFilter::new(
interpolation_quality
.get_interpolation_coefficients(coefficients, spec.resample_factor_reciprocal),
),
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,
),
}
}
};

View file

@ -23,8 +23,7 @@ impl SamplePipeline {
sink: Box<dyn Sink>,
volume_getter: Box<dyn VolumeGetter>,
) -> 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);

View file

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