Vosk added as wake-word engine option
This commit is contained in:
parent
d8b8ba2617
commit
75691211ad
3 changed files with 65 additions and 4 deletions
|
|
@ -66,6 +66,13 @@ pub fn start_listening(app_handle: tauri::AppHandle) -> Result<bool, String> {
|
||||||
events::play("run", &app_handle);
|
events::play("run", &app_handle);
|
||||||
}, |app, kidx| keyword_callback(app, kidx));
|
}, |app, kidx| keyword_callback(app, kidx));
|
||||||
},
|
},
|
||||||
|
"vosk" => {
|
||||||
|
info!("Starting vosk wake-word engine ...");
|
||||||
|
return vosk_listen(&app_handle, |_app| {
|
||||||
|
// Greet user
|
||||||
|
events::play("run", &app_handle);
|
||||||
|
}, |app, kidx| keyword_callback(app, kidx));
|
||||||
|
},
|
||||||
"picovoice" => {
|
"picovoice" => {
|
||||||
info!("Starting picovoice wake-word engine ...");
|
info!("Starting picovoice wake-word engine ...");
|
||||||
return picovoice_listen(&app_handle, |_app| {
|
return picovoice_listen(&app_handle, |_app| {
|
||||||
|
|
@ -100,7 +107,7 @@ pub fn keyword_callback(app_handle: &tauri::AppHandle, _keyword_index: i32) {
|
||||||
recorder::read_microphone(&mut frame_buffer);
|
recorder::read_microphone(&mut frame_buffer);
|
||||||
|
|
||||||
// vosk part (partials included)
|
// vosk part (partials included)
|
||||||
if let Some(mut test) = vosk::recognize(&frame_buffer) {
|
if let Some(mut test) = vosk::recognize(&frame_buffer, false) {
|
||||||
if !test.is_empty() {
|
if !test.is_empty() {
|
||||||
println!("Recognized: {}", test);
|
println!("Recognized: {}", test);
|
||||||
|
|
||||||
|
|
@ -156,6 +163,56 @@ pub fn keyword_callback(app_handle: &tauri::AppHandle, _keyword_index: i32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn vosk_listen<'s, S, K>(app_handle: &tauri::AppHandle, start_callback: S, mut keyword_callback: K) -> Result<bool, String>
|
||||||
|
where S: Fn(&tauri::AppHandle),
|
||||||
|
K: FnMut(&tauri::AppHandle, i32) {
|
||||||
|
|
||||||
|
// vars
|
||||||
|
let fetch_phrase = "джарвис".chars().collect::<Vec<_>>();
|
||||||
|
let frame_length: usize = 128;
|
||||||
|
let min_ratio: f64 = 0.8;
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
let mut frame_buffer = vec![0; frame_length];
|
||||||
|
recorder::FRAME_LENGTH.store(frame_length as u32, Ordering::SeqCst);
|
||||||
|
recorder::start_recording();
|
||||||
|
LISTENING.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
// run start callback
|
||||||
|
start_callback(app_handle);
|
||||||
|
|
||||||
|
// Listen until stop flag will be true
|
||||||
|
while !STOP_LISTENING.load(Ordering::SeqCst) {
|
||||||
|
recorder::read_microphone(&mut frame_buffer);
|
||||||
|
|
||||||
|
// recognize & convert to sequence
|
||||||
|
let recognized_phrase = vosk::recognize(&frame_buffer, true).unwrap_or("".into());
|
||||||
|
|
||||||
|
if !recognized_phrase.trim().is_empty() {
|
||||||
|
info!("Rec: {}", recognized_phrase);
|
||||||
|
let recognized_phrases = recognized_phrase.split_whitespace();
|
||||||
|
for phrase in recognized_phrases {
|
||||||
|
let recognized_phrase_chars = phrase.trim().to_lowercase().chars().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// compare
|
||||||
|
if seqdiff::ratio(&fetch_phrase, &recognized_phrase_chars) >= min_ratio {
|
||||||
|
info!("Phrase: {:?}", &fetch_phrase);
|
||||||
|
info!("Compare: {:?}", &recognized_phrase_chars);
|
||||||
|
keyword_callback(&app_handle, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop listening
|
||||||
|
recorder::stop_recording();
|
||||||
|
LISTENING.store(false, Ordering::SeqCst);
|
||||||
|
STOP_LISTENING.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn picovoice_listen<'s, S, K>(app_handle: &tauri::AppHandle, start_callback: S, mut keyword_callback: K) -> Result<bool, String>
|
pub fn picovoice_listen<'s, S, K>(app_handle: &tauri::AppHandle, start_callback: S, mut keyword_callback: K) -> Result<bool, String>
|
||||||
where S: Fn(&tauri::AppHandle),
|
where S: Fn(&tauri::AppHandle),
|
||||||
K: FnMut(&tauri::AppHandle, i32) {
|
K: FnMut(&tauri::AppHandle, i32) {
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,16 @@ pub fn init_vosk() {
|
||||||
RECOGNIZER.lock().unwrap().set_partial_words(true);
|
RECOGNIZER.lock().unwrap().set_partial_words(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recognize(data: &[i16]) -> Option<String> {
|
pub fn recognize(data: &[i16], include_partial: bool) -> Option<String> {
|
||||||
let state = RECOGNIZER.lock().unwrap().accept_waveform(data);
|
let state = RECOGNIZER.lock().unwrap().accept_waveform(data);
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
DecodingState::Running => {
|
DecodingState::Running => {
|
||||||
|
if include_partial {
|
||||||
|
Some(RECOGNIZER.lock().unwrap().partial_result().partial.into())
|
||||||
|
} else {
|
||||||
None
|
None
|
||||||
// Some(RECOGNIZER.lock().unwrap().partial_result().partial.into())
|
}
|
||||||
}
|
}
|
||||||
DecodingState::Finalized => {
|
DecodingState::Finalized => {
|
||||||
// Result will always be multiple because we called set_max_alternatives
|
// Result will always be multiple because we called set_max_alternatives
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
|
|
||||||
<NativeSelect data={[
|
<NativeSelect data={[
|
||||||
{ label: 'Rustpotter', value: 'rustpotter' },
|
{ label: 'Rustpotter', value: 'rustpotter' },
|
||||||
|
{ label: 'Vosk (медленный)', value: 'vosk' },
|
||||||
{ label: 'Picovoice Porcupine (требует API ключ)', value: 'picovoice' }
|
{ label: 'Picovoice Porcupine (требует API ключ)', value: 'picovoice' }
|
||||||
]}
|
]}
|
||||||
label="Распознавание активационной фразы (Wake Word)"
|
label="Распознавание активационной фразы (Wake Word)"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue