Config notification/toast sound (#179)

* try adding config to load different sound

looks like include_bytes!() doesn't take a variable parameter, so will need a different solution lol.

* no errors in loading, but toast.rs now has a lifetime issue

* hey, config toast sound works now!

the sound is loaded in AppState now, since Toast are created in several different places at different times, and i don't want to load the sound every time a toast is made.

also put back the original sound since i accidentally commited my custom one.

* clean up

* change custom toast sound leak to happen on load rather than every play

* move toast sound loading into a function

so it can be reused in the future, for example if we want to load arbitrary keypress sounds or other [u8] data.

* remove label from try_load_bytes, return early when failing
This commit is contained in:
Jay
2025-03-24 22:32:29 +11:00
committed by GitHub
parent 659f1492fb
commit 5f93bc9cac
3 changed files with 47 additions and 4 deletions

View File

@@ -207,6 +207,9 @@ pub struct GeneralConfig {
#[serde(default = "def_toast_topics")] #[serde(default = "def_toast_topics")]
pub notification_topics: IdMap<ToastTopic, DisplayMethod>, pub notification_topics: IdMap<ToastTopic, DisplayMethod>,
#[serde(default = "def_empty")]
pub notification_sound: Arc<str>,
#[serde(default = "def_true")] #[serde(default = "def_true")]
pub keyboard_sound_enabled: bool, pub keyboard_sound_enabled: bool,

View File

@@ -18,7 +18,6 @@ use crate::{
const FONT_SIZE: isize = 16; const FONT_SIZE: isize = 16;
const PADDING: (f32, f32) = (25., 7.); const PADDING: (f32, f32) = (25., 7.);
const PIXELS_TO_METERS: f32 = 1. / 2000.; const PIXELS_TO_METERS: f32 = 1. / 2000.;
const TOAST_AUDIO_WAV: &[u8] = include_bytes!("../res/557297.wav");
static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into()); static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into());
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
@@ -78,6 +77,9 @@ impl Toast {
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout)); let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
let has_sound = self.sound && app.session.config.notifications_sound_enabled; let has_sound = self.sound && app.session.config.notifications_sound_enabled;
if has_sound {
app.audio.play(app.toast_sound);
}
// drop any toast that was created before us. // drop any toast that was created before us.
// (DropOverlay only drops overlays that were // (DropOverlay only drops overlays that were
@@ -109,9 +111,6 @@ impl Toast {
instant, instant,
); );
if has_sound {
app.audio.play(TOAST_AUDIO_WAV);
}
} }
} }

View File

@@ -51,6 +51,7 @@ pub struct AppState {
pub anchor: Affine3A, pub anchor: Affine3A,
pub sprites: AStrMap<Arc<ImageView>>, pub sprites: AStrMap<Arc<ImageView>>,
pub keyboard_focus: KeyboardFocus, pub keyboard_focus: KeyboardFocus,
pub toast_sound: &'static [u8],
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
pub osc_sender: Option<OscSender>, pub osc_sender: Option<OscSender>,
@@ -111,6 +112,11 @@ impl AppState {
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok(); let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok();
let toast_sound_wav = AppState::try_load_bytes(
&session.config.notification_sound,
include_bytes!("res/557297.wav")
);
Ok(AppState { Ok(AppState {
fc: FontCache::new(session.config.primary_font.clone())?, fc: FontCache::new(session.config.primary_font.clone())?,
session, session,
@@ -123,6 +129,7 @@ impl AppState {
anchor: Affine3A::IDENTITY, anchor: Affine3A::IDENTITY,
sprites: AStrMap::new(), sprites: AStrMap::new(),
keyboard_focus: KeyboardFocus::PhysicalScreen, keyboard_focus: KeyboardFocus::PhysicalScreen,
toast_sound: toast_sound_wav,
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
osc_sender, osc_sender,
@@ -145,6 +152,40 @@ impl AppState {
Ok(wayvr) Ok(wayvr)
} }
} }
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8]
{
if path.is_empty() {
return fallback_data;
}
let real_path = config_io::get_config_root().join(&*path);
if std::fs::File::open(real_path.clone()).is_err() {
log::warn!(
"Could not open file at: {}",
path
);
return fallback_data;
};
return match std::fs::read(real_path.clone()){
// Box is used here to work around `f`'s limited lifetime
Ok(f) => {
Box::leak(Box::new(f)).as_slice()
},
Err(e) => {
log::warn!(
"Failed to read file at: {}",
path
);
log::warn!("{:?}", e);
fallback_data
}
};
}
} }
pub struct AppSession { pub struct AppSession {