mirror of
https://github.com/exoticorn/microw8.git
synced 2026-11-14 07:17:19 +01:00
Compare commits
2
Commits
893158e136
...
a5edeb21d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5edeb21d8 | ||
|
|
2839fe5be4 |
No files matched your search
Generated
+423
-276
File diff suppressed because it is too large.
Load diff
+4
-3
@@ -11,9 +11,9 @@ native = ["wasmtime"]
|
||||
browser = ["warp", "tokio", "tokio-stream", "webbrowser"]
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { version = "0.34", optional = true }
|
||||
wasmtime = { version = "0.35.3", optional = true }
|
||||
anyhow = "1"
|
||||
minifb = { version = "0.20", default-features = false, features = ["x11"] }
|
||||
minifb = { version = "0.22", default-features = false, features = ["x11"] }
|
||||
notify = "4"
|
||||
pico-args = "0.4"
|
||||
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "aac7bbd" }
|
||||
@@ -24,4 +24,5 @@ warp = { version = "0.3.2", optional = true }
|
||||
tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true }
|
||||
tokio-stream = { version = "0.1.8", features = ["sync"], optional = true }
|
||||
webbrowser = { version = "0.6.0", optional = true }
|
||||
ansi_term = "0.12.1"
|
||||
ansi_term = "0.12.1"
|
||||
cpal = "0.13.5"
|
||||
+1
-1
@@ -1 +1 @@
|
||||
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>body,canvas,html{background-color:#202024;margin:0;padding:0}body,html{height:100%}body{display:grid;grid-template-rows:1fr}#screen{align-self:center;border:4px solid #303040;image-rendering:pixelated;justify-self:center}#message{background-color:rgba(0,0,0,.4);color:#c64;font:700 12pt sans-serif;padding:8px;position:absolute;width:calc(100% - 16px);z-index:2}@media (min-width:648px) and (min-height:488px){#screen{height:480px;width:640px}}@media (min-width:968px) and (min-height:728px){#screen{height:720px;width:960px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let B=function(B,o={}){o.setMessage||(o.setMessage=(e,t)=>{t&&console.log("error: "+t)});let n,s,r,i,l=B.getContext("2d"),F=l.createImageData(320,240),d=o.devkitMode,D=0,c=null==o.keyboardElement?B:o.keyboardElement;if(c){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&f(s,!0);break;case"F9":A&&B.toBlob((e=>{p(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(r)return r.stop(),void(r=null);r=new MediaRecorder(B.captureStream(),{mimeType:"video/webm",videoBitsPerSecond:25e6});let e=[];r.ondataavailable=t=>{e.push(t.data)};let t=o.timerElement;t&&(t.hidden=!1,t.innerText="00:00");function A(){if(i){if(t){let e=Math.floor((Date.now()-i)/1e3);t.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(A,1e3)}}r.onstop=()=>{t&&(t.hidden=!0),p(new Blob(e,{type:"video/webm"}),".webm")},r.start(),i=Date.now(),setTimeout(A,1e3)}(),e.preventDefault()}A?D|=t:D&=~t};c.onkeydown=e,c.onkeyup=e}async function f(r,i){n&&(n(),n=null);let c=new AudioContext({sampleRate:44100}),p=!0;n=()=>{c.close(),p=!1};let g=r.byteLength;if(o.setMessage(g),0==g)return;await c.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.port.onmessage%3Ds%3D%3E%7Bthis.memory%3Fe%28this.memory.buffer%2C80%2C32%29.set%28e%28s.data%29%29%3Athis.load%28s.data%5B0%5D%2Cs.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ca%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29a.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29a.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29a.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20r%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ca%29%29.instance%2Cn%3Dawait%20r%28e%29%3Bfor%28let%20e%20in%20n.exports%29a.env%5Be%5D%3Dn.exports%5Be%5D%3Blet%20o%3Dawait%20r%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Cn.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28e%2Cs%2Ct%29%7Bif%28this.snd%29%7Blet%20e%3Ds%5B0%5D%2Ct%3Dthis.sampleIndex%2Ca%3De%5B0%5D.length%3Bfor%28let%20s%3D0%3Bs%3Ca%3B%2B%2Bs%29e%5B0%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%2Ce%5B1%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26t%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B"));let Q,C=new t(c),m=0,u=new Promise((e=>Q=e)),E=e=>{m|=e,3==m&&Q&&(Q(!0),Q=null)};C.port.onmessage=e=>E(e.data);let w=()=>{"suspended"==c.state?(o.startButton&&(o.startButton.style="",B.style="display:none"),(o.startButton||B).onclick=()=>{c.resume()}):(o.startButton&&(o.startButton.style="display:none",B.style=""),E(1))};c.onstatechange=w,w(),s=r;let I=window.location.pathname;if(g<=1024&&!i){let e="";for(let t of A(r))e+=String.fromCharCode(t);I+="#"+btoa(e),I!=window.location.pathname+window.location.hash&&history.pushState(null,null,I)}B.width=B.width;try{let t={initial:4};d||(t.maximum=4);let B,n=new WebAssembly.Memory(t),i=A(n.buffer),Q={env:{memory:n}},m=e=>{if(B&&(!d||0!=A(e)[0])){i.set(A(e));let t=B.exports.load_uw8(e.byteLength);e=new ArrayBuffer(t),A(e).set(i.slice(0,t))}return e},E=async e=>(await WebAssembly.instantiate(e,Q)).instance,w=async e=>m(await(await fetch(e)).arrayBuffer());B=await E(await w(e("data:application/wasm;base64,AGFzbQEAAAABFQRgAX8Bf2ACf38Bf2ADf39%2FAGAAAAIPAQNlbnYGbWVtb3J5AgAEAwkIAAABAgEAAAMGEAN%2FAUEAC38BQQALfwFBAAsHGQIIbG9hZF91dzgAAAp1bmNvbXByZXNzAAQIAQcKngUIxwEBB39BAC0AAEEBayIBQQBIBEAgAA8LQYDAByAAaiECIAFBAXEEQEEBQYHABxAEIQIFQYDAB0EAIAAQAwtBAEGAhA9BCBADQYiEDyEDQQghBEGBwAchBQNAIAUgAkLine truncated
|
||||
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>body,canvas,html{background-color:#202024;margin:0;padding:0}body,html{height:100%}body{display:grid;grid-template-rows:1fr}#screen{align-self:center;border:4px solid #303040;image-rendering:pixelated;justify-self:center}#message{background-color:rgba(0,0,0,.4);color:#c64;font:700 12pt sans-serif;padding:8px;position:absolute;width:calc(100% - 16px);z-index:2}@media (min-width:648px) and (min-height:488px){#screen{height:480px;width:640px}}@media (min-width:968px) and (min-height:728px){#screen{height:720px;width:960px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let n=function(n,B={}){B.setMessage||(B.setMessage=(e,t)=>{t&&console.log("error: "+t)});let o,s,r,i,l=n.getContext("2d"),d=l.createImageData(320,240),F=B.devkitMode,c=0,D=null==B.keyboardElement?n:B.keyboardElement;if(D){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&f(s,!0);break;case"F9":A&&n.toBlob((e=>{p(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(r)return r.stop(),void(r=null);r=new MediaRecorder(n.captureStream(),{mimeType:"video/webm",videoBitsPerSecond:25e6});let e=[];r.ondataavailable=t=>{e.push(t.data)};let t=B.timerElement;t&&(t.hidden=!1,t.innerText="00:00");function A(){if(i){if(t){let e=Math.floor((Date.now()-i)/1e3);t.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(A,1e3)}}r.onstop=()=>{t&&(t.hidden=!0),p(new Blob(e,{type:"video/webm"}),".webm")},r.start(),i=Date.now(),setTimeout(A,1e3)}(),e.preventDefault()}A?c|=t:c&=~t};D.onkeydown=e,D.onkeyup=e}async function f(r,i){o&&(o(),o=null);let D=new AudioContext({sampleRate:44100}),p=!0,g=new AbortController;o=()=>{D.close(),p=!1,g.abort()};let Q=r.byteLength;if(B.setMessage(Q),0==Q)return;await D.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.port.onmessage%3Ds%3D%3E%7Bthis.memory%3Fe%28this.memory.buffer%2C80%2C32%29.set%28e%28s.data%29%29%3Athis.load%28s.data%5B0%5D%2Cs.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ca%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29a.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29a.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29a.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20r%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ca%29%29.instance%2Cn%3Dawait%20r%28e%29%3Bfor%28let%20e%20in%20n.exports%29a.env%5Be%5D%3Dn.exports%5Be%5D%3Blet%20o%3Dawait%20r%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Cn.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28e%2Cs%2Ct%29%7Bif%28this.snd%29%7Blet%20e%3Ds%5B0%5D%2Ct%3Dthis.sampleIndex%2Ca%3De%5B0%5D.length%3Bfor%28let%20s%3D0%3Bs%3Ca%3B%2B%2Bs%29e%5B0%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%2Ce%5B1%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26t%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B"));let m,C=new t(D),u=0,w=new Promise((e=>m=e)),E=e=>{u|=e,3==u&&m&&(m(!0),m=null)};C.port.onmessage=e=>E(e.data);let I=()=>{"suspended"==D.state?(B.startButton&&(B.startButton.style="",n.style="display:none"),(B.startButton||n).onclick=()=>{D.resume()}):(B.startButton&&(B.startButton.style="display:none",n.style=""),E(1))};D.onstatechange=I,I(),s=r;let b=window.location.pathname;if(Q<=1024&&!i){let e="";for(let t of A(r))e+=String.fromCharCode(t);b+="#"+btoa(e),b!=window.location.pathname+window.location.hash&&history.pushState(null,null,b)}n.width=n.width;try{let t={initial:4};F||(t.maximum=4);let n,o=new WebAssembly.Memory(t),i=A(o.buffer),m={env:{memory:o}},u=e=>{if(n&&(!F||0!=A(e)[0])){i.set(A(e));let t=n.exports.load_uw8(e.byteLength);e=new ArrayBuffer(t),A(e).set(i.slice(0,t))}return e},E=async e=>(await WebAssembly.instantiate(e,m)).instance,I=async e=>u(await(await fetch(e)).arrayBuffer());n=await E(await I(e("data:application/wasm;base64,AGFzbQEAAAABFQRgAX8Bf2ACf38Bf2ADf39%2FAGAAAAIPAQNlbnYGbWVtb3J5AgAEAwkIAAABAgEAAAMGEAN%2FAUEAC38BQQALfwFBAAsHGQIIbG9hZF91dzgAAAp1bmNvbXByZXNzAAQIAQcKngUIxwEBB39BAC0AAEEBayIBQQBIBEAgAA8LQYDAByAAaiECIAFBAXEEQEEBQYHABxAEIQIFQYDAB0EAIAAQAwtBAEGAhA9BCBLine truncated
|
||||
+141
-37
@@ -1,8 +1,9 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{thread, time::Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use cpal::traits::*;
|
||||
use minifb::{Key, Window, WindowOptions};
|
||||
use wasmtime::{
|
||||
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
||||
@@ -32,10 +33,11 @@ struct UW8Instance {
|
||||
store: Store<()>,
|
||||
memory: Memory,
|
||||
end_frame: TypedFunc<(), ()>,
|
||||
update: TypedFunc<(), ()>,
|
||||
update: Option<TypedFunc<(), ()>>,
|
||||
start_time: Instant,
|
||||
module: Vec<u8>,
|
||||
watchdog: Arc<Mutex<UW8WatchDog>>,
|
||||
sound: Uw8Sound,
|
||||
}
|
||||
|
||||
impl Drop for UW8Instance {
|
||||
@@ -86,6 +88,11 @@ impl MicroW8 {
|
||||
}
|
||||
}
|
||||
|
||||
struct Uw8Sound {
|
||||
stream: cpal::Stream,
|
||||
tx: mpsc::SyncSender<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl super::Runtime for MicroW8 {
|
||||
fn is_open(&self) -> bool {
|
||||
self.window.is_open() && !self.window.is_key_down(Key::Escape)
|
||||
@@ -119,43 +126,62 @@ impl super::Runtime for MicroW8 {
|
||||
let module_length = load_uw8.call(&mut store, module_data.len() as i32)? as u32 as usize;
|
||||
let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?;
|
||||
|
||||
linker.func_wrap("env", "acos", |v: f32| v.acos())?;
|
||||
linker.func_wrap("env", "asin", |v: f32| v.asin())?;
|
||||
linker.func_wrap("env", "atan", |v: f32| v.atan())?;
|
||||
linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?;
|
||||
linker.func_wrap("env", "cos", |v: f32| v.cos())?;
|
||||
linker.func_wrap("env", "exp", |v: f32| v.exp())?;
|
||||
linker.func_wrap("env", "log", |v: f32| v.ln())?;
|
||||
linker.func_wrap("env", "sin", |v: f32| v.sin())?;
|
||||
linker.func_wrap("env", "tan", |v: f32| v.tan())?;
|
||||
linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?;
|
||||
for i in 9..64 {
|
||||
linker.func_wrap("env", &format!("reserved{}", i), || {})?;
|
||||
}
|
||||
for i in 0..16 {
|
||||
linker.define(
|
||||
"env",
|
||||
&format!("g_reserved{}", i),
|
||||
wasmtime::Global::new(
|
||||
&mut store,
|
||||
GlobalType::new(ValType::I32, Mutability::Const),
|
||||
0.into(),
|
||||
)?,
|
||||
)?;
|
||||
fn add_native_functions(
|
||||
linker: &mut wasmtime::Linker<()>,
|
||||
store: &mut wasmtime::Store<()>,
|
||||
) -> Result<()> {
|
||||
linker.func_wrap("env", "acos", |v: f32| v.acos())?;
|
||||
linker.func_wrap("env", "asin", |v: f32| v.asin())?;
|
||||
linker.func_wrap("env", "atan", |v: f32| v.atan())?;
|
||||
linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?;
|
||||
linker.func_wrap("env", "cos", |v: f32| v.cos())?;
|
||||
linker.func_wrap("env", "exp", |v: f32| v.exp())?;
|
||||
linker.func_wrap("env", "log", |v: f32| v.ln())?;
|
||||
linker.func_wrap("env", "sin", |v: f32| v.sin())?;
|
||||
linker.func_wrap("env", "tan", |v: f32| v.tan())?;
|
||||
linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?;
|
||||
for i in 10..64 {
|
||||
linker.func_wrap("env", &format!("reserved{}", i), || {})?;
|
||||
}
|
||||
for i in 0..16 {
|
||||
linker.define(
|
||||
"env",
|
||||
&format!("g_reserved{}", i),
|
||||
wasmtime::Global::new(
|
||||
&mut *store,
|
||||
GlobalType::new(ValType::I32, Mutability::Const),
|
||||
0.into(),
|
||||
)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let platform_instance = linker.instantiate(&mut store, &platform_module)?;
|
||||
add_native_functions(&mut linker, &mut store)?;
|
||||
|
||||
for export in platform_instance.exports(&mut store) {
|
||||
linker.define(
|
||||
"env",
|
||||
export.name(),
|
||||
export
|
||||
.into_func()
|
||||
.expect("platform surely only exports functions"),
|
||||
)?;
|
||||
fn instantiate_platform(
|
||||
linker: &mut wasmtime::Linker<()>,
|
||||
store: &mut wasmtime::Store<()>,
|
||||
platform_module: &wasmtime::Module,
|
||||
) -> Result<wasmtime::Instance> {
|
||||
let platform_instance = linker.instantiate(&mut *store, &platform_module)?;
|
||||
|
||||
for export in platform_instance.exports(&mut *store) {
|
||||
linker.define(
|
||||
"env",
|
||||
export.name(),
|
||||
export
|
||||
.into_func()
|
||||
.expect("platform surely only exports functions"),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(platform_instance)
|
||||
}
|
||||
|
||||
let platform_instance = instantiate_platform(&mut linker, &mut store, &platform_module)?;
|
||||
|
||||
let watchdog = Arc::new(Mutex::new(UW8WatchDog {
|
||||
interupt: store.interrupt_handle()?,
|
||||
timeout: self.timeout,
|
||||
@@ -187,7 +213,77 @@ impl super::Runtime for MicroW8 {
|
||||
watchdog.timeout = 0;
|
||||
}
|
||||
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?;
|
||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
|
||||
|
||||
let sound = {
|
||||
let mut store = wasmtime::Store::new(&self.engine, ());
|
||||
|
||||
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
|
||||
|
||||
let mut linker = wasmtime::Linker::new(&self.engine);
|
||||
linker.define("env", "memory", memory)?;
|
||||
add_native_functions(&mut linker, &mut store)?;
|
||||
|
||||
let platform_instance =
|
||||
instantiate_platform(&mut linker, &mut store, &platform_module)?;
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
|
||||
let snd = instance
|
||||
.get_typed_func::<(i32,), f32, _>(&mut store, "snd")
|
||||
.or_else(|_| {
|
||||
platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "gesSnd")
|
||||
})?;
|
||||
|
||||
let host = cpal::default_host();
|
||||
let device = host
|
||||
.default_output_device()
|
||||
.ok_or_else(|| anyhow!("No audio output device available"))?;
|
||||
let config = device
|
||||
.supported_output_configs()?
|
||||
.find(|config| {
|
||||
config.min_sample_rate().0 <= 44100
|
||||
&& config.max_sample_rate().0 >= 44100
|
||||
&& config.channels() == 2
|
||||
&& config.sample_format() == cpal::SampleFormat::F32
|
||||
})
|
||||
.ok_or_else(|| anyhow!("Could not find 44.1kHz float config"))?;
|
||||
let config = config.with_sample_rate(cpal::SampleRate(44100));
|
||||
let buffer_size = match *config.buffer_size() {
|
||||
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
||||
cpal::SupportedBufferSize::Range { min, max } => {
|
||||
cpal::BufferSize::Fixed(256.max(min).min(max))
|
||||
}
|
||||
};
|
||||
let config = cpal::StreamConfig {
|
||||
buffer_size,
|
||||
..config.config()
|
||||
};
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel::<[u8; 32]>(1);
|
||||
|
||||
let mut sample_index = 0;
|
||||
let stream = {
|
||||
device.build_output_stream(
|
||||
&config,
|
||||
move |buffer: &mut [f32], _| {
|
||||
if let Ok(regs) = rx.try_recv() {
|
||||
memory.write(&mut store, 80, ®s).unwrap();
|
||||
}
|
||||
for v in buffer {
|
||||
*v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0);
|
||||
sample_index = sample_index.wrapping_add(1);
|
||||
}
|
||||
},
|
||||
move |err| {
|
||||
dbg!(err);
|
||||
},
|
||||
)?
|
||||
};
|
||||
|
||||
Uw8Sound { stream, tx }
|
||||
};
|
||||
|
||||
sound.stream.play()?;
|
||||
|
||||
self.instance = Some(UW8Instance {
|
||||
store,
|
||||
@@ -197,6 +293,7 @@ impl super::Runtime for MicroW8 {
|
||||
start_time: Instant::now(),
|
||||
module: module_data.into(),
|
||||
watchdog,
|
||||
sound,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@@ -227,13 +324,20 @@ impl super::Runtime for MicroW8 {
|
||||
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
||||
watchdog.timeout = self.timeout;
|
||||
}
|
||||
result = instance.update.call(&mut instance.store, ());
|
||||
if let Some(ref update) = instance.update {
|
||||
result = update.call(&mut instance.store, ());
|
||||
}
|
||||
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
||||
watchdog.timeout = 0;
|
||||
}
|
||||
instance.end_frame.call(&mut instance.store, ())?;
|
||||
|
||||
let memory = instance.memory.data(&instance.store);
|
||||
|
||||
let mut sound_regs = [0u8; 32];
|
||||
sound_regs.copy_from_slice(&memory[80..112]);
|
||||
instance.sound.tx.send(sound_regs)?;
|
||||
|
||||
let framebuffer = &memory[120..(120 + 320 * 240)];
|
||||
let palette = &memory[0x13000..];
|
||||
for (i, &color_index) in framebuffer.iter().enumerate() {
|
||||
|
||||
Reference in new issue
Block a user