mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 19:26:43 +01:00
Compare commits
5 Commits
v0.2.0-rc1
...
v0.2.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f67e92c5c | |||
| a2714f25e4 | |||
| 7e203d93e6 | |||
| e44c87d1f6 | |||
| 614b7cf358 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2637,7 +2637,7 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.2.0-rc1"
|
version = "0.2.0-rc2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.2.0-rc1"
|
version = "0.2.0-rc2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
55
examples/curlywas/cracklebass.cwa
Normal file
55
examples/curlywas/cracklebass.cwa
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// port of cracklebass by pestis (originally on TIC-80)
|
||||||
|
|
||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
const MUSIC_DATA = 0x20000;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
let inline t = 32!32 * 6 / 100;
|
||||||
|
let inline p = t / 1024;
|
||||||
|
|
||||||
|
let channel:i32;
|
||||||
|
|
||||||
|
loop channels {
|
||||||
|
let inline e = t * channel?MUSIC_DATA / 8;
|
||||||
|
let lazy pattern = (8 * channel + p)?(MUSIC_DATA + 56);
|
||||||
|
let lazy n = !!pattern * (8 * pattern + e / 16 % 8)?MUSIC_DATA;
|
||||||
|
let inline prev_ctrl = (channel * 6)?80;
|
||||||
|
(channel * 6)?80 = if n {
|
||||||
|
let inline base_note = 12 + 12 * channel?(MUSIC_DATA + 4) + n;
|
||||||
|
let inline pitch_drop = e % 16 * channel?(MUSIC_DATA + 94);
|
||||||
|
let inline key_pattern = p?(MUSIC_DATA + 8*4 + 56);
|
||||||
|
let inline key = select(key_pattern, (8 * key_pattern + t / 128 % 8)?MUSIC_DATA, 1);
|
||||||
|
(channel * 6)?83 = base_note - pitch_drop / 4 + key;
|
||||||
|
prev_ctrl & 0xfc | (e / 8 & 2) | 1
|
||||||
|
} else {
|
||||||
|
prev_ctrl & 0xfe
|
||||||
|
};
|
||||||
|
|
||||||
|
branch_if (channel := channel + 1) < 4: channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 {
|
||||||
|
i8(
|
||||||
|
0x44, 0, 0, 0, 0x50, 0x40,
|
||||||
|
0x4, 0x50, 0, 0, 0x80, 0x80,
|
||||||
|
0x40, 0x80, 0, 0, 0x40, 0x40,
|
||||||
|
0, 0, 0, 0, 0x50, 0x50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data MUSIC_DATA {
|
||||||
|
i8(
|
||||||
|
16, 2, 8, 8, 1, 2, 2, 3, 1, 0,
|
||||||
|
1,13,16, 0, 1, 8, 1, 0, 1,13,
|
||||||
|
16, 1, 1, 8, 1, 0, 8,13,13, 0,
|
||||||
|
16,13, 1, 0, 1, 0, 1, 0, 1, 1,
|
||||||
|
1, 0, 0, 0, 1, 0,13, 1, 1, 1,
|
||||||
|
6, 8, 1, 1, 6, 8, 1, 1, 2, 1,
|
||||||
|
2, 1, 2, 0, 0, 0, 0, 3, 3, 3,
|
||||||
|
5, 0, 0, 2, 1, 2, 1, 2, 1, 2,
|
||||||
|
0, 4, 4, 0, 4, 4, 4, 4, 0, 0,
|
||||||
|
0, 0, 6, 6, 0, 0, 0, 8
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -29,9 +29,23 @@ Examplers for older versions:
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
|
### v0.2.0-rc2
|
||||||
|
|
||||||
|
* [Web runtime](v0.2.0-rc2)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* fix timing issues of sound playback, especially on systems with large sound buffers
|
||||||
|
|
||||||
### v0.2.0-rc1
|
### v0.2.0-rc1
|
||||||
|
|
||||||
* [Web runtime](v0.2.0-rc1)
|
* [Web runtime](v0.2.0-rc1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-windows.zip)
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|
||||||
|
|||||||
1
site/static/v0.2.0-rc2/index.html
Normal file
1
site/static/v0.2.0-rc2/index.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -197,8 +197,8 @@ impl super::Runtime for MicroW8 {
|
|||||||
fn run_frame(&mut self) -> Result<()> {
|
fn run_frame(&mut self) -> Result<()> {
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
if let Some(mut instance) = self.instance.take() {
|
if let Some(mut instance) = self.instance.take() {
|
||||||
|
let time = instance.start_time.elapsed().as_millis() as i32;
|
||||||
{
|
{
|
||||||
let time = instance.start_time.elapsed().as_millis() as i32;
|
|
||||||
let mut gamepad: u32 = 0;
|
let mut gamepad: u32 = 0;
|
||||||
for key in self.window.get_keys() {
|
for key in self.window.get_keys() {
|
||||||
if let Some(index) = GAMEPAD_KEYS
|
if let Some(index) = GAMEPAD_KEYS
|
||||||
@@ -232,7 +232,10 @@ impl super::Runtime for MicroW8 {
|
|||||||
let mut sound_regs = [0u8; 32];
|
let mut sound_regs = [0u8; 32];
|
||||||
sound_regs.copy_from_slice(&memory[80..112]);
|
sound_regs.copy_from_slice(&memory[80..112]);
|
||||||
if let Some(ref sound) = instance.sound {
|
if let Some(ref sound) = instance.sound {
|
||||||
sound.tx.send(sound_regs)?;
|
sound.tx.send(RegisterUpdate {
|
||||||
|
time,
|
||||||
|
data: sound_regs,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let framebuffer = &memory[120..(120 + 320 * 240)];
|
let framebuffer = &memory[120..(120 + 320 * 240)];
|
||||||
@@ -312,9 +315,14 @@ fn instantiate_platform(
|
|||||||
Ok(platform_instance)
|
Ok(platform_instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RegisterUpdate {
|
||||||
|
time: i32,
|
||||||
|
data: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
struct Uw8Sound {
|
struct Uw8Sound {
|
||||||
stream: cpal::Stream,
|
stream: cpal::Stream,
|
||||||
tx: mpsc::SyncSender<[u8; 32]>,
|
tx: mpsc::SyncSender<RegisterUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_sound(
|
fn init_sound(
|
||||||
@@ -368,7 +376,7 @@ fn init_sound(
|
|||||||
let buffer_size = match *config.buffer_size() {
|
let buffer_size = match *config.buffer_size() {
|
||||||
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
||||||
cpal::SupportedBufferSize::Range { min, max } => {
|
cpal::SupportedBufferSize::Range { min, max } => {
|
||||||
cpal::BufferSize::Fixed(256.max(min).min(max))
|
cpal::BufferSize::Fixed(65536.max(min).min(max))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let config = cpal::StreamConfig {
|
let config = cpal::StreamConfig {
|
||||||
@@ -378,9 +386,7 @@ fn init_sound(
|
|||||||
|
|
||||||
let sample_rate = config.sample_rate.0 as usize;
|
let sample_rate = config.sample_rate.0 as usize;
|
||||||
|
|
||||||
let (tx, rx) = mpsc::sync_channel::<[u8; 32]>(1);
|
let (tx, rx) = mpsc::sync_channel::<RegisterUpdate>(30);
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
struct Resampler {
|
struct Resampler {
|
||||||
resampler: rubato::FftFixedIn<f32>,
|
resampler: rubato::FftFixedIn<f32>,
|
||||||
@@ -403,60 +409,91 @@ fn init_sound(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut sample_index = 0;
|
let mut sample_index = 0;
|
||||||
|
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
|
||||||
|
let mut current_time = 0;
|
||||||
let stream = device.build_output_stream(
|
let stream = device.build_output_stream(
|
||||||
&config,
|
&config,
|
||||||
move |mut buffer: &mut [f32], _| {
|
move |mut outer_buffer: &mut [f32], _| {
|
||||||
if let Ok(regs) = rx.try_recv() {
|
let mut first_update = true;
|
||||||
memory.write(&mut store, 80, ®s).unwrap();
|
while let Ok(update) = rx.try_recv() {
|
||||||
|
if first_update {
|
||||||
|
current_time += update.time.wrapping_sub(current_time) / 8;
|
||||||
|
first_update = false;
|
||||||
|
}
|
||||||
|
pending_updates.push(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
while !outer_buffer.is_empty() {
|
||||||
let time = start_time.elapsed().as_millis() as i32;
|
while pending_updates
|
||||||
let mem = memory.data_mut(&mut store);
|
.first()
|
||||||
mem[64..68].copy_from_slice(&time.to_le_bytes());
|
.into_iter()
|
||||||
}
|
.any(|u| u.time.wrapping_sub(current_time) <= 0)
|
||||||
|
{
|
||||||
|
let update = pending_updates.remove(0);
|
||||||
|
memory.write(&mut store, 80, &update.data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref mut resampler) = resampler {
|
let duration = if let Some(update) = pending_updates.first() {
|
||||||
while !buffer.is_empty() {
|
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
|
||||||
let copy_size = resampler.output_buffers[0]
|
} else {
|
||||||
.len()
|
outer_buffer.len()
|
||||||
.saturating_sub(resampler.output_index)
|
};
|
||||||
.min(buffer.len() / 2);
|
let step_size = (duration.max(64) * 2).min(outer_buffer.len());
|
||||||
if copy_size == 0 {
|
|
||||||
resampler.input_buffers[0].clear();
|
|
||||||
resampler.input_buffers[1].clear();
|
|
||||||
for _ in 0..resampler.resampler.input_frames_next() {
|
|
||||||
resampler.input_buffers[0]
|
|
||||||
.push(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
|
|
||||||
resampler.input_buffers[1]
|
|
||||||
.push(snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0));
|
|
||||||
sample_index = sample_index.wrapping_add(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
resampler
|
let mut buffer = &mut outer_buffer[..step_size];
|
||||||
.resampler
|
|
||||||
.process_into_buffer(
|
{
|
||||||
&resampler.input_buffers,
|
let mem = memory.data_mut(&mut store);
|
||||||
&mut resampler.output_buffers,
|
mem[64..68].copy_from_slice(¤t_time.to_le_bytes());
|
||||||
None,
|
}
|
||||||
)
|
|
||||||
.unwrap();
|
if let Some(ref mut resampler) = resampler {
|
||||||
resampler.output_index = 0;
|
while !buffer.is_empty() {
|
||||||
} else {
|
let copy_size = resampler.output_buffers[0]
|
||||||
for i in 0..copy_size {
|
.len()
|
||||||
buffer[i * 2] = resampler.output_buffers[0][resampler.output_index + i];
|
.saturating_sub(resampler.output_index)
|
||||||
buffer[i * 2 + 1] =
|
.min(buffer.len() / 2);
|
||||||
resampler.output_buffers[1][resampler.output_index + i];
|
if copy_size == 0 {
|
||||||
|
resampler.input_buffers[0].clear();
|
||||||
|
resampler.input_buffers[1].clear();
|
||||||
|
for _ in 0..resampler.resampler.input_frames_next() {
|
||||||
|
resampler.input_buffers[0]
|
||||||
|
.push(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
|
||||||
|
resampler.input_buffers[1]
|
||||||
|
.push(snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0));
|
||||||
|
sample_index = sample_index.wrapping_add(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
resampler
|
||||||
|
.resampler
|
||||||
|
.process_into_buffer(
|
||||||
|
&resampler.input_buffers,
|
||||||
|
&mut resampler.output_buffers,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
resampler.output_index = 0;
|
||||||
|
} else {
|
||||||
|
for i in 0..copy_size {
|
||||||
|
buffer[i * 2] =
|
||||||
|
resampler.output_buffers[0][resampler.output_index + i];
|
||||||
|
buffer[i * 2 + 1] =
|
||||||
|
resampler.output_buffers[1][resampler.output_index + i];
|
||||||
|
}
|
||||||
|
resampler.output_index += copy_size;
|
||||||
|
buffer = &mut buffer[copy_size * 2..];
|
||||||
}
|
}
|
||||||
resampler.output_index += copy_size;
|
}
|
||||||
buffer = &mut buffer[copy_size * 2..];
|
} else {
|
||||||
|
for v in buffer {
|
||||||
|
*v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0);
|
||||||
|
sample_index = sample_index.wrapping_add(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for v in buffer {
|
outer_buffer = &mut outer_buffer[step_size..];
|
||||||
*v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0);
|
current_time =
|
||||||
sample_index = sample_index.wrapping_add(1);
|
current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
move |err| {
|
move |err| {
|
||||||
|
|||||||
@@ -3,13 +3,17 @@ class APU extends AudioWorkletProcessor {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.sampleIndex = 0;
|
this.sampleIndex = 0;
|
||||||
|
this.currentTime = 0;
|
||||||
|
this.isFirstMessage = true;
|
||||||
|
this.pendingUpdates = [];
|
||||||
this.port.onmessage = (ev) => {
|
this.port.onmessage = (ev) => {
|
||||||
if(this.memory) {
|
if(this.memory) {
|
||||||
if(isNaN(ev.data)) {
|
if(this.isFirstMessage)
|
||||||
U8(this.memory.buffer, 80, 32).set(U8(ev.data));
|
{
|
||||||
} else {
|
this.currentTime += (ev.data.t - this.currentTime) / 8;
|
||||||
this.startTime = ev.data;
|
this.isFirstMessage = false;
|
||||||
}
|
}
|
||||||
|
this.pendingUpdates.push(ev.data);
|
||||||
} else {
|
} else {
|
||||||
this.load(ev.data[0], ev.data[1]);
|
this.load(ev.data[0], ev.data[1]);
|
||||||
}
|
}
|
||||||
@@ -55,9 +59,13 @@ class APU extends AudioWorkletProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process(inputs, outputs, parameters) {
|
process(inputs, outputs, parameters) {
|
||||||
if(this.snd && this.startTime) {
|
this.isFirstMessage = true;
|
||||||
|
if(this.snd) {
|
||||||
|
while(this.pendingUpdates.length > 0 && this.pendingUpdates[0].t <= this.currentTime) {
|
||||||
|
U8(this.memory.buffer, 80, 32).set(U8(this.pendingUpdates.shift().r));
|
||||||
|
}
|
||||||
let u32Mem = new Uint32Array(this.memory.buffer);
|
let u32Mem = new Uint32Array(this.memory.buffer);
|
||||||
u32Mem[16] = Date.now() - this.startTime;
|
u32Mem[16] = this.currentTime;
|
||||||
let channels = outputs[0];
|
let channels = outputs[0];
|
||||||
let index = this.sampleIndex;
|
let index = this.sampleIndex;
|
||||||
let numSamples = channels[0].length;
|
let numSamples = channels[0].length;
|
||||||
@@ -66,6 +74,7 @@ class APU extends AudioWorkletProcessor {
|
|||||||
channels[1][i] = this.snd(index++);
|
channels[1][i] = this.snd(index++);
|
||||||
}
|
}
|
||||||
this.sampleIndex = index & 0xffffffff;
|
this.sampleIndex = index & 0xffffffff;
|
||||||
|
this.currentTime += numSamples / 44.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="uw8">
|
<div id="uw8">
|
||||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.0-rc1
|
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.0-rc2
|
||||||
</div>
|
</div>
|
||||||
<div id="centered">
|
<div id="centered">
|
||||||
<canvas class="screen" id="screen" width="320" height="240">
|
<canvas class="screen" id="screen" width="320" height="240">
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
audioContext.close();
|
audioContext.close();
|
||||||
keepRunning = false;
|
keepRunning = false;
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}
|
};
|
||||||
|
|
||||||
let cartridgeSize = data.byteLength;
|
let cartridgeSize = data.byteLength;
|
||||||
|
|
||||||
@@ -232,7 +232,6 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
let startTime = Date.now();
|
let startTime = Date.now();
|
||||||
|
|
||||||
const timePerFrame = 1000 / 60;
|
const timePerFrame = 1000 / 60;
|
||||||
let nextFrame = startTime;
|
|
||||||
|
|
||||||
audioNode.connect(audioContext.destination);
|
audioNode.connect(audioContext.destination);
|
||||||
|
|
||||||
@@ -244,12 +243,10 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
isPaused = false;
|
isPaused = false;
|
||||||
audioContext.resume();
|
audioContext.resume();
|
||||||
startTime += now - pauseTime;
|
startTime += now - pauseTime;
|
||||||
audioNode.port.postMessage(startTime);
|
|
||||||
} else {
|
} else {
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
audioContext.suspend();
|
audioContext.suspend();
|
||||||
pauseTime = now;
|
pauseTime = now;
|
||||||
audioNode.port.postMessage(0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('focus', () => updateVisibility(true), { signal: abortController.signal });
|
window.addEventListener('focus', () => updateVisibility(true), { signal: abortController.signal });
|
||||||
@@ -263,6 +260,7 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let restart = false;
|
let restart = false;
|
||||||
|
let thisFrame;
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
let gamepads = navigator.getGamepads();
|
let gamepads = navigator.getGamepads();
|
||||||
let gamepad = 0;
|
let gamepad = 0;
|
||||||
@@ -291,7 +289,8 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let u32Mem = U32(memory.buffer);
|
let u32Mem = U32(memory.buffer);
|
||||||
u32Mem[16] = Date.now() - startTime;
|
let time = Date.now() - startTime;
|
||||||
|
u32Mem[16] = time;
|
||||||
u32Mem[17] = pad | gamepad;
|
u32Mem[17] = pad | gamepad;
|
||||||
if(instance.exports.upd) {
|
if(instance.exports.upd) {
|
||||||
instance.exports.upd();
|
instance.exports.upd();
|
||||||
@@ -300,16 +299,21 @@ export default function MicroW8(screen, config = {}) {
|
|||||||
|
|
||||||
let soundRegisters = new ArrayBuffer(32);
|
let soundRegisters = new ArrayBuffer(32);
|
||||||
U8(soundRegisters).set(U8(memory.buffer, 80, 32));
|
U8(soundRegisters).set(U8(memory.buffer, 80, 32));
|
||||||
audioNode.port.postMessage(soundRegisters, [soundRegisters]);
|
audioNode.port.postMessage({t: time, r: soundRegisters}, [soundRegisters]);
|
||||||
|
|
||||||
let palette = U32(memory.buffer, 0x13000, 1024);
|
let palette = U32(memory.buffer, 0x13000, 1024);
|
||||||
for (let i = 0; i < 320 * 240; ++i) {
|
for (let i = 0; i < 320 * 240; ++i) {
|
||||||
buffer[i] = palette[memU8[i + 120]] | 0xff000000;
|
buffer[i] = palette[memU8[i + 120]] | 0xff000000;
|
||||||
}
|
}
|
||||||
canvasCtx.putImageData(imageData, 0, 0);
|
canvasCtx.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
|
let timeOffset = ((time * 6) % 100 - 50) / 6;
|
||||||
|
thisFrame = startTime + time - timeOffset / 8;
|
||||||
|
} else {
|
||||||
|
thisFrame = Date.now();
|
||||||
}
|
}
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
nextFrame = Math.max(nextFrame + timePerFrame, now);
|
let nextFrame = Math.max(thisFrame + timePerFrame, now);
|
||||||
|
|
||||||
if (restart) {
|
if (restart) {
|
||||||
runModule(currentData);
|
runModule(currentData);
|
||||||
|
|||||||
Reference in New Issue
Block a user