use gstreamer as gst;
use gstreamer_base as gst_base;
use crossbeam_channel::{unbounded, Sender};
use deepspeech::Model;
use gst::message::Message;
use gst::structure::Structure;
use gst_base::prelude::*;
use gst_base::BaseTransform;
use serde::Serialize;
use std::path::Path;
pub enum Msg {
Audio(Vec<i16>),
Done(BaseTransform),
}
#[derive(Debug, Serialize)]
struct Candidate {
text: String,
confidence: f64,
}
#[derive(Debug, Serialize)]
enum Output {
// Intermediate(String),
Final(Vec<Candidate>),
}
pub type DSChan = Sender<Msg>;
// TODO error handling
pub fn deepspeech_thread() -> DSChan {
let (s, r) = unbounded();
std::thread::spawn(move || {
println!("\n\n{:?}\n\n", std::env::var("DS_MODELS_DIR"));
let model_env =
std::env::var("DS_MODEL_PATH").expect("Invalid model path");
let mut model = Model::load_from_files(Path::new(&model_env)).unwrap();
if let Ok(scorer_env) = std::env::var("DS_SCORER_PATH") {
model.enable_external_scorer(Path::new(&scorer_env));
}
'main: loop {
let mut stream = model.create_stream().unwrap();
loop {
match r.recv() {
Ok(msg) => match msg {
Msg::Audio(buf) => {
stream.feed_audio(&buf);
// let text = stream.intermediate_decode().unwrap();
// if !text.is_empty() {
// let text = serde_json::to_string_pretty(
// &Output::Intermediate(text),
// )
// .unwrap();
// }
}
Msg::Done(element) => {
let meta = stream.finish_with_metadata(5).unwrap();
let trans = meta.transcripts();
// let text = stream.finish().unwrap();
if !trans.is_empty() {
let candidates = trans
.iter()
.map(|v| Candidate {
text: v.to_string(),
confidence: v.confidence(),
})
.collect();
let text = serde_json::to_string_pretty(
&Output::Final(candidates),
)
.unwrap();
let msg = Message::new_element(
Structure::builder("deepspeech")
.field("text", &text)
.build(),
)
.build();
element.post_message(&msg).unwrap();
}
break;
}
},
Err(_) => {
break 'main;
}
}
}
}
});
s
}