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)
}
#[derive(Clone)]
pub struct EventSender<T> {
queue: Arc<Mutex<VecDeque<T>>>,
}
@ -23,10 +24,6 @@ impl<T> EventSender<T> {
pub fn send(&self, event: T) {
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> {

View File

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

View File

@ -1,49 +1,70 @@
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", () => {
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
var reader = new FileReader();
const reader = new FileReader();
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 (data[8] == 0xAA && data[9] == 0xBB)
data = Emulator.smd_to_bin(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 => {
document.getElementById("video").focus();
reader.readAsArrayBuffer(file_input.files[0])
@ -62,8 +83,8 @@ document.getElementById("power").addEventListener("click", () => {
initialize_emulator();
});
var mute_state = false;
var mute = document.getElementById("mute");
let mute_state = false;
const mute = document.getElementById("mute");
mute.addEventListener("click", () => {
mute_state = !mute_state;
if (mute_state) {
@ -74,19 +95,3 @@ mute.addEventListener("click", () => {
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;
}
#controller {
width: 100%;
display: flex;
}
#controller button {
flex: 1;
width: 100%;
height: 40px;
margin-top: 2px;
margin-bottom: 2px;
}
#controller .updown {
overflow: hidden;
}

View File

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

View File

@ -5,12 +5,13 @@ use winit::dpi::LogicalSize;
use winit::event_loop::EventLoop;
use winit::window::{Window, WindowBuilder};
use crate::frontend::{self, LoadSystemFn};
use crate::frontend::{self, LoadSystemFn, PixelsFrontend};
pub fn start(load: LoadSystemFn) {
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> {

View File

@ -13,6 +13,7 @@ use wasm_bindgen::JsCast;
use wasm_bindgen::closure::Closure;
use moa_core::{ClockDuration, System, wrap_transmutable};
use moa_core::host::{ControllerInput, ControllerDevice, ControllerEvent, EventSender};
use crate::settings;
use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
@ -72,19 +73,41 @@ pub fn set_mute(mute: bool) {
}
#[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]
pub struct HostHandle(PixelsFrontend);
#[wasm_bindgen]
pub struct ControllersHandle(EventSender<ControllerEvent>);
#[wasm_bindgen]
pub fn new_host() -> HostHandle {
HostHandle(PixelsFrontend::new())
}
#[wasm_bindgen]
pub fn get_controllers(handle: &HostHandle) -> ControllersHandle {
ControllersHandle(handle.0.get_controllers().unwrap())
}
#[wasm_bindgen]
pub fn host_run_loop(handle: HostHandle) {
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
}

View File

@ -691,7 +691,7 @@ fn sign_extend_u16(value: u16, size: usize) -> i16 {
struct Dac {
enabled: bool,
samples: VecDeque<f32>,
samples: VecDeque<(FmClock, f32)>,
}
impl Default for Dac {
@ -704,16 +704,19 @@ impl Default for Dac {
}
impl Dac {
fn add_sample(&mut self, sample: f32) {
self.samples.push_back(sample);
fn add_sample(&mut self, clock: FmClock, sample: f32) {
self.samples.push_back((clock, sample));
}
fn get_sample(&mut self) -> f32 {
if let Some(data) = self.samples.pop_front() {
data
} else {
0.0
fn get_sample_after(&mut self, clock: FmClock) -> f32 {
if let Some((sample_clock, data)) = self.samples.front().cloned() {
if clock > sample_clock {
self.samples.pop_front();
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
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
@ -872,8 +875,9 @@ impl Ym2612 {
0x2a => {
if self.dac.enabled {
for _ in 0..3 {
self.dac.add_sample(((data as f32 - 128.0) / 255.0) * 2.0);
let fm_clock = clock.as_duration() / self.fm_clock_period;
for i in 0..3 {
self.dac.add_sample(fm_clock + i, ((data as f32 - 128.0) / 255.0) * 2.0);
}
}
},