use crate::{Error, Feedback, PrepareConfig, Processor, ProcessorConfig, SampleRate, TimedEvent};
use js_sys::Int16Array;
use serde::{Deserialize, Serialize};
use tsify::{Ts, Tsify};
use wasm_bindgen::prelude::*;
#[derive(Default, Serialize, Deserialize, Tsify)]
#[serde(transparent)]
pub struct BlockEvents(Vec<TimedEvent>);
#[wasm_bindgen(js_name = defaultConfig)]
pub fn default_config() -> Result<Ts<ProcessorConfig>, JsError> {
Ok(ProcessorConfig::default().into_ts()?)
}
#[wasm_bindgen(js_name = Tunelet)]
pub struct WasmProcessor {
processor: Processor,
input: Vec<i16>,
left: Vec<i16>,
right: Vec<i16>,
}
#[wasm_bindgen(js_class = Tunelet)]
impl WasmProcessor {
#[wasm_bindgen(constructor)]
pub fn new(
sample_rate: f64,
max_block_frames: f64,
config: Option<Ts<ProcessorConfig>>,
) -> Result<WasmProcessor, JsError> {
let rate = match sample_rate {
44100.0 => SampleRate::Hz44100,
48000.0 => SampleRate::Hz48000,
_ => return Err(JsError::new("sampleRate must be 44100 or 48000")),
};
if !(1.0..=f64::from(u32::MAX)).contains(&max_block_frames)
|| max_block_frames.fract() != 0.0
{
return Err(JsError::new(
"maxBlockFrames must be an integer in 1..=4294967295",
));
}
let max_block_frames = max_block_frames as usize;
let preparation = PrepareConfig::new(rate, max_block_frames)?;
let config = match config {
Some(value) => value.to_rust()?,
None => ProcessorConfig::default(),
};
let processor = Processor::new(preparation, config)?;
Ok(Self {
processor,
input: pcm_buffer(max_block_frames)?,
left: pcm_buffer(max_block_frames)?,
right: pcm_buffer(max_block_frames)?,
})
}
#[wasm_bindgen(getter, js_name = sampleRate)]
pub fn sample_rate(&self) -> u32 {
self.processor.prepare_config().sample_rate.as_u32()
}
#[wasm_bindgen(getter, js_name = maxBlockFrames)]
pub fn max_block_frames(&self) -> usize {
self.input.len()
}
#[wasm_bindgen(getter, js_name = latencyFrames)]
pub fn latency_frames(&self) -> usize {
self.processor.latency_frames()
}
pub fn config(&self) -> Result<Ts<ProcessorConfig>, JsError> {
Ok(self.processor.config().into_ts()?)
}
#[wasm_bindgen(js_name = setConfig)]
pub fn set_config(&mut self, config: Ts<ProcessorConfig>) -> Result<(), JsError> {
self.processor.set_config(config.to_rust()?)?;
Ok(())
}
pub fn process(
&mut self,
input: &Int16Array,
left: &Int16Array,
right: &Int16Array,
events: Option<Ts<BlockEvents>>,
) -> Result<(), JsError> {
let frames = input.length() as usize;
Error::check_length(frames, left.length() as usize)?;
Error::check_length(frames, right.length() as usize)?;
self.check_frames(frames)?;
let events = decode_events(events)?;
input.copy_to(&mut self.input[..frames]);
self.processor.process_planar(
&self.input[..frames],
&mut self.left[..frames],
&mut self.right[..frames],
&events.0,
)?;
left.copy_from(&self.left[..frames]);
right.copy_from(&self.right[..frames]);
Ok(())
}
#[wasm_bindgen(js_name = processMono)]
pub fn process_mono(
&mut self,
audio: &Int16Array,
events: Option<Ts<BlockEvents>>,
) -> Result<(), JsError> {
let frames = audio.length() as usize;
self.check_frames(frames)?;
let events = decode_events(events)?;
audio.copy_to(&mut self.input[..frames]);
self.processor
.process_mono_in_place(&mut self.input[..frames], &events.0)?;
audio.copy_from(&self.input[..frames]);
Ok(())
}
#[wasm_bindgen(js_name = beginDrain)]
pub fn begin_drain(&mut self) -> usize {
self.processor.begin_drain()
}
#[wasm_bindgen(js_name = tailFrames)]
pub fn tail_frames(&self) -> usize {
self.processor.tail_frames()
}
pub fn drain(&mut self, left: &Int16Array, right: &Int16Array) -> Result<usize, JsError> {
let frames = left.length() as usize;
Error::check_length(frames, right.length() as usize)?;
self.check_frames(frames)?;
let count = self
.processor
.drain_planar(&mut self.left[..frames], &mut self.right[..frames])?;
left.copy_from(&self.left[..frames]);
right.copy_from(&self.right[..frames]);
Ok(count)
}
#[wasm_bindgen(js_name = drainMono)]
pub fn drain_mono(&mut self, audio: &Int16Array) -> Result<usize, JsError> {
let frames = audio.length() as usize;
self.check_frames(frames)?;
let count = self.processor.drain_mono(&mut self.input[..frames])?;
audio.copy_from(&self.input[..frames]);
Ok(count)
}
pub fn feedback(&self) -> Result<Ts<Feedback>, JsError> {
Ok(self.processor.feedback().into_ts()?)
}
#[wasm_bindgen(js_name = takeFeedback)]
pub fn take_feedback(&mut self) -> Result<Ts<Feedback>, JsError> {
let feedback = self.processor.feedback().into_ts()?;
self.processor.take_feedback();
Ok(feedback)
}
pub fn reset(&mut self) {
self.processor.reset();
}
}
impl WasmProcessor {
fn check_frames(&self, frames: usize) -> Result<(), Error> {
let max = self.input.len();
if frames > max {
Err(Error::BlockTooLarge { frames, max })
} else {
Ok(())
}
}
}
fn decode_events(events: Option<Ts<BlockEvents>>) -> Result<BlockEvents, JsError> {
Ok(match events {
Some(value) => value.to_rust()?,
None => BlockEvents::default(),
})
}
fn pcm_buffer(frames: usize) -> Result<Vec<i16>, JsError> {
let mut buffer = Vec::new();
buffer.try_reserve_exact(frames)?;
buffer.resize(frames, 0);
Ok(buffer)
}
pub(crate) fn serialize_frame_count<S: serde::Serializer>(
frames: &usize,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_u32(*frames as u32)
}