diff --git a/apu2a03/src/apu2a03.rs b/apu2a03/src/apu2a03.rs index ec9fa67..b63dd64 100644 --- a/apu2a03/src/apu2a03.rs +++ b/apu2a03/src/apu2a03.rs @@ -40,6 +40,8 @@ pub struct APU2A03 { filter: Filter, filter_counter: u8, + + player: rodio::Sink, } impl APU2A03 { @@ -54,6 +56,16 @@ impl APU2A03 { let noise = Rc::new(RefCell::new(LengthCountedChannel::new(NoiseWave::new()))); let dmc = Rc::new(RefCell::new(Dmc::new())); + let buffered_channel = Arc::new(Mutex::new(BufferedChannel::new())); + + let device = rodio::default_output_device().unwrap(); + let sink = rodio::Sink::new(&device); + + sink.append(APUChannelPlayer::from_clone(buffered_channel.clone())); + sink.set_volume(0.15); + + sink.pause(); + Self { square_pulse_1: square_pulse_1.clone(), square_pulse_2: square_pulse_2.clone(), @@ -64,7 +76,7 @@ impl APU2A03 { dmc: dmc.clone(), - buffered_channel: Arc::new(Mutex::new(BufferedChannel::new())), + buffered_channel, mixer: Mixer::new( square_pulse_1.clone(), @@ -91,6 +103,8 @@ impl APU2A03 { filter: Filter::new(), filter_counter: 0, + + player: sink, } } @@ -416,14 +430,11 @@ impl APU2A03 { } pub fn play(&self) { - let device = rodio::default_output_device().unwrap(); - let sink = rodio::Sink::new(&device); - - sink.append(APUChannelPlayer::from_clone(self.buffered_channel.clone())); - sink.set_volume(0.15); + self.player.play() + } - sink.play(); - sink.detach(); + pub fn pause(&self) { + self.player.pause(); } fn length_counter_decrement(channel: &mut Rc>>) { diff --git a/nes_ui_base/src/lib.rs b/nes_ui_base/src/lib.rs index 552d86e..ee1f0dc 100644 --- a/nes_ui_base/src/lib.rs +++ b/nes_ui_base/src/lib.rs @@ -12,6 +12,8 @@ use std::sync::{mpsc::Sender, Arc, Mutex}; pub enum UiEvent { Exit, Reset, + Pause, + Resume, LoadRom(String), } diff --git a/nes_ui_base/src/nes.rs b/nes_ui_base/src/nes.rs index 01d9168..c6a520b 100644 --- a/nes_ui_base/src/nes.rs +++ b/nes_ui_base/src/nes.rs @@ -256,6 +256,14 @@ impl NES

{ break; } } + UiEvent::Pause => { + self.paused = true; + self.apu.borrow_mut().pause(); + } + UiEvent::Resume => { + self.paused = false; + self.apu.borrow_mut().play(); + } } } diff --git a/nes_ui_gtk/src/main.rs b/nes_ui_gtk/src/main.rs index 6daafde..eec85fe 100644 --- a/nes_ui_gtk/src/main.rs +++ b/nes_ui_gtk/src/main.rs @@ -7,9 +7,9 @@ fn main() { let args = args().collect::>(); let mut nes = if args.len() >= 2 { - NES::new(&args[1], GtkProvider {}).expect("") + NES::new(&args[1], GtkProvider::new()).unwrap() } else { - NES::new_without_file(GtkProvider {}) + NES::new_without_file(GtkProvider::new()) }; nes.run(); diff --git a/nes_ui_gtk/src/ui.rs b/nes_ui_gtk/src/ui.rs index ba19cea..e908eda 100644 --- a/nes_ui_gtk/src/ui.rs +++ b/nes_ui_gtk/src/ui.rs @@ -4,7 +4,7 @@ use nes_ui_base::{ nes_display::Color as NESColor, UiEvent, UiProvider, }; -use std::sync::{mpsc::Sender, Arc, Mutex}; +use std::sync::{atomic::AtomicBool, atomic::Ordering, mpsc::Sender, Arc, Mutex}; use gdk::enums::key; use gdk::{keyval_to_upper, DragAction, ModifierType}; @@ -15,7 +15,17 @@ use gtk::{ FileFilter, Inhibit, MenuItem, ResponseType, TargetEntry, TargetFlags, Window, }; -pub struct GtkProvider {} +pub struct GtkProvider { + paused: Arc, +} + +impl GtkProvider { + pub fn new() -> Self { + Self { + paused: Arc::new(AtomicBool::new(false)), + } + } +} impl UiProvider for GtkProvider { fn get_tv_color_converter() -> fn(&NESColor) -> [u8; 4] { @@ -42,6 +52,10 @@ impl UiProvider for GtkProvider { let menu_action_open = builder.get_object::("menu_action_open").unwrap(); let menu_action_quit = builder.get_object::("menu_action_quit").unwrap(); let menu_action_reset = builder.get_object::("menu_action_reset").unwrap(); + let menu_action_pause = builder.get_object::("menu_action_pause").unwrap(); + let menu_action_resume = builder + .get_object::("menu_action_resume") + .unwrap(); window.show_all(); @@ -90,6 +104,7 @@ impl UiProvider for GtkProvider { let ctrl_state_clone = ctrl_state.clone(); let ui_to_nes_sender_clone = ui_to_nes_sender.clone(); + let paused_clone = self.paused.clone(); window.connect_key_press_event(move |_, event| { let mut ctrl = ctrl_state_clone.lock().unwrap(); @@ -105,6 +120,15 @@ impl UiProvider for GtkProvider { key::R if event.get_state().intersects(ModifierType::CONTROL_MASK) => { ui_to_nes_sender_clone.send(UiEvent::Reset).unwrap() } + key::Escape => { + if paused_clone.load(Ordering::Relaxed) { + ui_to_nes_sender_clone.send(UiEvent::Resume).unwrap(); + paused_clone.store(false, Ordering::Relaxed); + } else { + ui_to_nes_sender_clone.send(UiEvent::Pause).unwrap(); + paused_clone.store(true, Ordering::Relaxed); + } + } _ => {} } @@ -162,6 +186,20 @@ impl UiProvider for GtkProvider { ui_to_nes_sender_clone.send(UiEvent::Reset).unwrap(); }); + let ui_to_nes_sender_clone = ui_to_nes_sender.clone(); + let paused_clone = self.paused.clone(); + menu_action_pause.connect_activate(move |_| { + ui_to_nes_sender_clone.send(UiEvent::Pause).unwrap(); + paused_clone.store(true, Ordering::Relaxed); + }); + + let ui_to_nes_sender_clone = ui_to_nes_sender.clone(); + let paused_clone = self.paused.clone(); + menu_action_resume.connect_activate(move |_| { + ui_to_nes_sender_clone.send(UiEvent::Resume).unwrap(); + paused_clone.store(false, Ordering::Relaxed); + }); + let app_clone = app.clone(); menu_action_quit.connect_activate(move |_| app_clone.quit()); diff --git a/nes_ui_gtk/ui.glade b/nes_ui_gtk/ui.glade index 2f15d13..3bdf116 100644 --- a/nes_ui_gtk/ui.glade +++ b/nes_ui_gtk/ui.glade @@ -68,6 +68,22 @@ True + + + True + False + _Pause + True + + + + + True + False + _Resume + True + +