diff --git a/Monkeydo.mjs b/Monkeydo.mjs index 34ea401..8fb502d 100644 --- a/Monkeydo.mjs +++ b/Monkeydo.mjs @@ -26,8 +26,8 @@ export default class Monkeydo extends MonkeyMaster { if(typeof times !== "number") { times = parseInt(times); } - // Clamp number to 8 bit max - times = Math.min(Math.max(times,0),255); + times = Math.floor(times); + times = Math.min(Math.max(times,0),255); // Clamp number to 8 bits return await this.setFlag("playing",times); } @@ -38,4 +38,14 @@ export default class Monkeydo extends MonkeyMaster { } return await this.loadManifest(manifest); } + + async play(manifest = null) { + if(!this.ready && !manifest) throw new Error("Can not start playback without a manifest"); + if(manifest) { + const load = this.load(manifest) + load.then(() => this.start()); + return; + } + return await this.start(); + } } \ No newline at end of file diff --git a/monkey/Monkey.js b/monkey/Monkey.js index 04389de..557b068 100644 --- a/monkey/Monkey.js +++ b/monkey/Monkey.js @@ -6,6 +6,8 @@ class Monkey { constructor() { this.flags = new Uint8ClampedArray(2); this.tasks = []; + this.tasksLength = 0; + this.i = 0; // Runtime task queue this.queue = { thisTask: null, @@ -15,21 +17,22 @@ class Monkey { // Task scheduler next() { - if(this.flags[0] === 0) return; + if(this.flags[0] === 0 || this.flags[1] === 0) return; const task = this.tasks[this.i]; + console.log(task,this.i); // Run task after delay this.queue.thisTask = setTimeout(() => { // Dispatch task to main thread - this.postMessage(["TASK",task]); + postMessage(["TASK",task]); this.i++; },task[0]); // Loop until flag is 0 or infinite if 255 - if(this.i === this.tasks.length) { + if(this.i === this.tasksLength) { this.i = 0; - if(flags[1] === 255) return; - flags[1]--; + if(this.flags[1] === 255) return; + this.flags[1]--; } // Queue the next task @@ -52,6 +55,11 @@ class Monkey { // Fetch and install manifest from URL async fetchManifest(url) { const manifest = await fetch(url); + if(!manifest.ok) { + console.error("Monkeydo fetch error:",manifest); + throw new Error("Server responded with an error"); + }; + const json = await manifest.json(); return await this.loadManifest(json); } @@ -68,6 +76,8 @@ class Monkey { } } this.tasks = manifest.tasks; + // Store length as property so we don't have to calculate the offset each iteration of next() + this.tasksLength = manifest.tasks.length - 1; this.flags[0] = 1; // Manifest loaded: true resolve(); }); diff --git a/monkey/MonkeyMaster.mjs b/monkey/MonkeyMaster.mjs index e086f12..d6739d1 100644 --- a/monkey/MonkeyMaster.mjs +++ b/monkey/MonkeyMaster.mjs @@ -18,8 +18,7 @@ export default class MonkeyMaster { // Copy flags and clear queue const flags = [...this.queue._flags]; this.queue._flags = []; - - flags.forEach(flag => this.setFlag(flag)); + flags.forEach(flag => this.setFlag(...flag)); } }; } @@ -40,6 +39,11 @@ export default class MonkeyMaster { async init() { // Spawn and wrap dedicated worker with Comlink const worker = new Worker(this.getWorkerPath()); + worker.addEventListener("message",event => { + if(event.data[0] !== "TASK") return; + this.do(event.data); + }); + const Monkey = Comlink.wrap(worker); this.comlink = await new Monkey(); @@ -60,13 +64,14 @@ export default class MonkeyMaster { "PLAYING" ]; + // Translate string to index if(typeof flag === "string" || flag < 0) { - const key = flags.indexOf(flag.toUpperCase()); - if(key < 0) return; + flag = flags.indexOf(flag.toUpperCase()); + if(flag < 0) return; } - // Check key is in bounds + // Check that key is in bounds if(flag < 0 || flags > flags.length - 1) { throw new Error(`Array key '${flag}' out of range`); } @@ -94,15 +99,28 @@ export default class MonkeyMaster { return update; } + // Load a Monkeydo manifest by URL or JSON string async loadManifest(manifest) { if(!this.ready) await this.init(); - try { - const url = new URL(manifest); - this.comlink.fetchManifest(url.toString()); - } - catch { - this.comlink.loadManifest(manifest); - } - return true; + return await new Promise((resolve,reject) => { + let load = null; + try { + const url = new URL(manifest); + load = this.comlink.fetchManifest(url.toString()); + } + catch { + load = this.comlink.loadManifest(manifest); + } + load.then(() => resolve()) + .catch(() => reject("Failed to load manifest")); + }); + } + + // Start playback of a loaded manifest + async start() { + // Set playstate if no value is present already + const loop = await this.getFlag("playing"); + if(loop < 1) await this.setFlag("playing",1); + return await this.comlink.next(); } } \ No newline at end of file