1use std::{mem, path::PathBuf, str::FromStr, time::Duration};
2
3pub use crate::dither::{DithererBuilder, TriangularDitherer, mk_ditherer};
4use crate::{convert::i24, player::duration_to_coefficient};
5
6#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Default)]
7pub enum Bitrate {
8 Bitrate96,
9 #[default]
10 Bitrate160,
11 Bitrate320,
12}
13
14impl FromStr for Bitrate {
15 type Err = ();
16 fn from_str(s: &str) -> Result<Self, Self::Err> {
17 match s {
18 "96" => Ok(Self::Bitrate96),
19 "160" => Ok(Self::Bitrate160),
20 "320" => Ok(Self::Bitrate320),
21 _ => Err(()),
22 }
23 }
24}
25
26#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Default)]
27pub enum AudioFormat {
28 F64,
29 F32,
30 S32,
31 S24,
32 S24_3,
33 #[default]
34 S16,
35}
36
37impl FromStr for AudioFormat {
38 type Err = ();
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 match s.to_uppercase().as_ref() {
41 "F64" => Ok(Self::F64),
42 "F32" => Ok(Self::F32),
43 "S32" => Ok(Self::S32),
44 "S24" => Ok(Self::S24),
45 "S24_3" => Ok(Self::S24_3),
46 "S16" => Ok(Self::S16),
47 _ => Err(()),
48 }
49 }
50}
51
52impl AudioFormat {
53 #[allow(dead_code)]
55 pub fn size(&self) -> usize {
56 match self {
57 Self::F64 => mem::size_of::<f64>(),
58 Self::F32 => mem::size_of::<f32>(),
59 Self::S24_3 => mem::size_of::<i24>(),
60 Self::S16 => mem::size_of::<i16>(),
61 _ => mem::size_of::<i32>(), }
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
67pub enum NormalisationType {
68 Album,
69 Track,
70 #[default]
71 Auto,
72}
73
74impl FromStr for NormalisationType {
75 type Err = ();
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
77 match s.to_lowercase().as_ref() {
78 "album" => Ok(Self::Album),
79 "track" => Ok(Self::Track),
80 "auto" => Ok(Self::Auto),
81 _ => Err(()),
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
87pub enum NormalisationMethod {
88 Basic,
89 #[default]
90 Dynamic,
91}
92
93impl FromStr for NormalisationMethod {
94 type Err = ();
95 fn from_str(s: &str) -> Result<Self, Self::Err> {
96 match s.to_lowercase().as_ref() {
97 "basic" => Ok(Self::Basic),
98 "dynamic" => Ok(Self::Dynamic),
99 _ => Err(()),
100 }
101 }
102}
103
104#[derive(Clone)]
105pub struct PlayerConfig {
106 pub bitrate: Bitrate,
107 pub gapless: bool,
108 pub passthrough: bool,
109
110 pub normalisation: bool,
111 pub normalisation_type: NormalisationType,
112 pub normalisation_method: NormalisationMethod,
113 pub normalisation_pregain_db: f64,
114 pub normalisation_threshold_dbfs: f64,
115 pub normalisation_attack_cf: f64,
116 pub normalisation_release_cf: f64,
117 pub normalisation_knee_db: f64,
118
119 pub local_file_directories: Vec<PathBuf>,
120
121 pub ditherer: Option<DithererBuilder>,
124 pub position_update_interval: Option<Duration>,
127}
128
129impl Default for PlayerConfig {
130 fn default() -> Self {
131 Self {
132 bitrate: Bitrate::default(),
133 gapless: true,
134 normalisation: false,
135 normalisation_type: NormalisationType::default(),
136 normalisation_method: NormalisationMethod::default(),
137 normalisation_pregain_db: 0.0,
138 normalisation_threshold_dbfs: -2.0,
139 normalisation_attack_cf: duration_to_coefficient(Duration::from_millis(5)),
140 normalisation_release_cf: duration_to_coefficient(Duration::from_millis(100)),
141 normalisation_knee_db: 5.0,
142 passthrough: false,
143 ditherer: Some(mk_ditherer::<TriangularDitherer>),
144 position_update_interval: None,
145 local_file_directories: Vec::new(),
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug)]
152pub enum VolumeCtrl {
153 Cubic(f64),
154 Fixed,
155 Linear,
156 Log(f64),
157}
158
159impl FromStr for VolumeCtrl {
160 type Err = ();
161 fn from_str(s: &str) -> Result<Self, Self::Err> {
162 Self::from_str_with_range(s, Self::DEFAULT_DB_RANGE)
163 }
164}
165
166impl Default for VolumeCtrl {
167 fn default() -> VolumeCtrl {
168 VolumeCtrl::Log(Self::DEFAULT_DB_RANGE)
169 }
170}
171
172impl VolumeCtrl {
173 pub const MAX_VOLUME: u16 = u16::MAX;
174
175 pub const DEFAULT_DB_RANGE: f64 = 60.0;
177
178 pub fn from_str_with_range(s: &str, db_range: f64) -> Result<Self, <Self as FromStr>::Err> {
179 use self::VolumeCtrl::*;
180 match s.to_lowercase().as_ref() {
181 "cubic" => Ok(Cubic(db_range)),
182 "fixed" => Ok(Fixed),
183 "linear" => Ok(Linear),
184 "log" => Ok(Log(db_range)),
185 _ => Err(()),
186 }
187 }
188}