rustpotter detections optimization + few code improvements

This commit is contained in:
Priler 2026-01-04 06:14:05 +05:00
parent 88d73eeb10
commit 61b7a79455
12 changed files with 64 additions and 58 deletions

2
Cargo.lock generated
View file

@ -4846,7 +4846,7 @@ dependencies = [
[[package]] [[package]]
name = "rustpotter" name = "rustpotter"
version = "3.0.2" version = "3.0.2"
source = "git+https://github.com/Priler/rustpotter#5604a094b4a21ad12645e5aac5ffc463a2892af8" source = "git+https://github.com/Priler/rustpotter#f0d5c4d6b63aa1ebb6e0e81089993e4ae7aef3d8"
dependencies = [ dependencies = [
"candle-core", "candle-core",
"candle-nn", "candle-nn",

View file

@ -75,7 +75,7 @@ fn main() -> Result<(), String> {
// start the app (in the background thread) // start the app (in the background thread)
std::thread::spawn(|| { std::thread::spawn(|| {
app::start(); let _ = app::start();
}); });
tray::init_blocking(); tray::init_blocking();

View file

@ -11,7 +11,7 @@ use crate::{config, DB, SOUND_DIR};
static AUDIO_TYPE: OnceCell<AudioType> = OnceCell::new(); static AUDIO_TYPE: OnceCell<AudioType> = OnceCell::new();
pub fn init() -> Result<(), ()> { pub fn init() -> Result<(), ()> {
if !AUDIO_TYPE.get().is_none() { if AUDIO_TYPE.get().is_some() {
return Ok(()); return Ok(());
} // already initialized } // already initialized
@ -29,7 +29,7 @@ pub fn init() -> Result<(), ()> {
Ok(_) => { Ok(_) => {
info!("Successfully initialized Rodio audio backend."); info!("Successfully initialized Rodio audio backend.");
} }
Err(msg) => { Err(()) => {
error!("Failed to initialize Rodio audio backend."); error!("Failed to initialize Rodio audio backend.");
return Err(()); return Err(());

View file

@ -17,7 +17,7 @@ static STREAM_HANDLE: OnceCell<OutputStream> = OnceCell::new();
static SINK: OnceCell<Sink> = OnceCell::new(); static SINK: OnceCell<Sink> = OnceCell::new();
pub fn init() -> Result<(), ()> { pub fn init() -> Result<(), ()> {
if !STREAM_HANDLE.get().is_none() { if STREAM_HANDLE.get().is_some() {
return Ok(()); return Ok(());
} // already initialized } // already initialized

View file

@ -23,7 +23,13 @@ pub fn parse_commands() -> Result<Vec<AssistantCommand>, String> {
if let Ok(cpaths) = fs::read_dir(config::COMMANDS_PATH) { if let Ok(cpaths) = fs::read_dir(config::COMMANDS_PATH) {
for cpath in cpaths { for cpath in cpaths {
// validate this command, check if required files exists // validate this command, check if required files exists
let _cpath = cpath.unwrap().path(); let _cpath = match cpath {
Ok(entry) => entry.path(),
Err(e) => {
warn!("Failed to read command directory entry: {}", e);
continue;
}
};
let cc_file = Path::new(&_cpath).join("command.yaml"); let cc_file = Path::new(&_cpath).join("command.yaml");
if cc_file.exists() { if cc_file.exists() {
@ -53,7 +59,7 @@ pub fn parse_commands() -> Result<Vec<AssistantCommand>, String> {
} }
} }
if commands.len() > 0 { if !commands.is_empty() {
Ok(commands) Ok(commands)
} else { } else {
error!("No commands were found"); error!("No commands were found");
@ -101,7 +107,7 @@ pub fn fetch_command<'a>(
} }
if let Some((cmd_path, scmd)) = result_scmd { if let Some((cmd_path, scmd)) = result_scmd {
println!("Ratio is: {}", current_max_ratio); debug!("Ratio is: {}", current_max_ratio);
info!( info!(
"CMD is: {cmd_path:?}, SCMD is: {scmd:?}, Ratio is: {}", "CMD is: {cmd_path:?}, SCMD is: {scmd:?}, Ratio is: {}",
current_max_ratio current_max_ratio
@ -118,7 +124,7 @@ pub fn execute_exe(exe: &str, args: &Vec<String>) -> std::io::Result<Child> {
} }
pub fn execute_cli(cmd: &str, args: &Vec<String>) -> std::io::Result<Child> { pub fn execute_cli(cmd: &str, args: &Vec<String>) -> std::io::Result<Child> {
println!("Spawning cmd as: cmd /C {} {:?}", cmd, args); debug!("Spawning cmd as: cmd /C {} {:?}", cmd, args);
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
Command::new("cmd").arg("/C").arg(cmd).args(args).spawn() Command::new("cmd").arg("/C").arg(cmd).args(args).spawn()

View file

@ -19,7 +19,7 @@ static WAKE_WORD_ENGINE: OnceCell<WakeWordEngine> = OnceCell::new();
static LISTENING: AtomicBool = AtomicBool::new(false); static LISTENING: AtomicBool = AtomicBool::new(false);
pub fn init() -> Result<(), ()> { pub fn init() -> Result<(), ()> {
if !WAKE_WORD_ENGINE.get().is_none() { if WAKE_WORD_ENGINE.get().is_some() {
return Ok(()); return Ok(());
} // already initialized } // already initialized

View file

@ -33,7 +33,10 @@ pub fn init() -> Result<(), ()> {
// load wake word files // load wake word files
for rpw in rustpotter_wake_word_files { for rpw in rustpotter_wake_word_files {
rinstance.add_wakeword_from_file(rpw, rpw).unwrap(); // @TODO: Change wakeword key to something else? // @TODO: Change wakeword key to something else?
if let Err(e) = rinstance.add_wakeword_from_file(rpw, rpw) {
error!("Failed to load wakeword file '{}': {}", rpw, e);
}
} }
// store // store
@ -52,7 +55,8 @@ pub fn init() -> Result<(), ()> {
pub fn data_callback(frame_buffer: &[i16]) -> Option<i32> { pub fn data_callback(frame_buffer: &[i16]) -> Option<i32> {
let mut lock = RUSTPOTTER.get().unwrap().lock(); let mut lock = RUSTPOTTER.get().unwrap().lock();
let rustpotter = lock.as_mut().unwrap(); let rustpotter = lock.as_mut().unwrap();
let detection = rustpotter.process_samples(frame_buffer.to_vec()); // @TODO. Temp crutch. Fix optimization issue, frame_buffer should not be copied to a new vector! // let detection = rustpotter.process_samples(frame_buffer.to_vec()); // @TODO. Temp crutch. Fix optimization issue, frame_buffer should not be copied to a new vector!
let detection = rustpotter.process_samples(frame_buffer);
if let Some(detection) = detection { if let Some(detection) = detection {
if detection.score > config::RUSPOTTER_MIN_SCORE { if detection.score > config::RUSPOTTER_MIN_SCORE {

View file

@ -175,7 +175,7 @@ pub fn start_recording(device_index: i32, frame_length: u32) {
pub fn stop_recording() { pub fn stop_recording() {
// ensure microphone is initialized // ensure microphone is initialized
RECORDER.with(|recorder| { RECORDER.with(|recorder| {
if !recorder.get().is_none() && IS_RECORDING.load(Ordering::SeqCst) { if recorder.get().is_some() && IS_RECORDING.load(Ordering::SeqCst) {
// pause instead of stop // pause instead of stop
match recorder.get().unwrap().load().pause() { match recorder.get().unwrap().load().pause() {
Err(msg) => { Err(msg) => {

View file

@ -159,7 +159,7 @@ where
pub fn read_microphone(frame_buffer: &mut [i16]) { pub fn read_microphone(frame_buffer: &mut [i16]) {
// ensure microphone is initialized // ensure microphone is initialized
RECORDER.with(|r| { RECORDER.with(|r| {
if !r.get().is_none() { if r.get().is_some() {
let cell = r.get().unwrap().load(); let cell = r.get().unwrap().load();
let mut lock = cell.lock(); let mut lock = cell.lock();
let stream = lock.as_mut().unwrap(); let stream = lock.as_mut().unwrap();
@ -194,7 +194,7 @@ pub fn start_recording(device_index: i32, frame_length: u32) {
pub fn stop_recording() { pub fn stop_recording() {
RECORDER.with(|r| { RECORDER.with(|r| {
if !r.get().is_none() && IS_RECORDING.load(Ordering::SeqCst) { if r.get().is_some() && IS_RECORDING.load(Ordering::SeqCst) {
// stop recording // stop recording
let pa = r.get().unwrap().load(); let pa = r.get().unwrap().load();
r.get().unwrap().load().lock().unwrap().stop().expect("Failed to stop audio recording!"); r.get().unwrap().load().lock().unwrap().stop().expect("Failed to stop audio recording!");

View file

@ -6,8 +6,11 @@ static RECORDER: OnceCell<PvRecorder> = OnceCell::new();
static IS_RECORDING: AtomicBool = AtomicBool::new(false); static IS_RECORDING: AtomicBool = AtomicBool::new(false);
pub fn init_microphone(device_index: i32, frame_length: u32) -> bool { pub fn init_microphone(device_index: i32, frame_length: u32) -> bool {
match RECORDER.get().is_none() { if RECORDER.get().is_some() {
true => { return true; // already initialized
}
// initialize
let pv_recorder = PvRecorderBuilder::new(frame_length as i32) let pv_recorder = PvRecorderBuilder::new(frame_length as i32)
.device_index(device_index) .device_index(device_index)
// .frame_length(frame_length as i32) // .frame_length(frame_length as i32)
@ -29,13 +32,10 @@ pub fn init_microphone(device_index: i32, frame_length: u32) -> bool {
} }
} }
} }
_ => true, // already initialized
}
}
pub fn read_microphone(frame_buffer: &mut [i16]) { pub fn read_microphone(frame_buffer: &mut [i16]) {
// ensure microphone is initialized // ensure microphone is initialized
if !RECORDER.get().is_none() { if RECORDER.get().is_some() {
// read to frame buffer // read to frame buffer
let frame = RECORDER.get().unwrap().read(); let frame = RECORDER.get().unwrap().read();
@ -68,7 +68,7 @@ pub fn start_recording(device_index: i32, frame_length: u32) -> Result<(), ()> {
Ok(()) Ok(())
} }
Err(msg) => { Err(msg) => {
error!("Failed to start audio recording!"); error!("Failed to START audio recording: {}", msg);
// fail // fail
Err(()) Err(())
@ -78,7 +78,7 @@ pub fn start_recording(device_index: i32, frame_length: u32) -> Result<(), ()> {
pub fn stop_recording() -> Result<(), ()> { pub fn stop_recording() -> Result<(), ()> {
// ensure microphone is initialized & recording is in process // ensure microphone is initialized & recording is in process
if !RECORDER.get().is_none() && IS_RECORDING.load(Ordering::SeqCst) { if RECORDER.get().is_some() && IS_RECORDING.load(Ordering::SeqCst) {
// stop recording // stop recording
match RECORDER.get().unwrap().stop() { match RECORDER.get().unwrap().stop() {
Ok(_) => { Ok(_) => {
@ -91,7 +91,7 @@ pub fn stop_recording() -> Result<(), ()> {
return Ok(()); return Ok(());
} }
Err(msg) => { Err(msg) => {
error!("Failed to stop audio recording!"); error!("Failed to STOP audio recording: {}", msg);
// fail // fail
return Err(()); return Err(());
@ -106,7 +106,10 @@ pub fn list_audio_devices() -> Vec<String> {
let audio_devices = PvRecorderBuilder::default().get_available_devices(); let audio_devices = PvRecorderBuilder::default().get_available_devices();
match audio_devices { match audio_devices {
Ok(audio_devices) => audio_devices, Ok(audio_devices) => audio_devices,
Err(err) => panic!("Failed to get audio devices: {}", err), Err(err) => {
error!("Failed to get audio devices: {}", err);
Vec::new()
},
} }
} }

View file

@ -8,7 +8,7 @@ use crate::config::structs::SpeechToTextEngine;
static STT_TYPE: OnceCell<SpeechToTextEngine> = OnceCell::new(); static STT_TYPE: OnceCell<SpeechToTextEngine> = OnceCell::new();
pub fn init() -> Result<(), ()> { pub fn init() -> Result<(), ()> {
if !STT_TYPE.get().is_none() { if STT_TYPE.get().is_some() {
return Ok(()); return Ok(());
} // already initialized } // already initialized

View file

@ -9,7 +9,7 @@ static MODEL: OnceCell<Model> = OnceCell::new();
static RECOGNIZER: OnceCell<Mutex<Recognizer>> = OnceCell::new(); static RECOGNIZER: OnceCell<Mutex<Recognizer>> = OnceCell::new();
pub fn init_vosk() { pub fn init_vosk() {
if !RECOGNIZER.get().is_none() { if RECOGNIZER.get().is_some() {
return; return;
} // already initialized } // already initialized
@ -53,7 +53,6 @@ pub fn recognize(data: &[i16], include_partial: bool) -> Option<String> {
} }
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
Some(
RECOGNIZER RECOGNIZER
.get() .get()
.unwrap() .unwrap()
@ -61,13 +60,7 @@ pub fn recognize(data: &[i16], include_partial: bool) -> Option<String> {
.unwrap() .unwrap()
.result() .result()
.multiple() .multiple()
.unwrap() .and_then(|m| m.alternatives.first().map(|a| a.text.to_string()))
.alternatives
.first()
.unwrap()
.text
.into(),
)
} }
DecodingState::Failed => None, DecodingState::Failed => None,
} }