Connected the web ui controller inputs to sim

This commit is contained in:
transistor 2023-05-07 14:57:45 -07:00
parent 6f871a3b0b
commit 6389fa0482
8 changed files with 120 additions and 114 deletions

View File

@ -15,6 +15,7 @@ pub fn event_queue<T>() -> (EventSender<T>, EventReceiver<T>) {
(sender, receiver) (sender, receiver)
} }
#[derive(Clone)]
pub struct EventSender<T> { pub struct EventSender<T> {
queue: Arc<Mutex<VecDeque<T>>>, queue: Arc<Mutex<VecDeque<T>>>,
} }
@ -23,10 +24,6 @@ impl<T> EventSender<T> {
pub fn send(&self, event: T) { pub fn send(&self, event: T) {
self.queue.lock().unwrap().push_back(event); self.queue.lock().unwrap().push_back(event);
} }
//pub fn send_at_instant(&self, instant: Instant, event: T) {
// self.queue.lock().unwrap().push_back((instant, event));
//}
} }
pub struct EventReceiver<T> { pub struct EventReceiver<T> {

View File

@ -24,11 +24,12 @@
<div id="video-screen"> <div id="video-screen">
<canvas id="video" tabindex="0" draw-raw-handle="1" /> <canvas id="video" tabindex="0" draw-raw-handle="1" />
</div> </div>
<div id="controller"> <div id="controller">
<button name="left">&#x21E6;</button> <button name="left">&#x21E6;</button>
<button name="up">&#x21E7;</button> <button name="up">&#x21E7;</button>
<button name="down">&#x21E8;</button> <button name="down">&#x21E9;</button>
<button name="right">&#x21E9;</button> <button name="right">&#x21E8;</button>
<button name="start">Start</button> <button name="start">Start</button>

View File

@ -1,49 +1,70 @@
import * as Emulator from './moa-genesis.js'; import * as Emulator from './moa-genesis.js';
function initialize_emulator() {
let host = Emulator.new_host();
let system = Emulator.load_system(host, Emulator.get_load_system_fn());
//Emulator.start_system(system);
let last_update = performance.now();
setTimeout(function refreshFrame() {
let current = performance.now();
let diff = current - last_update;
//let remaining = Math.max((16 * Emulator.get_speed()) - diff, 0);
last_update = current;
let runtime = Emulator.run_system_for(system, diff * 1_000_000);
if (Emulator.is_running()) {
let remaining = Math.max(diff - runtime - (diff * 0.1), 1);
setTimeout(refreshFrame, remaining);
}
}, 0);
Emulator.host_run_loop(host);
}
// Update the frame rate display
var frame_rate_el = document.getElementById("frame-rate");
var frame_rate = setInterval(function () {
frame_rate_el.value = Emulator.get_frames_since();
}, 1000);
window.addEventListener("load", () => { window.addEventListener("load", () => {
Emulator.default(); Emulator.default();
}); });
function initialize_emulator() {
const host = Emulator.new_host();
const system = Emulator.load_system(host, Emulator.get_load_system_fn());
//Emulator.start_system(system);
let last_update = performance.now();
setTimeout(function refreshFrame() {
// Calculate the time difference since the last update cycle
const current = performance.now();
const diff = current - last_update;
last_update = current;
// Run the system for the difference, and get the realtime runtime in millis
const runtime = Emulator.run_system_for(system, diff * 1_000_000);
if (Emulator.is_running()) {
// Calculate the timeout needed to fill the time that was *not* taken by the sim
const remaining = Math.max(diff - runtime - (diff * 0.1), 1);
setTimeout(refreshFrame, remaining);
}
}, 0);
const controllers = Emulator.get_controllers(host);
function button_event(e) {
let state;
if (e.type == 'mousedown' || e.type == 'touchstart') {
state = true;
} else {
state = false;
}
Emulator.button_press(controllers, e.target.name, state);
}
document.getElementById("controller").querySelectorAll('button').forEach(function (button) {
button.addEventListener('mousedown', button_event);
button.addEventListener('mouseup', button_event);
button.addEventListener('touchstart', button_event);
button.addEventListener('touchend', button_event);
});
Emulator.host_run_loop(host);
}
// Update the frame rate display
const frame_rate_el = document.getElementById("frame-rate");
const frame_rate = setInterval(function () {
frame_rate_el.value = Emulator.get_frames_since();
}, 1000);
// Load a new ROM file // Load a new ROM file
var reader = new FileReader(); const reader = new FileReader();
reader.onloadend = function (e) { reader.onloadend = function (e) {
var data = new Uint8Array(reader.result); let data = new Uint8Array(reader.result);
// If the SMD file magic number is present, then convert it before loading // If the SMD file magic number is present, then convert it before loading
if (data[8] == 0xAA && data[9] == 0xBB) if (data[8] == 0xAA && data[9] == 0xBB)
data = Emulator.smd_to_bin(data); data = Emulator.smd_to_bin(data);
Emulator.set_rom_data(data); Emulator.set_rom_data(data);
}; };
var file_input = document.getElementById("rom-file"); const file_input = document.getElementById("rom-file");
file_input.addEventListener("change", e => { file_input.addEventListener("change", e => {
document.getElementById("video").focus(); document.getElementById("video").focus();
reader.readAsArrayBuffer(file_input.files[0]) reader.readAsArrayBuffer(file_input.files[0])
@ -62,8 +83,8 @@ document.getElementById("power").addEventListener("click", () => {
initialize_emulator(); initialize_emulator();
}); });
var mute_state = false; let mute_state = false;
var mute = document.getElementById("mute"); const mute = document.getElementById("mute");
mute.addEventListener("click", () => { mute.addEventListener("click", () => {
mute_state = !mute_state; mute_state = !mute_state;
if (mute_state) { if (mute_state) {
@ -74,19 +95,3 @@ mute.addEventListener("click", () => {
Emulator.set_mute(mute_state); Emulator.set_mute(mute_state);
}); });
function button_event(e) {
var state;
if (e.type == 'mousedown' || e.type == 'touchstart') {
state = true;
} else {
state = false;
}
Emulator.button_press(e.target.name, state);
}
document.getElementById("controller").querySelectorAll('button').forEach(function (button) {
button.addEventListener('mousedown', button_event);
button.addEventListener('mouseup', button_event);
button.addEventListener('touchstart', button_event);
button.addEventListener('touchend', button_event);
});

View File

@ -25,6 +25,19 @@ body {
cursor: pointer; cursor: pointer;
} }
#controller {
width: 100%;
display: flex;
}
#controller button {
flex: 1;
width: 100%;
height: 40px;
margin-top: 2px;
margin-bottom: 2px;
}
#controller .updown { #controller .updown {
overflow: hidden; overflow: hidden;
} }

View File

@ -1,4 +1,6 @@
use std::rc::Rc;
use instant::Instant; use instant::Instant;
use pixels::{Pixels, SurfaceTexture}; use pixels::{Pixels, SurfaceTexture};
use winit::event::{Event, VirtualKeyCode, WindowEvent, ElementState}; use winit::event::{Event, VirtualKeyCode, WindowEvent, ElementState};
@ -38,6 +40,10 @@ impl PixelsFrontend {
pub fn get_mixer(&self) -> AudioMixer { pub fn get_mixer(&self) -> AudioMixer {
self.mixer.clone() self.mixer.clone()
} }
pub fn get_controllers(&self) -> Option<EventSender<ControllerEvent>> {
self.controllers.clone()
}
} }
impl Host for PixelsFrontend { impl Host for PixelsFrontend {

View File

@ -5,12 +5,13 @@ use winit::dpi::LogicalSize;
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
use winit::window::{Window, WindowBuilder}; use winit::window::{Window, WindowBuilder};
use crate::frontend::{self, LoadSystemFn}; use crate::frontend::{self, LoadSystemFn, PixelsFrontend};
pub fn start(load: LoadSystemFn) { pub fn start(load: LoadSystemFn) {
env_logger::init(); env_logger::init();
pollster::block_on(frontend::run(load)); let host = PixelsFrontend::new();
pollster::block_on(frontend::run_loop(host));
} }
pub fn create_window<T>(event_loop: &EventLoop<T>) -> Rc<Window> { pub fn create_window<T>(event_loop: &EventLoop<T>) -> Rc<Window> {

View File

@ -13,6 +13,7 @@ use wasm_bindgen::JsCast;
use wasm_bindgen::closure::Closure; use wasm_bindgen::closure::Closure;
use moa_core::{ClockDuration, System, wrap_transmutable}; use moa_core::{ClockDuration, System, wrap_transmutable};
use moa_core::host::{ControllerInput, ControllerDevice, ControllerEvent, EventSender};
use crate::settings; use crate::settings;
use crate::frontend::{self, PixelsFrontend, LoadSystemFn}; use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
@ -72,19 +73,41 @@ pub fn set_mute(mute: bool) {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn button_press(name: String, state: bool) { pub fn button_press(handle: &ControllersHandle, name: String, state: bool) {
let input = match name.as_str() {
"up" => Some(ControllerInput::DpadUp(state)),
"down" => Some(ControllerInput::DpadDown(state)),
"left" => Some(ControllerInput::DpadLeft(state)),
"right" => Some(ControllerInput::DpadRight(state)),
"start" => Some(ControllerInput::Start(state)),
"a" => Some(ControllerInput::ButtonA(state)),
"b" => Some(ControllerInput::ButtonB(state)),
"c" => Some(ControllerInput::ButtonC(state)),
_ => None
};
if let Some(input) = input {
handle.0.send(ControllerEvent::new(ControllerDevice::A, input));
}
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct HostHandle(PixelsFrontend); pub struct HostHandle(PixelsFrontend);
#[wasm_bindgen]
pub struct ControllersHandle(EventSender<ControllerEvent>);
#[wasm_bindgen] #[wasm_bindgen]
pub fn new_host() -> HostHandle { pub fn new_host() -> HostHandle {
HostHandle(PixelsFrontend::new()) HostHandle(PixelsFrontend::new())
} }
#[wasm_bindgen]
pub fn get_controllers(handle: &HostHandle) -> ControllersHandle {
ControllersHandle(handle.0.get_controllers().unwrap())
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn host_run_loop(handle: HostHandle) { pub fn host_run_loop(handle: HostHandle) {
wasm_bindgen_futures::spawn_local(frontend::run_loop(handle.0)); wasm_bindgen_futures::spawn_local(frontend::run_loop(handle.0));
@ -196,50 +219,6 @@ pub fn create_window<T>(event_loop: &EventLoop<T>) -> Rc<Window> {
} }
*/ */
/*
fn ycombinator<F>(f: &F)
where
F: Fn(&F)
{
f(f)
}
let closure: Closure<dyn Fn(Event)> = Closure::new(move |_e: Event| {
});
client_window
.set_interval_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), 17)
.unwrap();
closure.forget();
*/
/*
let mut update_timer = Instant::now();
let mut system = load(&mut host, settings::get().rom_data.clone()).unwrap();
let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| {
let run_timer = Instant::now();
let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock;
if let Err(err) = system.run_for(nanoseconds_per_frame) {
log::error!("{:?}", err);
}
log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis());
let settings = settings::get();
if settings.run {
//match load(&mut host.lock().unwrap(), settings.rom_data.clone()) {
// Ok(s) => { system = s; },
// Err(err) => log::error!("{:?}", err),
//}
}
}) as Box<dyn FnMut(_)>);
client_window
.set_interval_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), 17)
.unwrap();
closure.forget();
*/
window window
} }

View File

@ -691,7 +691,7 @@ fn sign_extend_u16(value: u16, size: usize) -> i16 {
struct Dac { struct Dac {
enabled: bool, enabled: bool,
samples: VecDeque<f32>, samples: VecDeque<(FmClock, f32)>,
} }
impl Default for Dac { impl Default for Dac {
@ -704,16 +704,19 @@ impl Default for Dac {
} }
impl Dac { impl Dac {
fn add_sample(&mut self, sample: f32) { fn add_sample(&mut self, clock: FmClock, sample: f32) {
self.samples.push_back(sample); self.samples.push_back((clock, sample));
} }
fn get_sample(&mut self) -> f32 { fn get_sample_after(&mut self, clock: FmClock) -> f32 {
if let Some(data) = self.samples.pop_front() { if let Some((sample_clock, data)) = self.samples.front().cloned() {
data if clock > sample_clock {
} else { self.samples.pop_front();
0.0 return data;
}
} }
0.0
} }
} }
@ -798,7 +801,7 @@ impl Steppable for Ym2612 {
// The DAC uses an 8000 Hz sample rate, so we don't want to skip clocks // The DAC uses an 8000 Hz sample rate, so we don't want to skip clocks
if self.dac.enabled { if self.dac.enabled {
sample += self.dac.get_sample(); sample += self.dac.get_sample_after(fm_clock);
} }
// TODO add stereo output, which is supported by ym2612 // TODO add stereo output, which is supported by ym2612
@ -872,8 +875,9 @@ impl Ym2612 {
0x2a => { 0x2a => {
if self.dac.enabled { if self.dac.enabled {
for _ in 0..3 { let fm_clock = clock.as_duration() / self.fm_clock_period;
self.dac.add_sample(((data as f32 - 128.0) / 255.0) * 2.0); for i in 0..3 {
self.dac.add_sample(fm_clock + i, ((data as f32 - 128.0) / 255.0) * 2.0);
} }
} }
}, },