diff --git a/canvas/machine-wasm/.gitignore b/canvas/machine-wasm/.gitignore index 4e301317..5a945b7a 100644 --- a/canvas/machine-wasm/.gitignore +++ b/canvas/machine-wasm/.gitignore @@ -4,3 +4,4 @@ Cargo.lock bin/ pkg/ wasm-pack.log +pkg-release \ No newline at end of file diff --git a/canvas/machine-wasm/src/controller/mod.rs b/canvas/machine-wasm/src/controller/mod.rs index 78743945..e1d6b05b 100644 --- a/canvas/machine-wasm/src/controller/mod.rs +++ b/canvas/machine-wasm/src/controller/mod.rs @@ -272,6 +272,10 @@ impl Controller { m.mem.write(address, &data); Ok(true.into()) } + + pub fn wake(&mut self, machine_id: u16) { + self.canvas.seq.wake(machine_id); + } } #[cfg(test)] diff --git a/canvas/src/engine/effects.ts b/canvas/src/engine/effects.ts index d881a21a..75cff75a 100644 --- a/canvas/src/engine/effects.ts +++ b/canvas/src/engine/effects.ts @@ -2,6 +2,7 @@ import { Effect } from "machine-wasm" import { processSynthEffect } from "@/services/audio" import { processMidiEvent } from "@/services/midi" +import { processSleepEffect } from "@/services/sleep" export function processEffects(effects: Effect[], block: number) { for (const effect of effects) { @@ -13,7 +14,7 @@ export function processEffects(effects: Effect[], block: number) { processSynthEffect(block, effect) break case "Sleep": - console.log("[SLEEP]", effect.duration) + processSleepEffect(block, effect) break default: console.warn("unknown effect:", effect) diff --git a/canvas/src/services/sleep.ts b/canvas/src/services/sleep.ts new file mode 100644 index 00000000..3d96b031 --- /dev/null +++ b/canvas/src/services/sleep.ts @@ -0,0 +1,20 @@ +import { Effect } from "machine-wasm" + +import { engine } from "@/engine" + +export function processSleepEffect(id: number, effect: Effect) { + if (effect.type !== "Sleep") return + + const { duration } = effect + + if (duration.type === "Tick") { + // tick sleep logic - count n ticks before sleep? + } + + if (duration.type === "Ms") { + // ms sleep logic + setTimeout(() => { + engine.ctx.wake(id) + }, duration.value) + } +} diff --git a/machine/src/canvas/event.rs b/machine/src/canvas/event.rs index cab3b8a6..8b7d7146 100644 --- a/machine/src/canvas/event.rs +++ b/machine/src/canvas/event.rs @@ -4,6 +4,14 @@ use tsify::Tsify; use crate::audio::midi::{MidiOutputFormat}; use crate::audio::synth::SynthTrigger; +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Tsify)] +#[serde(tag = "type", content = "value")] +#[tsify(into_wasm_abi, from_wasm_abi, namespace)] +pub enum SleepDuration { + Ms(u16), + Tick(u16), +} + /// Events that can be sent by blocks and machines. /// This event can be considered a side effect that will be executed by the host. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Tsify)] @@ -34,11 +42,3 @@ pub enum Event { }, } -#[derive(Tsify, Debug, Serialize, Deserialize, PartialEq, Clone)] -#[serde(tag = "type", rename_all = "camelCase")] -#[tsify(into_wasm_abi, from_wasm_abi, namespace)] -pub enum SleepDuration { - Ms(u16), - Tick(u16), -} - diff --git a/machine/src/machine/execute.rs b/machine/src/machine/execute.rs index c83763a8..c930907f 100644 --- a/machine/src/machine/execute.rs +++ b/machine/src/machine/execute.rs @@ -246,8 +246,15 @@ impl Execute for Machine { Op::RightShift => s.apply_two(|a, b| Ok(a >> b))?, // Pause the execution of the thread - Op::Sleep(tick) => self.events.push(Event::Sleep {duration: SleepDuration::Tick(tick)}), - Op::SleepMs(ms) => self.events.push(Event::Sleep {duration: SleepDuration::Ms(ms)}), + Op::Sleep(tick) => { + self.sleeping = true; + self.events.push(Event::Sleep {duration: SleepDuration::Tick(tick)}) + }, + + Op::SleepMs(ms) => { + self.sleeping = true; + self.events.push(Event::Sleep {duration: SleepDuration::Ms(ms)}) + }, }; // Advance or jump the program counter. diff --git a/machine/src/machine/mod.rs b/machine/src/machine/mod.rs index 6a01dc06..cb54f06d 100644 --- a/machine/src/machine/mod.rs +++ b/machine/src/machine/mod.rs @@ -41,6 +41,9 @@ pub struct Machine { /// How many messages does the machine expect to receive? pub expected_receives: u16, + + /// Is the machine sleeping? + pub sleeping: bool, } impl Machine { @@ -57,6 +60,7 @@ impl Machine { outbox: vec![], is_debug: false, + sleeping: false, expected_receives: 0, } } @@ -90,6 +94,7 @@ impl Machine { self.reg.reset(); self.mem.reset_stacks(); self.expected_receives = 0; + self.sleeping = false; } } diff --git a/machine/src/sequencer/mod.rs b/machine/src/sequencer/mod.rs index c926a2ef..114c10b2 100644 --- a/machine/src/sequencer/mod.rs +++ b/machine/src/sequencer/mod.rs @@ -10,7 +10,7 @@ use status::MachineStatus::{Awaiting, Halted, Running}; pub use seq_error::SequencerError::*; pub use seq_error::SequencerError; -use crate::status::MachineStatus::{Errored, Invalid, Loaded, Ready}; +use crate::status::MachineStatus::{Errored, Invalid, Loaded, Ready, Sleeping}; type Errorable = Result<(), SequencerError>; type Statuses = HashMap; @@ -106,7 +106,7 @@ impl Sequencer { // Manage state transitions of the machine. match status { - Halted | Invalid | Loaded | Errored => continue, + Halted | Invalid | Loaded | Sleeping | Errored => continue, Ready => { self.statuses.insert(id, Running); } _ => {} } @@ -160,6 +160,12 @@ impl Sequencer { break; } + // Sleep the machine. + if machine.sleeping { + self.statuses.insert(id, Sleeping); + break; + } + // Halt the machine if we reached the end of the program. if machine.should_halt() { self.statuses.insert(id, Halted); @@ -171,6 +177,17 @@ impl Sequencer { Ok(()) } + /// Wake the machine up from sleep. + pub fn wake(&mut self, machine_id: u16) { + // Resume the machine's execution state. + self.statuses.insert(machine_id, Running); + + // Reset the machine's sleeping flag. + if let Some(machine) = self.get_mut(machine_id) { + machine.sleeping = false; + } + } + pub fn is_halted(&self) -> bool { self.statuses.values().all(|s| s == &Halted || s == &Invalid || s == &Errored) } diff --git a/machine/src/sequencer/status.rs b/machine/src/sequencer/status.rs index 9d29e16c..d60838bf 100644 --- a/machine/src/sequencer/status.rs +++ b/machine/src/sequencer/status.rs @@ -22,6 +22,9 @@ pub enum MachineStatus { /// Machine is awaiting a message. Awaiting, + /// Machine is sleeping for a pre-determined duration. zzzzzz! + Sleeping, + /// Machine has reached the end of execution. Halted, diff --git a/machine/tests/sleep.rs b/machine/tests/sleep.rs index 6f29726c..7c745e16 100644 --- a/machine/tests/sleep.rs +++ b/machine/tests/sleep.rs @@ -13,7 +13,7 @@ mod sleep_tests { .into(); m.run().expect("cannot run the test program"); - + assert_eq!(m.sleeping, true); assert_eq!(m.mem.read_stack(2), [0xFF, 0xAB]); assert_eq!(m.events[0], Event::Sleep {duration: SleepDuration::Tick(10)}); assert_eq!(m.events[1], Event::Sleep {duration: SleepDuration::Ms(200)});