mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
New filter coeff's and only have one quality setting.
This commit is contained in:
parent
e31293cc10
commit
3928d0775f
5 changed files with 1427 additions and 1345 deletions
|
@ -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
|
@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
37
src/main.rs
37
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,
|
||||
|
|
Loading…
Reference in a new issue