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