diff --git a/assets/beep.ogg b/assets/beep.ogg new file mode 100644 index 0000000..3cc5b78 Binary files /dev/null and b/assets/beep.ogg differ diff --git a/src/config/mod.rs b/src/config/mod.rs index fd9eabb..4821756 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; pub struct Configuration { pub vm: VmOptions, pub debug: DebugOptions, + pub sound: SoundOptions, } #[derive(Clone, Deserialize, Serialize)] @@ -23,11 +24,17 @@ pub struct DebugOptions { pub enable_debug_menu: bool, } +#[derive(Clone, Deserialize, Serialize)] +pub struct SoundOptions { + pub volume: f32, +} + impl Default for Configuration { fn default() -> Self { Self { vm: VmOptions { cycles_per_frame: 10 }, debug: DebugOptions { enable_debug_menu: false }, + sound: SoundOptions { volume: 0.85 }, } } } diff --git a/src/ui/options.rs b/src/ui/options.rs index b672da5..b4b1055 100644 --- a/src/ui/options.rs +++ b/src/ui/options.rs @@ -1,6 +1,6 @@ use crate::ui::state::State; use notan::egui; -use notan::egui::Window; +use notan::egui::{Slider, Window}; pub fn option_dialog(state: &mut State, ctx: &egui::Context) { Window::new("Options") @@ -24,6 +24,14 @@ pub fn option_dialog(state: &mut State, ctx: &egui::Context) { } }); + ui.horizontal(|ui| { + ui.label("Sound volume"); + ui.add(Slider::new( + &mut state.configuration.sound.volume, + 0.0..=1.0, + )); + }); + ui.horizontal(|ui| { ui.checkbox( &mut state.configuration.debug.enable_debug_menu, diff --git a/src/ui/state.rs b/src/ui/state.rs index 1578e34..8dacad9 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -24,6 +24,9 @@ pub struct State { pub timer: f32, pub show_configuration_window: bool, pub cycles_per_frame: u32, + sound: Option, + beep_audio_source: AudioSource, + is_beeping: bool, } #[derive(Parser)] @@ -32,7 +35,7 @@ struct Args { file: OsString, } -pub fn setup(gfx: &mut Graphics) -> State { +pub fn setup(app: &mut App, gfx: &mut Graphics) -> State { let args = Args::parse(); let file_path = if args.file.is_empty() { @@ -78,6 +81,11 @@ pub fn setup(gfx: &mut Graphics) -> State { let config = Configuration::load().unwrap(); + let audio_source = app + .audio + .create_source(include_bytes!("../../assets/beep.ogg")) + .expect("Failed to create audio source"); + State { last_dir, configuration: config.clone(), @@ -92,6 +100,9 @@ pub fn setup(gfx: &mut Graphics) -> State { timer: 0.0, show_configuration_window: false, cycles_per_frame: config.vm.cycles_per_frame, + sound: None, + beep_audio_source: audio_source, + is_beeping: false, } } @@ -123,9 +134,24 @@ pub fn update(app: &mut App, state: &mut State) { if state.vm.delay_timer != 0 { state.vm.delay_timer -= 1; } - if state.vm.sound_timer != 0 { - // TODO: play sound here + + if state.vm.sound_timer > 0 { + if !state.is_beeping { + state.sound = Some(app.audio.play_sound( + &state.beep_audio_source, + state.configuration.sound.volume, + true, + )); + + state.is_beeping = true; + } + state.vm.sound_timer -= 1; + } else if state.is_beeping { + if let Some(sound) = &state.sound { + app.audio.stop(sound); + state.is_beeping = false; + } } }