mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
201 lines
5 KiB
Rust
201 lines
5 KiB
Rust
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)]
|
|
pub enum Bitrate {
|
|
Bitrate96,
|
|
Bitrate160,
|
|
Bitrate320,
|
|
}
|
|
|
|
impl FromStr for Bitrate {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"96" => Ok(Self::Bitrate96),
|
|
"160" => Ok(Self::Bitrate160),
|
|
"320" => Ok(Self::Bitrate320),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for Bitrate {
|
|
fn default() -> Self {
|
|
Self::Bitrate160
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
|
pub enum AudioFormat {
|
|
F64,
|
|
F32,
|
|
S32,
|
|
S24,
|
|
S24_3,
|
|
S16,
|
|
}
|
|
|
|
impl FromStr for AudioFormat {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_uppercase().as_ref() {
|
|
"F64" => Ok(Self::F64),
|
|
"F32" => Ok(Self::F32),
|
|
"S32" => Ok(Self::S32),
|
|
"S24" => Ok(Self::S24),
|
|
"S24_3" => Ok(Self::S24_3),
|
|
"S16" => Ok(Self::S16),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for AudioFormat {
|
|
fn default() -> Self {
|
|
Self::S16
|
|
}
|
|
}
|
|
|
|
impl AudioFormat {
|
|
// not used by all backends
|
|
#[allow(dead_code)]
|
|
pub fn size(&self) -> usize {
|
|
match self {
|
|
Self::F64 => mem::size_of::<f64>(),
|
|
Self::F32 => mem::size_of::<f32>(),
|
|
Self::S24_3 => mem::size_of::<i24>(),
|
|
Self::S16 => mem::size_of::<i16>(),
|
|
_ => mem::size_of::<i32>(), // S32 and S24 are both stored in i32
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum NormalisationType {
|
|
Album,
|
|
Track,
|
|
Auto,
|
|
}
|
|
|
|
impl FromStr for NormalisationType {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_lowercase().as_ref() {
|
|
"album" => Ok(Self::Album),
|
|
"track" => Ok(Self::Track),
|
|
"auto" => Ok(Self::Auto),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for NormalisationType {
|
|
fn default() -> Self {
|
|
Self::Auto
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum NormalisationMethod {
|
|
Basic,
|
|
Dynamic,
|
|
}
|
|
|
|
impl FromStr for NormalisationMethod {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_lowercase().as_ref() {
|
|
"basic" => Ok(Self::Basic),
|
|
"dynamic" => Ok(Self::Dynamic),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for NormalisationMethod {
|
|
fn default() -> Self {
|
|
Self::Dynamic
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PlayerConfig {
|
|
pub bitrate: Bitrate,
|
|
pub gapless: bool,
|
|
pub passthrough: bool,
|
|
|
|
pub normalisation: bool,
|
|
pub normalisation_type: NormalisationType,
|
|
pub normalisation_method: NormalisationMethod,
|
|
pub normalisation_pregain_db: f64,
|
|
pub normalisation_threshold_dbfs: f64,
|
|
pub normalisation_attack_cf: f64,
|
|
pub normalisation_release_cf: f64,
|
|
pub normalisation_knee_db: f64,
|
|
|
|
// pass function pointers so they can be lazily instantiated *after* spawning a thread
|
|
// (thereby circumventing Send bounds that they might not satisfy)
|
|
pub ditherer: Option<DithererBuilder>,
|
|
}
|
|
|
|
impl Default for PlayerConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
bitrate: Bitrate::default(),
|
|
gapless: true,
|
|
normalisation: false,
|
|
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)),
|
|
normalisation_knee_db: 5.0,
|
|
passthrough: false,
|
|
ditherer: Some(mk_ditherer::<TriangularDitherer>),
|
|
}
|
|
}
|
|
}
|
|
|
|
// fields are intended for volume control range in dB
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum VolumeCtrl {
|
|
Cubic(f64),
|
|
Fixed,
|
|
Linear,
|
|
Log(f64),
|
|
}
|
|
|
|
impl FromStr for VolumeCtrl {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Self::from_str_with_range(s, Self::DEFAULT_DB_RANGE)
|
|
}
|
|
}
|
|
|
|
impl Default for VolumeCtrl {
|
|
fn default() -> VolumeCtrl {
|
|
VolumeCtrl::Log(Self::DEFAULT_DB_RANGE)
|
|
}
|
|
}
|
|
|
|
impl VolumeCtrl {
|
|
pub const MAX_VOLUME: u16 = u16::MAX;
|
|
|
|
// Taken from: https://www.dr-lex.be/info-stuff/volumecontrols.html
|
|
pub const DEFAULT_DB_RANGE: f64 = 60.0;
|
|
|
|
pub fn from_str_with_range(s: &str, db_range: f64) -> Result<Self, <Self as FromStr>::Err> {
|
|
use self::VolumeCtrl::*;
|
|
match s.to_lowercase().as_ref() {
|
|
"cubic" => Ok(Cubic(db_range)),
|
|
"fixed" => Ok(Fixed),
|
|
"linear" => Ok(Linear),
|
|
"log" => Ok(Log(db_range)),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|