1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum AnalysisDomain {
5 Waveform,
6 Spectrum,
7}
8
9#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
11#[derive(Clone, Debug, PartialEq)]
12pub struct AudioAnalyser {
13 node: web_sys::AnalyserNode,
14}
15
16#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
17impl AudioAnalyser {
18 pub(crate) fn new(node: web_sys::AnalyserNode) -> Self {
19 Self { node }
20 }
21
22 pub fn read(&self, domain: AnalysisDomain) -> Vec<f32> {
23 match domain {
24 AnalysisDomain::Waveform => {
25 let mut samples = vec![0_u8; self.node.fft_size() as usize];
26 self.node.get_byte_time_domain_data(&mut samples);
27 samples
28 .into_iter()
29 .map(|sample| ((sample as f32 - 128.0) / 128.0).clamp(-1.0, 1.0))
30 .collect()
31 }
32 AnalysisDomain::Spectrum => {
33 let samples = js_sys::Uint8Array::new_with_length(self.node.frequency_bin_count());
34 self.node.get_byte_frequency_data_with_u8_array(&samples);
35 let mut values = vec![0_u8; samples.length() as usize];
36 samples.copy_to(&mut values);
37 values
38 .into_iter()
39 .map(|sample| sample as f32 / 255.0)
40 .collect()
41 }
42 }
43 }
44
45 pub fn level(&self) -> f32 {
46 let waveform = self.read(AnalysisDomain::Waveform);
47 if waveform.is_empty() {
48 return 0.0;
49 }
50 let mean_square =
51 waveform.iter().map(|sample| sample * sample).sum::<f32>() / waveform.len() as f32;
52 mean_square.sqrt().clamp(0.0, 1.0)
53 }
54
55 pub(crate) fn node(&self) -> &web_sys::AnalyserNode {
56 &self.node
57 }
58}
59
60#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
61#[derive(Clone, Debug, PartialEq)]
62pub struct AudioAnalyser {
63 _private: (),
64}
65
66#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
67impl AudioAnalyser {
68 pub fn read(&self, _domain: AnalysisDomain) -> Vec<f32> {
69 Vec::new()
70 }
71
72 pub fn level(&self) -> f32 {
73 0.0
74 }
75}
76
77pub fn downsample_peaks(peaks: &[u8], buckets: usize) -> Vec<u8> {
80 let bucket_count = peaks.len().min(buckets);
81 if bucket_count == 0 {
82 return Vec::new();
83 }
84
85 (0..bucket_count)
86 .map(|index| {
87 let start = index * peaks.len() / bucket_count;
88 let end = (index + 1) * peaks.len() / bucket_count;
89 peaks[start..end].iter().copied().max().unwrap_or(0)
90 })
91 .collect()
92}
93
94pub fn peak_amplitude(samples: &[u8]) -> u8 {
97 let distance = samples
98 .iter()
99 .map(|sample| (*sample as i16 - 128).unsigned_abs())
100 .max()
101 .unwrap_or(0);
102
103 ((u32::from(distance) * 255 + 64) / 128).min(255) as u8
104}
105
106#[derive(Clone, Copy, Debug, PartialEq)]
108pub struct WaveformSelection {
109 start: f64,
110 end: f64,
111}
112
113impl WaveformSelection {
114 pub fn new(start: f64, end: f64) -> Self {
115 let start = if start.is_finite() {
116 start.clamp(0.0, 1.0)
117 } else {
118 0.0
119 };
120 let end = if end.is_finite() {
121 end.clamp(0.0, 1.0)
122 } else {
123 1.0
124 };
125
126 Self {
127 start: start.min(end),
128 end: start.max(end),
129 }
130 }
131
132 pub fn start(self) -> f64 {
133 self.start
134 }
135
136 pub fn end(self) -> f64 {
137 self.end
138 }
139
140 pub fn with_start(self, start: f64) -> Self {
141 let start = if start.is_finite() {
142 start.clamp(0.0, self.end)
143 } else {
144 self.start
145 };
146 Self {
147 start,
148 end: self.end,
149 }
150 }
151
152 pub fn with_end(self, end: f64) -> Self {
153 let end = if end.is_finite() {
154 end.clamp(self.start, 1.0)
155 } else {
156 self.end
157 };
158 Self {
159 start: self.start,
160 end,
161 }
162 }
163}
164
165pub fn trim_interleaved_pcm<T: Clone>(
167 samples: &[T],
168 channels: usize,
169 selection: WaveformSelection,
170) -> Vec<T> {
171 if channels == 0 {
172 return Vec::new();
173 }
174 if selection.start() == selection.end() {
175 return Vec::new();
176 }
177
178 let frame_count = samples.len() / channels;
179 let first_frame = (selection.start() * frame_count as f64).floor() as usize;
180 let end_frame = (selection.end() * frame_count as f64).ceil() as usize;
181 samples[first_frame * channels..end_frame.min(frame_count) * channels].to_vec()
182}