//! Browser sink for `wai.haptic.signal` — reconstructs the per-channel //! intensity/sharpness envelope in the deterministic floor and checks //! its hash against the corpus (sample-equivalence). Playback drives the //! Gamepad vibration actuator / Vibration API; the waveform view is the //! always-available presentation a sink without actuators shows. use wai::haptic::HapticSignal; use wasm_bindgen::prelude::*; #[wasm_bindgen] pub struct HapticSink { sig: HapticSignal, } fn hex(b: &[u8]) -> String { b.iter().map(|x| format!("{x:02x}")).collect() } #[wasm_bindgen] impl HapticSink { #[wasm_bindgen(constructor)] pub fn new(whap: &[u8]) -> Result { Ok(HapticSink { sig: HapticSignal::from_bytes(whap).map_err(|e| JsError::new(&e.to_string()))? }) } pub fn num_channels(&self) -> u32 { self.sig.contract.channels } pub fn duration(&self) -> u64 { self.sig.contract.duration } pub fn sample_rate(&self) -> u32 { self.sig.contract.sample_rate } pub fn signal_hash_hex(&self) -> String { hex(&self.sig.signal_hash()) } /// Reconstructed channel `c`: interleaved `[intensity, sharpness]` /// u8 per sample — the canonical buffer, for the waveform view and /// to drive the actuator. pub fn channel(&self, c: usize) -> Box<[u8]> { if c >= self.sig.channels.len() { return Box::new([]); } self.sig.reconstruct_channel(c).into_boxed_slice() } }