1use wasm_bindgen::prelude::*;
2
3use aria_kit::audio::{Recording, RecordingReader, RecordingWriter};
4use aria_kit::primitives::error_chain;
5use aria_kit::transcription::{Language, Transcription, TranscriptionError, Whole};
6use aria_stt::whisper_candle::WhisperCandle;
7
8use crate::silence::SilenceGate;
9
10#[wasm_bindgen]
12pub struct Dictation {
13 capture: RecordingWriter,
14 recording: RecordingReader,
15 transcription: Transcription<WhisperCandle, Whole>,
16 gate: SilenceGate,
17}
18
19#[wasm_bindgen]
20impl Dictation {
21 #[wasm_bindgen(constructor)]
27 pub fn new(
28 weights: &[u8],
29 tokenizer: &[u8],
30 config: &[u8],
31 trailing_silence_ms: u32,
32 ) -> Result<Self, JsError> {
33 let gate = SilenceGate::new(trailing_silence_ms)
34 .map_err(|error| JsError::new(&error_chain(&error)))?;
35 let engine = WhisperCandle::from_bytes(weights, tokenizer, config).map_err(failed)?;
36 let transcription = Transcription::complete_samples(engine);
37 let (recording, capture) = Recording::open();
38 Ok(Self {
39 capture,
40 recording,
41 transcription,
42 gate,
43 })
44 }
45
46 pub fn push_audio(&mut self, samples: &[f32]) {
48 self.capture.push(samples);
49 self.gate.hear(samples);
50 }
51
52 pub fn speaker_stopped(&self) -> bool {
55 self.gate.stopped()
56 }
57
58 pub fn finish(mut self) -> Result<String, JsError> {
61 self.capture.finish();
62 match self
63 .transcription
64 .transcribe(&mut self.recording, &Language::Automatic)
65 {
66 Ok(transcript) => Ok(transcript.into_text()),
67 Err(TranscriptionError::NoSpeech) => Ok(String::new()),
68 Err(error) => Err(failed(error)),
69 }
70 }
71}
72
73fn failed(error: TranscriptionError) -> JsError {
74 JsError::new(&error_chain(&error))
75}