1use crate::mic::converters::i16_to_f32;
2use crate::mic::mic_cpal::MicHandlerCpal;
3use crate::model::{Detection, Model, new_model};
4use hound::{SampleFormat, WavReader};
5use log::{debug, info, warn};
6use std::env;
7use std::error::Error;
8use std::fs::File;
9use std::io::BufReader;
10use std::path::{Path, PathBuf};
11use std::sync::{Arc, Mutex};
12use std::thread::sleep;
13use std::time::Duration;
14use tokio::sync::broadcast;
15use tokio_util::sync::CancellationToken;
16use tract_core::internal::{Graph, RunnableModel, TypedFact, TypedOp};
17use crate::chunk::ChunkType;
18use crate::config::UnlockConfig;
19
20pub mod config;
21pub mod info;
22mod model;
23pub mod oww;
24pub mod rms;
25pub mod save;
26mod tests;
27
28pub mod mic;
29pub mod chunk;
30
31pub const VOICE_SAMPLE_RATE: usize = 16000;
32pub const BUFFER_SECS: usize = 4;
33
34pub const RMS_BUFFER_SIZE: usize = 16; type ModelType = RunnableModel<TypedFact, Box<dyn TypedOp>, Graph<TypedFact, Box<dyn TypedOp>>>;
37
38pub struct Models {
39 pub(crate) model1: Box<dyn Model>,
40 pub(crate) model2: Box<dyn Model>,
41}
42
43impl Models {
44 pub(crate) fn new(model1: Box<dyn Model>, model2: Box<dyn Model>) -> Self {
45 Models { model1, model2 }
46 }
47 pub(crate) fn frame_length(&self) -> usize {
48 self.model1.frame_length() as usize
49 }
50
51 pub fn detect1(&mut self, data: Vec<f32>) -> Option<Detection> {
52 self.model1.detect(data)
53 }
54
55 pub fn detect2(&mut self, data: Vec<f32>) -> Option<Detection> {
56 self.model2.detect(data)
57 }
58
59 pub fn detect1_i16(&mut self, data: Vec<i16>) -> Option<Detection> {
60 self.model1.detect_i16(data)
61 }
62
63 pub fn detect2_i16(&mut self, data: Vec<i16>) -> Option<Detection> {
64 self.model2.detect_i16(data)
65 }
66}
67
68pub fn create_unlock_task_sync(
69 running: CancellationToken,
70 chunks_sender: broadcast::Sender<ChunkType>,
71) -> Result<bool, String> {
72 let running2 = running.clone();
73 let mut mic_failing = false;
74 while !running2.is_cancelled() {
75 let config = UnlockConfig::default(); let model1 = new_model(config.clone());
77 let model2 = new_model(config.clone());
78
79 let models = match (model1, model2) {
80 (Ok(model1), Ok(model2)) => (model1, model2),
81 _ => {
82 panic!("Unable to create unlock model");
83 }
84 };
85
86 let mic_loop = MicHandlerCpal::new(
87 Arc::new(Mutex::new(Models::new(models.0, models.1))),
88 &config,
89 chunks_sender.clone(),
90 );
91
92 match mic_loop {
93 Ok(mut mic) => {
94 mic_failing = false;
95 if let Err(e) = mic.loop_now_sync(running.clone()) {
96 warn!("Mic loop error {:?}. Reloading mic loop", e)
97 }
98 debug!("Mic loop successful");
99 }
100 Err(e) => {
101 if !mic_failing {
102 warn!("Mic init error {:?}", e);
103 mic_failing = true;
104 } else {
105 debug!("Mic err loop successful {:?}", e);
106 }
107 }
108 }
109 sleep(Duration::from_secs(1));
110 }
111 Ok(false)
112}
113
114pub fn load_wav(filename: &str) -> Result<Vec<f32>, Box<dyn Error>> {
116 let based_dir = env!("CARGO_MANIFEST_DIR");
117 let path = Path::new(based_dir).join(filename);
118 info!("Reading file {:?}", &path);
119 let reader: WavReader<BufReader<File>> = hound::WavReader::open(path)?;
120
121 match reader.spec().sample_format {
122 SampleFormat::Float => match load_wav_f32(reader) {
123 Ok(d) => Ok(d),
124 Err(e) => Err(e),
125 },
126 SampleFormat::Int => load_wav_i16(reader).map(|d| d.iter().map(i16_to_f32).collect()),
127 }
128}
129
130fn load_wav_i16(mut reader: WavReader<BufReader<File>>) -> Result<Vec<i16>, Box<dyn Error>> {
132 let mut data = vec![];
133 for s in reader.samples::<i16>() {
134 data.push(s.unwrap());
135 }
136 Ok(data)
137}
138
139fn load_wav_f32(mut reader: WavReader<BufReader<File>>) -> Result<Vec<f32>, Box<dyn Error>> {
141 let mut data = vec![];
142 for s in reader.samples::<f32>() {
143 data.push(s.unwrap());
144 }
145 Ok(data)
146}
147
148pub fn get_exec_dir() -> PathBuf {
149 let exec_dir = match env::current_exe() {
150 Ok(exe) => match exe.parent() {
151 None => {
152 panic!("No exec directory found");
153 }
154 Some(p) => p.to_path_buf(),
155 },
156 Err(e) => {
157 panic!("No exec directory found, error {:?}", e);
158 }
159 };
160 exec_dir
161}