Implement multiplayer #2
			
				
			
		
		
		
	| 
						 | 
					@ -238,6 +238,18 @@ impl GameWindow {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					            ui.menu("Audio", || {
 | 
				
			||||||
 | 
					                let p1_enabled = self.client.is_audio_enabled(SimId::Player1);
 | 
				
			||||||
 | 
					                let p2_enabled = self.client.is_audio_enabled(SimId::Player2);
 | 
				
			||||||
 | 
					                if ui.menu_item_config("Player 1").selected(p1_enabled).build() {
 | 
				
			||||||
 | 
					                    self.client
 | 
				
			||||||
 | 
					                        .send_command(EmulatorCommand::SetAudioEnabled(!p1_enabled, p2_enabled));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ui.menu_item_config("Player 2").selected(p2_enabled).build() {
 | 
				
			||||||
 | 
					                    self.client
 | 
				
			||||||
 | 
					                        .send_command(EmulatorCommand::SetAudioEnabled(p1_enabled, !p2_enabled));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
            ui.menu("Input", || {
 | 
					            ui.menu("Input", || {
 | 
				
			||||||
                if ui.menu_item("Bind Inputs") {
 | 
					                if ui.menu_item("Bind Inputs") {
 | 
				
			||||||
                    self.proxy.send_event(UserEvent::OpenInputWindow).unwrap();
 | 
					                    self.proxy.send_event(UserEvent::OpenInputWindow).unwrap();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,8 +12,8 @@ use std::{
 | 
				
			||||||
use anyhow::Result;
 | 
					use anyhow::Result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{audio::Audio, graphics::TextureSink};
 | 
					use crate::{audio::Audio, graphics::TextureSink};
 | 
				
			||||||
use shrooms_vb_core::Sim;
 | 
					 | 
				
			||||||
pub use shrooms_vb_core::VBKey;
 | 
					pub use shrooms_vb_core::VBKey;
 | 
				
			||||||
 | 
					use shrooms_vb_core::{Sim, EXPECTED_FRAME_SIZE};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod shrooms_vb_core;
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ pub struct EmulatorBuilder {
 | 
				
			||||||
    sim_count: Arc<AtomicUsize>,
 | 
					    sim_count: Arc<AtomicUsize>,
 | 
				
			||||||
    running: Arc<[AtomicBool; 2]>,
 | 
					    running: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    has_game: Arc<[AtomicBool; 2]>,
 | 
					    has_game: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
 | 
					    audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +53,7 @@ impl EmulatorBuilder {
 | 
				
			||||||
            sim_count: Arc::new(AtomicUsize::new(0)),
 | 
					            sim_count: Arc::new(AtomicUsize::new(0)),
 | 
				
			||||||
            running: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]),
 | 
					            running: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]),
 | 
				
			||||||
            has_game: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]),
 | 
					            has_game: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]),
 | 
				
			||||||
 | 
					            audio_on: Arc::new([AtomicBool::new(true), AtomicBool::new(true)]),
 | 
				
			||||||
            linked: Arc::new(AtomicBool::new(false)),
 | 
					            linked: Arc::new(AtomicBool::new(false)),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let client = EmulatorClient {
 | 
					        let client = EmulatorClient {
 | 
				
			||||||
| 
						 | 
					@ -59,6 +61,7 @@ impl EmulatorBuilder {
 | 
				
			||||||
            sim_count: builder.sim_count.clone(),
 | 
					            sim_count: builder.sim_count.clone(),
 | 
				
			||||||
            running: builder.running.clone(),
 | 
					            running: builder.running.clone(),
 | 
				
			||||||
            has_game: builder.has_game.clone(),
 | 
					            has_game: builder.has_game.clone(),
 | 
				
			||||||
 | 
					            audio_on: builder.audio_on.clone(),
 | 
				
			||||||
            linked: builder.linked.clone(),
 | 
					            linked: builder.linked.clone(),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        (builder, client)
 | 
					        (builder, client)
 | 
				
			||||||
| 
						 | 
					@ -77,6 +80,7 @@ impl EmulatorBuilder {
 | 
				
			||||||
            self.sim_count,
 | 
					            self.sim_count,
 | 
				
			||||||
            self.running,
 | 
					            self.running,
 | 
				
			||||||
            self.has_game,
 | 
					            self.has_game,
 | 
				
			||||||
 | 
					            self.audio_on,
 | 
				
			||||||
            self.linked,
 | 
					            self.linked,
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
        if let Some(path) = self.rom {
 | 
					        if let Some(path) = self.rom {
 | 
				
			||||||
| 
						 | 
					@ -93,6 +97,7 @@ pub struct Emulator {
 | 
				
			||||||
    sim_count: Arc<AtomicUsize>,
 | 
					    sim_count: Arc<AtomicUsize>,
 | 
				
			||||||
    running: Arc<[AtomicBool; 2]>,
 | 
					    running: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    has_game: Arc<[AtomicBool; 2]>,
 | 
					    has_game: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
 | 
					    audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
    renderers: HashMap<SimId, TextureSink>,
 | 
					    renderers: HashMap<SimId, TextureSink>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -103,6 +108,7 @@ impl Emulator {
 | 
				
			||||||
        sim_count: Arc<AtomicUsize>,
 | 
					        sim_count: Arc<AtomicUsize>,
 | 
				
			||||||
        running: Arc<[AtomicBool; 2]>,
 | 
					        running: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
        has_game: Arc<[AtomicBool; 2]>,
 | 
					        has_game: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
 | 
					        audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
        linked: Arc<AtomicBool>,
 | 
					        linked: Arc<AtomicBool>,
 | 
				
			||||||
    ) -> Result<Self> {
 | 
					    ) -> Result<Self> {
 | 
				
			||||||
        Ok(Self {
 | 
					        Ok(Self {
 | 
				
			||||||
| 
						 | 
					@ -112,6 +118,7 @@ impl Emulator {
 | 
				
			||||||
            sim_count,
 | 
					            sim_count,
 | 
				
			||||||
            running,
 | 
					            running,
 | 
				
			||||||
            has_game,
 | 
					            has_game,
 | 
				
			||||||
 | 
					            audio_on,
 | 
				
			||||||
            linked,
 | 
					            linked,
 | 
				
			||||||
            renderers: HashMap::new(),
 | 
					            renderers: HashMap::new(),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -210,15 +217,28 @@ impl Emulator {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            let weight = 1.0 / self.sims.len() as f32;
 | 
					            let p1_audio =
 | 
				
			||||||
            for sim in self.sims.iter_mut() {
 | 
					                p1_running && self.audio_on[SimId::Player1.to_index()].load(Ordering::Acquire);
 | 
				
			||||||
                sim.read_samples(&mut audio_samples, weight);
 | 
					            let p2_audio =
 | 
				
			||||||
 | 
					                p2_running && self.audio_on[SimId::Player2.to_index()].load(Ordering::Acquire);
 | 
				
			||||||
 | 
					            let weight = if p1_audio && p2_audio { 0.5 } else { 1.0 };
 | 
				
			||||||
 | 
					            if p1_audio {
 | 
				
			||||||
 | 
					                if let Some(sim) = self.sims.get_mut(SimId::Player1.to_index()) {
 | 
				
			||||||
 | 
					                    sim.read_samples(&mut audio_samples, weight);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if !audio_samples.is_empty() {
 | 
					            if p2_audio {
 | 
				
			||||||
 | 
					                if let Some(sim) = self.sims.get_mut(SimId::Player2.to_index()) {
 | 
				
			||||||
 | 
					                    sim.read_samples(&mut audio_samples, weight);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if audio_samples.is_empty() {
 | 
				
			||||||
 | 
					                audio_samples.resize(EXPECTED_FRAME_SIZE, 0.0);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
                idle = false;
 | 
					                idle = false;
 | 
				
			||||||
                self.audio.update(&audio_samples);
 | 
					 | 
				
			||||||
                audio_samples.clear();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            self.audio.update(&audio_samples);
 | 
				
			||||||
 | 
					            audio_samples.clear();
 | 
				
			||||||
            if idle {
 | 
					            if idle {
 | 
				
			||||||
                // The game is paused, and we have output all the video/audio we have.
 | 
					                // The game is paused, and we have output all the video/audio we have.
 | 
				
			||||||
                // Block the thread until a new command comes in.
 | 
					                // Block the thread until a new command comes in.
 | 
				
			||||||
| 
						 | 
					@ -274,6 +294,10 @@ impl Emulator {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::SetAudioEnabled(p1, p2) => {
 | 
				
			||||||
 | 
					                self.audio_on[SimId::Player1.to_index()].store(p1, Ordering::Release);
 | 
				
			||||||
 | 
					                self.audio_on[SimId::Player2.to_index()].store(p2, Ordering::Release);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::Link => {
 | 
					            EmulatorCommand::Link => {
 | 
				
			||||||
                self.link_sims();
 | 
					                self.link_sims();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -302,6 +326,7 @@ pub enum EmulatorCommand {
 | 
				
			||||||
    StopSecondSim,
 | 
					    StopSecondSim,
 | 
				
			||||||
    Pause,
 | 
					    Pause,
 | 
				
			||||||
    Resume,
 | 
					    Resume,
 | 
				
			||||||
 | 
					    SetAudioEnabled(bool, bool),
 | 
				
			||||||
    Link,
 | 
					    Link,
 | 
				
			||||||
    Unlink,
 | 
					    Unlink,
 | 
				
			||||||
    Reset(SimId),
 | 
					    Reset(SimId),
 | 
				
			||||||
| 
						 | 
					@ -314,6 +339,7 @@ pub struct EmulatorClient {
 | 
				
			||||||
    sim_count: Arc<AtomicUsize>,
 | 
					    sim_count: Arc<AtomicUsize>,
 | 
				
			||||||
    running: Arc<[AtomicBool; 2]>,
 | 
					    running: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    has_game: Arc<[AtomicBool; 2]>,
 | 
					    has_game: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
 | 
					    audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -330,6 +356,9 @@ impl EmulatorClient {
 | 
				
			||||||
    pub fn are_sims_linked(&self) -> bool {
 | 
					    pub fn are_sims_linked(&self) -> bool {
 | 
				
			||||||
        self.linked.load(Ordering::Acquire)
 | 
					        self.linked.load(Ordering::Acquire)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn is_audio_enabled(&self, sim_id: SimId) -> bool {
 | 
				
			||||||
 | 
					        self.audio_on[sim_id.to_index()].load(Ordering::Acquire)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    pub fn send_command(&self, command: EmulatorCommand) {
 | 
					    pub fn send_command(&self, command: EmulatorCommand) {
 | 
				
			||||||
        if let Err(err) = self.queue.send(command) {
 | 
					        if let Err(err) = self.queue.send(command) {
 | 
				
			||||||
            eprintln!(
 | 
					            eprintln!(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,6 +120,7 @@ extern "C" fn on_frame(sim: *mut VB) -> i32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
 | 
					const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
 | 
				
			||||||
const AUDIO_CAPACITY_FLOATS: usize = AUDIO_CAPACITY_SAMPLES * 2;
 | 
					const AUDIO_CAPACITY_FLOATS: usize = AUDIO_CAPACITY_SAMPLES * 2;
 | 
				
			||||||
 | 
					pub const EXPECTED_FRAME_SIZE: usize = 834 * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct VBState {
 | 
					struct VBState {
 | 
				
			||||||
    frame_seen: bool,
 | 
					    frame_seen: bool,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue