1use crate::scaling::MeterScale;
4use ratatui::widgets::Block;
5
6pub enum MeterInput {
8 Mono(f32),
9 Stereo(f32, f32),
10}
11
12#[derive(Debug, Clone, PartialEq)]
21pub struct Meter<'a> {
22 pub(crate) block: Option<Block<'a>>,
23 pub(crate) ratio: [f32; 2],
24 pub(crate) channels: usize,
25 pub(crate) show_labels: bool,
26 pub(crate) show_scale: bool,
27}
28
29impl<'a> Meter<'a> {
30 #[must_use = "method moves the value of self and returns the modified value"]
35 pub fn block(mut self, block: Block<'a>) -> Self {
36 self.block = Some(block);
37 self
38 }
39
40 pub fn mono() -> Self {
42 Self {
43 block: None,
44 ratio: [0.0; 2],
45 channels: 1,
46 show_labels: true,
47 show_scale: true,
48 }
49 }
50
51 pub fn stereo() -> Self {
53 Self {
54 block: None,
55 ratio: [0.0; 2],
56 channels: 2,
57 show_labels: true,
58 show_scale: true,
59 }
60 }
61
62 pub fn channels(&self) -> usize {
64 self.channels
65 }
66
67 #[must_use = "method moves the value of self and returns the modified value"]
69 pub fn show_labels(mut self, show: bool) -> Self {
70 self.show_labels = show;
71 self
72 }
73
74 #[must_use = "method moves the value of self and returns the modified value"]
76 pub fn show_scale(mut self, show: bool) -> Self {
77 self.show_scale = show;
78 self
79 }
80
81 #[must_use = "method moves the value of self and returns the modified value"]
84 pub fn db(mut self, input: MeterInput) -> Self {
85 match input {
86 MeterInput::Mono(dbfs) => {
87 self.ratio[0] = MeterScale::db_to_ratio(dbfs);
88 self.ratio[1] = 0.0;
89 }
90 MeterInput::Stereo(left_dbfs, right_dbfs) => {
91 self.ratio[0] = MeterScale::db_to_ratio(left_dbfs);
92 self.ratio[1] = MeterScale::db_to_ratio(right_dbfs);
93 }
94 }
95 self
96 }
97
98 #[must_use = "method moves the value of self and returns the modified value"]
101 pub fn sample_amplitude(mut self, input: MeterInput) -> Self {
102 match input {
103 MeterInput::Mono(ampl) => {
104 assert!(
105 (0.0..=1.0).contains(&l),
106 "Ratio should be between 0 and 1 inclusively."
107 );
108 self.ratio[0] = MeterScale::sample_to_ratio(ampl);
109 self.ratio[1] = 0.0;
110 }
111 MeterInput::Stereo(left_ampl, right_ampl) => {
112 assert!(
113 (0.0..=1.0).contains(&left_ampl) && (0.0..=1.0).contains(&right_ampl),
114 "Ratio should be between 0 and 1 inclusively."
115 );
116 self.ratio[0] = MeterScale::sample_to_ratio(left_ampl);
117 self.ratio[1] = MeterScale::sample_to_ratio(right_ampl);
118 }
119 }
120
121 self
122 }
123
124 #[must_use = "method moves the value of self and returns the modified value"]
132 pub fn ratio(mut self, input: MeterInput) -> Self {
133 match input {
134 MeterInput::Mono(ratio) => {
135 assert!(
136 (0.0..=1.0).contains(&ratio),
137 "Ratio should be between 0 and 1 inclusively."
138 );
139 self.ratio[0] = ratio;
140 self.ratio[1] = 0.0;
141 }
142 MeterInput::Stereo(left_ratio, right_ratio) => {
143 assert!(
144 (0.0..=1.0).contains(&left_ratio) && (0.0..=1.0).contains(&right_ratio),
145 "Ratio should be between 0 and 1 inclusively."
146 );
147 self.ratio[0] = left_ratio;
148 self.ratio[1] = right_ratio;
149 }
150 }
151 self
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn meter_db_zero() {
161 let meter = Meter::mono().db(MeterInput::Mono(0.0));
162 assert_eq!(meter.ratio[0], 1.0)
163 }
164
165 #[test]
166 fn meter_db_upper_bound() {
167 let meter = Meter::mono().db(MeterInput::Mono(0.1));
168 assert_eq!(meter.ratio[0], 1.0)
169 }
170
171 #[test]
172 fn meter_db_lower_bound() {
173 let meter = Meter::mono().db(MeterInput::Mono(-130.0));
174 assert_eq!(meter.ratio[0], 0.0);
175 }
176
177 #[test]
178 fn meter_stereo_db() {
179 let meter = Meter::stereo().db(MeterInput::Stereo(0.0, 0.0));
180 assert_eq!(meter.ratio[0], 1.0);
181 assert_eq!(meter.ratio[1], 1.0);
182 }
183
184 #[test]
185 fn meter_stereo_sample_amplitudes() {
186 let meter = Meter::stereo().sample_amplitude(MeterInput::Stereo(1.0, 0.0));
187 assert_eq!(meter.ratio[0], 1.0);
188 assert_eq!(meter.ratio[1], 0.0);
189 }
190
191 #[test]
192 #[should_panic = "Ratio should be between 0 and 1 inclusively"]
193 fn meter_invalid_ratio_upper_bound() {
194 let _ = Meter::mono().ratio(MeterInput::Mono(1.1));
195 }
196
197 #[test]
198 #[should_panic = "Ratio should be between 0 and 1 inclusively"]
199 fn meter_invalid_ratio_lower_bound() {
200 let _ = Meter::mono().ratio(MeterInput::Mono(-0.5));
201 }
202}