From e05701300c96331206babe1545952531ff287c42 Mon Sep 17 00:00:00 2001 From: Dennis Ranke Date: Fri, 22 Apr 2022 00:28:19 +0200 Subject: [PATCH] implement backchannel from audio thread --- src/run-web.html | 2 +- src/run_native.rs | 28 ++++++++++++++++++++++++++-- web/src/audiolet.js | 13 +++++++++++-- web/src/microw8.js | 11 ++++++++++- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/run-web.html b/src/run-web.html index e88a403..088401f 100644 --- a/src/run-web.html +++ b/src/run-web.html @@ -1 +1 @@ -uw8-run
\ No newline at end of file +uw8-run
\ No newline at end of file diff --git a/src/run_native.rs b/src/run_native.rs index eb96463..342a4fd 100644 --- a/src/run_native.rs +++ b/src/run_native.rs @@ -91,6 +91,7 @@ impl MicroW8 { struct Uw8Sound { stream: cpal::Stream, tx: mpsc::SyncSender<[u8; 32]>, + rx: mpsc::Receiver<[u8; 32]>, } impl super::Runtime for MicroW8 { @@ -260,15 +261,27 @@ impl super::Runtime for MicroW8 { }; let (tx, rx) = mpsc::sync_channel::<[u8; 32]>(1); + let (back_tx, back_rx) = mpsc::sync_channel::<[u8; 32]>(2); + + let start_time = Instant::now(); let mut sample_index = 0; let stream = { device.build_output_stream( &config, move |buffer: &mut [f32], _| { - if let Ok(regs) = rx.try_recv() { + if let Ok(mut regs) = rx.try_recv() { memory.write(&mut store, 80, ®s).unwrap(); + memory.read(&mut store, 0x12c80, &mut regs).unwrap(); + back_tx.send(regs).unwrap(); } + + { + let time = start_time.elapsed().as_millis() as i32; + let mem = memory.data_mut(&mut store); + mem[64..68].copy_from_slice(&time.to_le_bytes()); + } + for v in buffer { *v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0); sample_index = sample_index.wrapping_add(1); @@ -280,7 +293,11 @@ impl super::Runtime for MicroW8 { )? }; - Uw8Sound { stream, tx } + Uw8Sound { + stream, + tx, + rx: back_rx, + } }; sound.stream.play()?; @@ -302,6 +319,13 @@ impl super::Runtime for MicroW8 { fn run_frame(&mut self) -> Result<()> { let mut result = Ok(()); if let Some(mut instance) = self.instance.take() { + while let Ok(regs) = instance.sound.rx.try_recv() { + instance + .memory + .write(&mut instance.store, 0x12c80, ®s) + .unwrap(); + } + { let time = instance.start_time.elapsed().as_millis() as i32; let mut gamepad: u32 = 0; diff --git a/web/src/audiolet.js b/web/src/audiolet.js index 662fc76..6fb5fb0 100644 --- a/web/src/audiolet.js +++ b/web/src/audiolet.js @@ -5,7 +5,14 @@ class APU extends AudioWorkletProcessor { this.sampleIndex = 0; this.port.onmessage = (ev) => { if(this.memory) { - U8(this.memory.buffer, 80, 32).set(U8(ev.data)); + if(isNaN(ev.data)) { + let data = U8(ev.data); + U8(this.memory.buffer, 80, 32).set(data); + data.set(U8(this.memory.buffer, 0x12c80, 32)); + this.port.postMessage(ev.data); + } else { + this.startTime = ev.data; + } } else { this.load(ev.data[0], ev.data[1]); } @@ -51,7 +58,9 @@ class APU extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - if(this.snd) { + if(this.snd && this.startTime) { + let u32Mem = new Uint32Array(this.memory.buffer); + u32Mem[16] = Date.now() - this.startTime; let channels = outputs[0]; let index = this.sampleIndex; let numSamples = channels[0].length; diff --git a/web/src/microw8.js b/web/src/microw8.js index 0c80e38..0e471eb 100644 --- a/web/src/microw8.js +++ b/web/src/microw8.js @@ -126,7 +126,6 @@ export default function MicroW8(screen, config = {}) { audioReadyResolve = null; } }; - audioNode.port.onmessage = (e) => updateAudioReady(e.data); let audioStateChange = () => { if(audioContext.state == 'suspended') { if(config.startButton) { @@ -211,6 +210,14 @@ export default function MicroW8(screen, config = {}) { data = loadModuleData(data); let platform_data = await loadModuleURL(platformUrl); + + audioNode.port.onmessage = (e) => { + if(isNaN(e.data)) { + U8(memory.buffer, 0x12c80, 32).set(U8(e.data)); + } else { + updateAudioReady(e.data); + } + }; audioNode.port.postMessage([platform_data, data]); let platform_instance = await instantiate(platform_data); @@ -240,10 +247,12 @@ export default function MicroW8(screen, config = {}) { isPaused = false; audioContext.resume(); startTime += now - pauseTime; + audioNode.port.postMessage(startTime); } else { isPaused = true; audioContext.suspend(); pauseTime = now; + audioNode.port.postMessage(0); } }; window.addEventListener('focus', () => updateVisibility(true), { signal: abortController.signal });