mirror of https://github.com/wayvr-org/wayvr.git
app.tick() + whisper auto-unload
This commit is contained in:
parent
6f8afea588
commit
3e8b2ace93
|
|
@ -20,7 +20,7 @@ use crate::subsystem::hid::WheelDelta;
|
|||
use crate::subsystem::input::KeyboardFocus;
|
||||
use crate::windowing::backend::OverlayEventData;
|
||||
use crate::windowing::manager::OverlayWindowManager;
|
||||
use crate::windowing::window::{self, OverlayWindowData, realign, scalar_scale, window_scale};
|
||||
use crate::windowing::window::{self, OverlayWindowData, realign, scalar_scale};
|
||||
use crate::windowing::{OverlayID, OverlaySelector};
|
||||
|
||||
use super::task::TaskType;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ use crate::{
|
|||
graphics::{GpuFutures, init_openvr_graphics},
|
||||
overlays::toast::Toast,
|
||||
state::AppState,
|
||||
subsystem::notifications::NotificationManager,
|
||||
windowing::{
|
||||
backend::{RenderResources, RenderTarget, ShouldRender},
|
||||
manager::OverlayWindowManager,
|
||||
|
|
@ -113,12 +112,11 @@ pub fn openvr_run(
|
|||
log::info!("IPD: {:.0} mm", app.input_state.ipd);
|
||||
}
|
||||
|
||||
app.late_init();
|
||||
|
||||
let _ = install_manifest(&mut app_mgr);
|
||||
|
||||
let mut overlays = OverlayWindowManager::<OpenVrOverlayData>::new(&mut app, headless)?;
|
||||
let mut notifications = NotificationManager::new();
|
||||
notifications.run_dbus(&mut app.dbus);
|
||||
notifications.run_udp();
|
||||
|
||||
let mut playspace = playspace::PlayspaceMover::new();
|
||||
playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr);
|
||||
|
|
@ -216,9 +214,7 @@ pub fn openvr_run(
|
|||
next_device_update = Instant::now() + Duration::from_secs(30);
|
||||
}
|
||||
|
||||
app.dbus.tick();
|
||||
notifications.submit_pending(&mut app);
|
||||
|
||||
app.tick();
|
||||
app.tasks.retrieve_due(&mut due_tasks);
|
||||
|
||||
while let Some(task) = due_tasks.pop_front() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ use crate::{
|
|||
graphics::{GpuFutures, init_openxr_graphics},
|
||||
overlays::{toast::Toast, watch::WATCH_NAME},
|
||||
state::AppState,
|
||||
subsystem::notifications::NotificationManager,
|
||||
windowing::{
|
||||
backend::{RenderResources, RenderTarget, ShouldRender},
|
||||
manager::OverlayWindowManager,
|
||||
|
|
@ -87,13 +86,9 @@ pub fn openxr_run(
|
|||
let mut lines = LinePool::new(&app)?;
|
||||
let mut current_lines = Vec::with_capacity(2);
|
||||
|
||||
let mut notifications = NotificationManager::new();
|
||||
notifications.run_dbus(&mut app.dbus);
|
||||
notifications.run_udp();
|
||||
|
||||
let mut delete_queue = vec![];
|
||||
|
||||
app.monado_state_init();
|
||||
app.late_init();
|
||||
|
||||
let mut playspace_mover = app.monado_state.as_mut().and_then(|m| {
|
||||
playspace::PlayspaceMover::new(&mut m.ipc)
|
||||
|
|
@ -476,9 +471,7 @@ pub fn openxr_run(
|
|||
)?;
|
||||
// End layer submit
|
||||
|
||||
app.dbus.tick();
|
||||
notifications.submit_pending(&mut app);
|
||||
|
||||
app.tick();
|
||||
app.tasks.retrieve_due(&mut due_tasks);
|
||||
while let Some(task) = due_tasks.pop_front() {
|
||||
match task {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
||||
use glam::Affine3A;
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
event::EventCallback,
|
||||
|
|
@ -38,11 +38,9 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
pub const WHISPER_NAME: &str = "whisper";
|
||||
const WHISPER_NAME: &str = "whisper";
|
||||
|
||||
#[derive(Default)]
|
||||
struct WhisperState {
|
||||
whisper_sst: Option<WhisperStt>,
|
||||
clipboard_provider: Option<Box<dyn ClipboardProvider>>,
|
||||
last_transcription: Option<Rc<str>>,
|
||||
}
|
||||
|
|
@ -92,7 +90,7 @@ pub fn create_whisper(
|
|||
|
||||
let state = WhisperState {
|
||||
clipboard_provider,
|
||||
..Default::default()
|
||||
last_transcription: None,
|
||||
};
|
||||
let xml = "gui/whisper.xml";
|
||||
|
||||
|
|
@ -118,18 +116,18 @@ pub fn create_whisper(
|
|||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, WhisperState> = match command {
|
||||
"::WhisperTranscribeStart" => Box::new(move |_common, data, app, state| {
|
||||
"::WhisperTranscribeStart" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let whisper = match state.whisper_sst.as_mut() {
|
||||
let whisper = match app.whisper_sst.as_mut() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
let model_path = data_dir::get_path("whisper")
|
||||
.join(app.session.config.whisper_model.as_ref());
|
||||
if model_path.is_file() {
|
||||
state.whisper_sst = match WhisperStt::new(model_path)
|
||||
app.whisper_sst = match WhisperStt::new(model_path)
|
||||
.log_err("Error while starting Whisper engine")
|
||||
{
|
||||
Ok(x) => Some(x),
|
||||
|
|
@ -157,7 +155,7 @@ pub fn create_whisper(
|
|||
return Ok(EventResult::Consumed);
|
||||
}
|
||||
|
||||
state.whisper_sst.as_mut().unwrap()
|
||||
app.whisper_sst.as_mut().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -167,12 +165,12 @@ pub fn create_whisper(
|
|||
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::WhisperTranscribeStop" => Box::new(move |_common, data, app, state| {
|
||||
"::WhisperTranscribeStop" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
if let Some(whisper) = state.whisper_sst.as_mut() {
|
||||
if let Some(whisper) = app.whisper_sst.as_mut() {
|
||||
let _ = whisper
|
||||
.ptt_end()
|
||||
.log_err("Could not stop Whisper transcription");
|
||||
|
|
@ -240,12 +238,12 @@ pub fn create_whisper(
|
|||
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::WhisperUnloadAndClose" => Box::new(move |_common, data, app, state| {
|
||||
"::WhisperUnloadAndClose" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
state.whisper_sst = None;
|
||||
app.whisper_sst = None;
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleOverlay(
|
||||
OverlaySelector::Name(WHISPER_NAME.into()),
|
||||
|
|
@ -297,14 +295,14 @@ pub fn create_whisper(
|
|||
.push(GuiTimer::new(Duration::from_millis(100), 0));
|
||||
|
||||
let on_label_tick: EventCallback<AppState, WhisperState> =
|
||||
Box::new(move |common, data, _app, state| {
|
||||
if let Some(whisper_stt) = state.whisper_sst.as_mut()
|
||||
&& let Some(text) = whisper_stt.take_transcription()
|
||||
{
|
||||
let text: Rc<str> = text.into();
|
||||
state.last_transcription = Some(text.clone());
|
||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
||||
label.set_text(common, Translation::from_raw_text_rc(text));
|
||||
Box::new(move |common, data, app, state| {
|
||||
if let Some(whisper_stt) = app.whisper_sst.as_mut() {
|
||||
if let Some(text) = whisper_stt.take_transcription() {
|
||||
let text: Rc<str> = text.into();
|
||||
state.last_transcription = Some(text.clone());
|
||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
||||
label.set_text(common, Translation::from_raw_text_rc(text));
|
||||
}
|
||||
}
|
||||
Ok(EventResult::Pass)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,9 +22,12 @@ use wlx_common::{
|
|||
use crate::backend;
|
||||
use crate::backend::wayvr::WvrServerState;
|
||||
|
||||
use crate::subsystem::notifications::NotificationManager;
|
||||
#[cfg(feature = "osc")]
|
||||
use crate::subsystem::osc::OscSender;
|
||||
|
||||
#[cfg(feature = "whisper")]
|
||||
use crate::subsystem::whisper_stt::WhisperStt;
|
||||
use crate::{
|
||||
backend::{XrBackend, input::InputState, task::TaskContainer},
|
||||
config::load_general_config,
|
||||
|
|
@ -45,6 +48,8 @@ pub struct AppState {
|
|||
pub audio_system: audio::AudioSystem,
|
||||
pub audio_sample_player: audio::SamplePlayer,
|
||||
|
||||
pub notifications: NotificationManager,
|
||||
|
||||
pub wgui_shared: WSharedContext,
|
||||
|
||||
pub input_state: InputState,
|
||||
|
|
@ -69,6 +74,9 @@ pub struct AppState {
|
|||
|
||||
pub wvr_server: Option<WvrServerState>,
|
||||
|
||||
#[cfg(feature = "whisper")]
|
||||
pub whisper_sst: Option<WhisperStt>,
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
pub monado_state: Option<backend::openxr::monado_state::MonadoState>,
|
||||
|
||||
|
|
@ -182,11 +190,14 @@ impl AppState {
|
|||
ipc_server,
|
||||
wayvr_signals: wvr_signals,
|
||||
desktop_finder,
|
||||
notifications: NotificationManager::new(),
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
osc_sender,
|
||||
|
||||
wvr_server,
|
||||
#[cfg(feature = "whisper")]
|
||||
whisper_sst: None,
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
monado_state: None,
|
||||
|
|
@ -200,19 +211,38 @@ impl AppState {
|
|||
Ok(app_state)
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
pub fn monado_state_init(&mut self) {
|
||||
use crate::backend::openxr::monado_state::MonadoState;
|
||||
pub fn late_init(&mut self) {
|
||||
self.notifications.run_dbus(&mut self.dbus);
|
||||
self.notifications.run_udp();
|
||||
|
||||
log::debug!("Connecting to Monado IPC");
|
||||
self.monado_state = None; // stop connection first
|
||||
if matches!(self.xr_backend, XrBackend::OpenXR) {
|
||||
use crate::backend::openxr::monado_state::MonadoState;
|
||||
|
||||
match MonadoState::new() {
|
||||
Ok(m) => {
|
||||
self.monado_state = Some(m);
|
||||
log::debug!("Connecting to Monado IPC");
|
||||
self.monado_state = None; // stop connection first
|
||||
|
||||
match MonadoState::new() {
|
||||
Ok(m) => {
|
||||
self.monado_state = Some(m);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Will not use libmonado: {e:?}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Will not use libmonado: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
self.dbus.tick();
|
||||
|
||||
for toast in self.notifications.drain_pending(&self.session) {
|
||||
toast.submit(self);
|
||||
}
|
||||
|
||||
#[cfg(feature = "whisper")]
|
||||
{
|
||||
if self.whisper_sst.as_ref().is_some_and(|x| x.should_unload()) {
|
||||
self.whisper_sst = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use std::{
|
|||
};
|
||||
use wlx_common::overlays::ToastTopic;
|
||||
|
||||
use crate::{overlays::toast::Toast, state::AppState, subsystem::dbus::DbusConnector};
|
||||
use crate::{overlays::toast::Toast, state::AppSession, subsystem::dbus::DbusConnector};
|
||||
|
||||
pub struct NotificationManager {
|
||||
rx_toast: mpsc::Receiver<Toast>,
|
||||
|
|
@ -29,14 +29,14 @@ impl NotificationManager {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn submit_pending(&self, app: &mut AppState) {
|
||||
if app.session.config.notifications_enabled {
|
||||
self.rx_toast.try_iter().for_each(|toast| {
|
||||
toast.submit(app);
|
||||
});
|
||||
pub fn drain_pending(&self, session: &AppSession) -> Vec<Toast> {
|
||||
if session.config.notifications_enabled {
|
||||
self.rx_toast.try_iter().collect()
|
||||
} else {
|
||||
// consume without submitting
|
||||
self.rx_toast.try_iter().last();
|
||||
for _ in self.rx_toast.try_iter() {}
|
||||
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use wlx_common::audio::rodio::{
|
|||
|
||||
const WHISPER_SAMPLE_RATE: usize = 16_000;
|
||||
const MAX_DURATION: Duration = Duration::from_secs(30);
|
||||
const UNLOAD_AFTER: Duration = Duration::from_mins(5);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WhisperSttConfig {
|
||||
|
|
@ -107,6 +108,7 @@ pub struct WhisperStt {
|
|||
completed_tx: mpsc::Sender<Result<String, String>>,
|
||||
|
||||
last_error: Option<String>,
|
||||
unload_at: Instant,
|
||||
}
|
||||
|
||||
impl WhisperStt {
|
||||
|
|
@ -133,11 +135,13 @@ impl WhisperStt {
|
|||
completed_rx,
|
||||
completed_tx,
|
||||
last_error: None,
|
||||
unload_at: Instant::now() + UNLOAD_AFTER,
|
||||
})
|
||||
}
|
||||
|
||||
/// starts a fresh capture stream and a transcription worker
|
||||
pub fn ptt_start(&mut self) -> Result<(), WhisperSttError> {
|
||||
self.unload_at = Instant::now() + UNLOAD_AFTER;
|
||||
self.reap_finished_recognizers();
|
||||
|
||||
if self.active.is_some() {
|
||||
|
|
@ -237,6 +241,7 @@ impl WhisperStt {
|
|||
/// stops the pw stream & finalizes recognition asynchronously
|
||||
/// poll `take_transcription()` from your main loop to receive transcription
|
||||
pub fn ptt_end(&mut self) -> Result<(), WhisperSttError> {
|
||||
self.unload_at = Instant::now() + UNLOAD_AFTER;
|
||||
self.stop_active_capture()
|
||||
}
|
||||
|
||||
|
|
@ -246,6 +251,7 @@ impl WhisperStt {
|
|||
let latest = self.drain_completed_transcriptions();
|
||||
|
||||
if latest.is_some() {
|
||||
self.unload_at = Instant::now() + UNLOAD_AFTER;
|
||||
return latest;
|
||||
}
|
||||
|
||||
|
|
@ -263,12 +269,8 @@ impl WhisperStt {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn take_error(&mut self) -> Option<String> {
|
||||
self.last_error.take()
|
||||
}
|
||||
|
||||
pub fn is_recording(&self) -> bool {
|
||||
self.active.is_some()
|
||||
pub fn should_unload(&self) -> bool {
|
||||
self.unload_at < Instant::now()
|
||||
}
|
||||
|
||||
fn reap_finished_recognizers(&mut self) {
|
||||
|
|
|
|||
|
|
@ -290,14 +290,6 @@ pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32) {
|
|||
transform.matrix3 = Mat3A::from_cols(col_x, col_y, col_z).mul_scalar(scale) * rot;
|
||||
}
|
||||
|
||||
pub fn window_scale(state: &OverlayWindowState) -> f32 {
|
||||
state
|
||||
.saved_transform
|
||||
.as_ref()
|
||||
.map(scalar_scale)
|
||||
.unwrap_or_else(|| scalar_scale(&state.transform))
|
||||
}
|
||||
|
||||
pub fn scalar_scale(a: &Affine3A) -> f32 {
|
||||
let det = a.matrix3.determinant();
|
||||
(a.matrix3.x_axis.length() * det.signum()
|
||||
|
|
|
|||
Loading…
Reference in New Issue