Update to Rust programming language.

This commit is contained in:
Abraham 2023-04-27 00:28:36 +05:00
parent 943efbfbdb
commit f88248643b
201 changed files with 12954 additions and 1690 deletions

View file

@ -0,0 +1,19 @@
use crate::DB;
#[tauri::command]
pub fn db_read(key: &str) -> String {
if let Some(value) = DB.lock().unwrap().get(key) {
value
} else {
String::from("")
}
}
#[tauri::command]
pub fn db_write(key: &str, val: &str) -> bool {
if let Ok(_) = DB.lock().unwrap().set(key, &val) {
true
} else {
false
}
}

View file

@ -0,0 +1,32 @@
use crate::config::APP_VERSION;
use crate::config::AUTHOR_NAME;
use crate::config::REPOSITORY_LINK;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
pub fn get_app_version() -> String {
if let Some(ver) = APP_VERSION {
ver.to_string()
} else {
String::from("error")
}
}
#[tauri::command]
pub fn get_author_name() -> String {
if let Some(ver) = AUTHOR_NAME {
ver.to_string()
} else {
String::from("error")
}
}
#[tauri::command]
pub fn get_repository_link() -> String {
if let Some(ver) = REPOSITORY_LINK {
ver.to_string()
} else {
String::from("error")
}
}

View file

@ -0,0 +1,193 @@
use porcupine::{BuiltinKeywords, Porcupine, PorcupineBuilder};
use pv_recorder::RecorderBuilder;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::events::Payload;
use tauri::Manager;
use rand::seq::SliceRandom;
use std::time::SystemTime;
use crate::assistant_commands;
use crate::events;
use crate::config;
use crate::vosk;
use crate::COMMANDS;
use crate::DB;
// track listening state
static LISTENING: AtomicBool = AtomicBool::new(false);
// stop listening with Atomic flag (to make it work between different threads)
static STOP_LISTENING: AtomicBool = AtomicBool::new(false);
#[tauri::command]
pub fn is_listening() -> bool {
LISTENING.load(Ordering::SeqCst)
}
#[tauri::command]
pub fn stop_listening() {
if is_listening() {
STOP_LISTENING.store(true, Ordering::SeqCst);
}
// wait until listening stops
while is_listening() {}
}
#[tauri::command(async)]
pub fn start_listening(app_handle: tauri::AppHandle) -> Result<bool, String> {
// only one listener thread is allowed
if is_listening() {
return Err("Already listening.".into());
}
// vars
let porcupine: Porcupine;
let picovoice_api_key: String;
let selected_microphone: i32;
let mut start = SystemTime::now();
// Retrieve API key from DB
if let Some(pkey) = DB.lock().unwrap().get::<String>("api_key__picovoice") {
picovoice_api_key = pkey;
} else {
return Err("Picovoice API key is not set!".into());
}
// Create instance of Porcupine with the given API key
if let Ok(pinstance) =
PorcupineBuilder::new_with_keywords(picovoice_api_key, &[BuiltinKeywords::Jarvis])
.sensitivities(&[1.0f32]) // max sensitivity possible
.init()
{
// porcupine successfully initialized with the valid API key
porcupine = pinstance;
} else {
// something went wrong
return Err(
"Porcupine error: either API key is not valid or there is no internet connection"
.into(),
);
}
// Retrieve microphone index
if let Some(smic) = DB.lock().unwrap().get::<String>("selected_microphone") {
selected_microphone = smic.parse().unwrap_or(-1);
} else {
selected_microphone = -1; // use default, if not selected
}
// Create recorder instance
let recorder = RecorderBuilder::new()
.device_index(selected_microphone)
.frame_length(porcupine.frame_length() as i32)
.init()
.expect("Failed to initialize pvrecorder");
// Start recording
println!("Listening (microphone idx = {selected_microphone}) ...");
recorder.start().expect("Failed to start audio recording");
LISTENING.store(true, Ordering::SeqCst);
// Greet user
events::play("run", &app_handle);
// Listen until stop flag will be true
let mut frame_buffer = vec![0; porcupine.frame_length() as usize];
while !STOP_LISTENING.load(Ordering::SeqCst) {
recorder
.read(&mut frame_buffer)
.expect("Failed to read audio frame");
if let Ok(keyword_index) = porcupine.process(&frame_buffer) {
if keyword_index >= 0 {
println!("Yes, sir! {}", keyword_index);
events::play(
config::ASSISTANT_GREET_PHRASES
.choose(&mut rand::thread_rng())
.unwrap(),
&app_handle,
);
start = SystemTime::now();
app_handle
.emit_all(events::EventTypes::AssistantGreet.get(), ())
.unwrap();
loop {
recorder
.read(&mut frame_buffer)
.expect("Failed to read audio frame");
// vosk part (partials included)
if let Some(mut test) = vosk::recognize(&frame_buffer) {
if !test.is_empty() {
println!("Recognized: {}", test);
// some filtration
test = test.to_lowercase();
for tbr in config::ASSISTANT_PHRASES_TBR {
test = test.replace(tbr, "");
}
// infer command
if let Some((cmd_path, cmd_config)) =
assistant_commands::fetch_command(&test, &COMMANDS)
{
println!("Recognized (filtered): {}", test);
println!("Command found: {:?}", cmd_path);
println!("Executing ...");
let cmd_result = assistant_commands::execute_command(
&cmd_path,
&cmd_config,
&app_handle,
);
match cmd_result {
Ok(_) => {
println!("Command executed successfully!");
start = SystemTime::now(); // listen for more commands
continue;
}
Err(error_message) => {
println!("Error executing command: {}", error_message);
}
}
app_handle
.emit_all(events::EventTypes::AssistantWaiting.get(), ())
.unwrap();
break; // return to picovoice after command execution (no matter successfull or not)
}
}
}
match start.elapsed() {
Ok(elapsed) if elapsed > config::CMS_WAIT_DELAY => {
// return to picovoice after N seconds
app_handle
.emit_all(events::EventTypes::AssistantWaiting.get(), ())
.unwrap();
break;
}
_ => (),
}
}
}
}
}
// Stop listening
println!("Stop listening ...");
recorder.stop().expect("Failed to stop audio recording");
LISTENING.store(false, Ordering::SeqCst);
STOP_LISTENING.store(false, Ordering::SeqCst);
Ok(true)
}

View file

@ -0,0 +1,33 @@
use pv_recorder::RecorderBuilder;
#[tauri::command]
pub fn pv_get_audio_devices() -> Vec<String> {
let audio_devices = RecorderBuilder::default().get_audio_devices();
match audio_devices {
Ok(audio_devices) => audio_devices,
Err(err) => panic!("Failed to get audio devices: {}", err),
}
}
#[tauri::command]
pub fn pv_get_audio_device_name(idx: i32) -> String {
let audio_devices = RecorderBuilder::default().get_audio_devices();
let mut first_device: String = String::new();
match audio_devices {
Ok(audio_devices) => {
for (_idx, device) in audio_devices.iter().enumerate() {
if idx as usize == _idx {
return device.to_string();
}
if _idx == 0 {
first_device = device.to_string()
}
}
}
Err(err) => panic!("Failed to get audio devices: {}", err),
};
// return first device as default, if none were matched
first_device
}

View file

@ -0,0 +1,48 @@
use peak_alloc::PeakAlloc;
#[global_allocator]
static PEAK_ALLOC: PeakAlloc = PeakAlloc;
extern crate systemstat;
use std::thread;
use std::time::Duration;
use systemstat::{saturating_sub_bytes, Platform, System};
lazy_static! {
static ref SYS: System = System::new();
}
#[tauri::command]
pub fn get_current_ram_usage() -> String {
let result = String::from(format!("{}", PEAK_ALLOC.current_usage_as_mb()));
result
}
#[tauri::command]
pub fn get_peak_ram_usage() -> String {
let result = String::from(format!("{}", PEAK_ALLOC.peak_usage_as_gb()));
result
}
#[tauri::command]
pub fn get_cpu_temp() -> String {
if let Ok(cpu_temp) = SYS.cpu_temp() {
String::from(format!("{}", cpu_temp))
} else {
String::from("error")
}
}
// https://github.com/valpackett/systemstat/blob/trunk/examples/info.rs
#[tauri::command(async)]
pub async fn get_cpu_usage() -> String {
if let Ok(cpu) = SYS.cpu_load_aggregate() {
thread::sleep(Duration::from_secs(1));
let cpu = cpu.done().unwrap();
String::from(format!("{}", cpu.user * 100.0))
} else {
String::from("error")
}
}

View file

@ -0,0 +1,29 @@
use std::fs::File;
use std::io::BufReader;
use rodio::{Decoder, OutputStream, Sink};
#[tauri::command(async)]
pub fn play_sound(filename: &str, sleep: bool) {
// Get a output stream handle to the default physical sound device
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
// Load a sound from a file, using a path relative to Cargo.toml
// let filepath = format!("{PUBLIC_PATH}/sound/{filename}.wav");
let filepath = filename;
let file = BufReader::new(File::open(&filepath).unwrap());
// Decode that sound file into a source
let source = Decoder::new(file).unwrap();
// Play the sound directly on the device
println!("Playing {} ...", filepath);
// stream_handle.play_raw(source.convert_samples());
sink.append(source);
if sleep {
// The sound plays in a separate thread. This call will block the current thread until the sink
// has finished playing all its queued sounds.
sink.sleep_until_end();
}
}