Improved wake-word & command detection, with dual mode

This commit is contained in:
Priler 2026-02-08 06:37:39 +05:00
parent baf84e0d8f
commit acc806d403
10 changed files with 233 additions and 62 deletions

View file

@ -206,29 +206,30 @@ pub const CMS_WAIT_DELAY: std::time::Duration = std::time::Duration::from_secs(1
pub fn get_wake_phrase(lang: &str) -> &'static str {
pub fn get_wake_phrases(lang: &str) -> &'static [&'static str] {
match lang {
"ru" => "джарвис",
"ua" => "джарвіс",
"en" => "jarvis",
_ => "jarvis",
"ru" => &["джарвис", "джервис", "гарвис", "джарви", "гарви"],
"ua" => &["джарвіс", "джервіс"],
"en" => &["jarvis", "jervis"],
_ => &["jarvis"],
}
}
pub fn get_phrases_to_remove(lang: &str) -> &'static [&'static str] {
match lang {
"ru" => &[
"джарвис", "сэр", "слушаю сэр", "всегда к услугам",
"джарвис", "джервис", "гарвис", "джарви", "гарви",
"сэр", "слушаю сэр", "всегда к услугам",
"произнеси", "ответь", "покажи", "скажи", "давай",
"да сэр", "к вашим услугам сэр", "загружаю сэр",
],
"ua" => &[
"джарвіс", "сер", "слухаю сер", "завжди до послуг",
"джарвіс", "джервіс", "сер", "слухаю сер", "завжди до послуг",
"скажи", "покажи", "відповідай", "давай",
"так сер", "до ваших послуг сер",
],
"en" => &[
"jarvis", "sir", "yes sir", "at your service",
"jarvis", "jervis", "sir", "yes sir", "at your service",
"please", "say", "show", "tell", "hey",
],
_ => &["jarvis"],

View file

@ -14,7 +14,6 @@ pub async fn init(commands: &Vec<JCommandsList>) -> Result<(), String> {
} // already initialized
// set default ire type
// @TODO. Make it configurable?
IRE_TYPE.set(config::DEFAULT_INTENT_RECOGNITION_ENGINE).unwrap();
// load given recorder

View file

@ -17,19 +17,28 @@ pub fn data_callback(frame_buffer: &[i16]) -> Option<i32> {
// language-specific wake phrase
let lang = i18n::get_language();
let wake_phrase = config::get_wake_phrase(&lang);
let wake_phrases = config::get_wake_phrases(&lang);
// verify with seqdiff ratio
let wake_chars: Vec<char> = wake_phrase.chars().collect();
let recognized_chars: Vec<char> = recognized.chars().collect();
let similarity = seqdiff::ratio(&wake_chars, &recognized_chars);
info!("Similarity: {:.1}% ('{}' vs '{}')", similarity, recognized, config::VOSK_FETCH_PHRASE);
if similarity >= config::VOSK_MIN_RATIO {
info!("Wake word activated!");
return Some(0);
for word in recognized.split_whitespace() {
if word == "[unk]" {
continue;
}
let word_chars: Vec<char> = word.chars().collect();
for wake_phrase in wake_phrases {
let wake_chars: Vec<char> = wake_phrase.chars().collect();
let similarity = seqdiff::ratio(&wake_chars, &word_chars);
if similarity >= config::VOSK_MIN_RATIO {
info!("Wake word match: '{}' ~ '{}' ({:.1}%)", word, wake_phrase, similarity);
return Some(0);
}
}
}
// info!("Similarity: {:.1}% ('{}' vs '{}')", similarity, recognized, config::VOSK_FETCH_PHRASE);
}
None

View file

@ -123,7 +123,19 @@ pub fn stop_recording() -> Result<(), ()> {
}
pub fn get_selected_microphone_index() -> i32 {
DB.get().unwrap().read().microphone
let idx = DB.get().unwrap().read().microphone;
if idx > 0 {
// validate that this microphone is actually in the list
let devices = get_audio_devices();
if (idx as usize) >= devices.len() {
warn!("Microphone index {} not found ({} available), falling back to default",
idx, devices.len());
return -1;
}
}
idx
}
pub fn get_audio_devices() -> Vec<String> {