use alpha2_audio::{Waveform, WaveformSummary};
use wasm_bindgen::prelude::*;
use crate::web::{invalid_input, invalid_state};
#[wasm_bindgen]
pub struct WaveformBuilder {
summary: Option<WaveformSummary>,
channels: usize,
}
#[wasm_bindgen]
impl WaveformBuilder {
#[wasm_bindgen(constructor)]
pub fn new(channels: usize, bucket_samples: usize, levels: usize) -> Result<Self, JsValue> {
let summary =
WaveformSummary::new(channels, bucket_samples, levels).map_err(invalid_input)?;
Ok(Self {
summary: Some(summary),
channels,
})
}
pub fn push(&mut self, planar: &[f32]) -> Result<(), JsValue> {
let summary = self
.summary
.as_mut()
.ok_or_else(|| invalid_state("the waveform is already finished"))?;
if !planar.len().is_multiple_of(self.channels) {
return Err(invalid_input(
"a planar block holds the same number of samples per channel",
));
}
let frames = planar.len() / self.channels;
let planes = planar.chunks(frames.max(1)).collect::<Vec<_>>();
if frames == 0 {
return Ok(());
}
summary.push(&planes).map_err(invalid_input)
}
pub fn finish(&mut self) -> Result<WaveformResult, JsValue> {
self.summary
.take()
.map(|summary| WaveformResult {
waveform: summary.finish(),
})
.ok_or_else(|| invalid_state("the waveform is already finished"))
}
}
#[wasm_bindgen]
pub struct WaveformResult {
waveform: Waveform,
}
#[wasm_bindgen]
impl WaveformResult {
#[wasm_bindgen(getter)]
#[must_use]
pub fn frames(&self) -> f64 {
#[allow(clippy::cast_precision_loss)]
let frames = self.waveform.frames as f64;
frames
}
#[wasm_bindgen(getter, js_name = levelCount)]
#[must_use]
pub fn level_count(&self) -> usize {
self.waveform.levels.len()
}
#[wasm_bindgen(js_name = bucketSamples)]
#[must_use]
pub fn bucket_samples(&self, level: usize) -> usize {
self.waveform
.levels
.get(level)
.map_or(0, |level| level.bucket_samples)
}
#[must_use]
pub fn buckets(&self, level: usize, channel: usize) -> Vec<f32> {
self.waveform
.levels
.get(level)
.and_then(|level| level.channels.get(channel))
.map_or_else(Vec::new, |buckets| {
buckets
.iter()
.flat_map(|bucket| [bucket.min, bucket.max, bucket.rms])
.collect()
})
}
}