diff --git a/package.json b/package.json index 49f9613..f0e1838 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "tone": "^14.8.49", "unique-names-generator": "^4.7.1", "vite-plugin-markdown": "^2.1.0", - "zifferjs": "^0.0.13", + "zifferjs": "^0.0.14", "zzfx": "^1.2.0" } } diff --git a/src/API.ts b/src/API.ts index ce6769d..1d65b41 100644 --- a/src/API.ts +++ b/src/API.ts @@ -316,10 +316,7 @@ export class UserAPI { player = new Player(input, options, this.app); this.app.api.patternCache.set(key, player); } - if ((player && player.notStarted()) || player.played) { - player.callTime = this.epulse(); - player.played = false; - } + if(player) player.updateLastCallTime(); return player; }; diff --git a/src/Clock.ts b/src/Clock.ts index bbd23c7..ed6c987 100644 --- a/src/Clock.ts +++ b/src/Clock.ts @@ -65,12 +65,23 @@ export class Clock { * * @returns number of ticks until next bar */ - const currentBeatInTicks = ((this.app.clock.beats_since_origin - 1) * 48) + this.time_position.pulse + 1 + const currentBeatInTicks = ((this.app.clock.beats_since_origin - 1) * this.ppqn) + this.time_position.pulse + 1 const nextBarinTicks = (this.beats_per_bar * this.ppqn) * this.time_position.bar + 1 - return nextBarinTicks - currentBeatInTicks + return nextBarinTicks - currentBeatInTicks; } - get beats_per_bar(): number { + get next_beat_in_ticks(): number { + /** + * This function returns the number of ticks separating the current moment + * from the beginning of the next beat. + * + * @returns number of ticks until next beat + */ + const ticksMissingToNextBeat = (this.time_position.pulse + 1) % this.ppqn; + return this.app.clock.pulses_since_origin + ticksMissingToNextBeat; + } + + get beats_per_bar(): number { /** * Returns the number of beats per bar. */ @@ -110,7 +121,6 @@ export class Clock { return n * this.pulse_duration } - public start(): void { /** * Starts the TransportNode (starts the clock). diff --git a/src/Evaluator.ts b/src/Evaluator.ts index c9130c7..d5659ed 100644 --- a/src/Evaluator.ts +++ b/src/Evaluator.ts @@ -18,7 +18,9 @@ const tryCatchWrapper = ( return new Promise((resolve, _) => { try { Function( - `"use strict";try{${codeReplace(code)}} catch (e) {console.log(e); _reportError(e);};` + `"use strict";try{${codeReplace( + code + )}} catch (e) {console.log(e); _reportError(e);};` ).call(application.api); resolve(true); } catch (error) { @@ -64,7 +66,7 @@ export const tryEvaluate = async ( const newFunction = new Function( `"use strict";try{${codeReplace( wrappedCode - )}} catch (e) {console.log(e)};` + )}} catch (e) {console.log(e); _reportError(e);};` ); addFunctionToCache(candidateCode, newFunction); } else { diff --git a/src/TransportNode.js b/src/TransportNode.js index 3ffd396..e961cac 100644 --- a/src/TransportNode.js +++ b/src/TransportNode.js @@ -21,20 +21,12 @@ export class TransportNode extends AudioWorkletNode { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; this.indexOfLastLatencies = 0; - // setInterval(() => this.ping(), 1000); - this.startTime = null; - this.elapsedTime = 0; } /** @type {(this: MessagePort, ev: MessageEvent) => any} */ handleMessage = (message) => { if (message.data && message.data.type === "bang") { - if (this.startTime === null) { - this.startTime = message.data.currentTime; - } - this.elapsedTime = message.data.currentTime - this.startTime; - this.prevCurrentTime = message.data.currentTime; - let { futureTimeStamp, timeToNextPulse, nextPulsePosition } = this.convertTimeToNextBarsBeats(this.elapsedTime); + let { futureTimeStamp, timeToNextPulse, nextPulsePosition } = this.convertTimeToNextBarsBeats(message.data.logicalTime); // Evaluate the global buffer only once per ppqn value if (this.nextPulsePosition !== nextPulsePosition) { @@ -42,7 +34,6 @@ export class TransportNode extends AudioWorkletNode { setTimeout(() => { const now = this.app.audioContext.currentTime; this.app.clock.time_position = futureTimeStamp; - // this.$clock.innerHTML = `[${futureTimeStamp.bar}:${futureTimeStamp.beat}:${zeroPad(futureTimeStamp.pulse, '2')}]`; tryEvaluate(this.app, this.app.global_buffer); this.hasBeenEvaluated = true; this.currentPulsePosition = nextPulsePosition; @@ -65,8 +56,6 @@ export class TransportNode extends AudioWorkletNode { } stop() { - this.startTime = null; - this.elapsedTime = null; this.app.clock.tick = 0; // this.$clock.innerHTML = `[${1} | ${1} | ${zeroPad(1, '2')}]`; this.port.postMessage("stop"); diff --git a/src/TransportProcessor.js b/src/TransportProcessor.js index da91ba9..aab0fdf 100644 --- a/src/TransportProcessor.js +++ b/src/TransportProcessor.js @@ -4,7 +4,11 @@ class TransportProcessor extends AudioWorkletProcessor { super(options); this.port.addEventListener("message", this.handleMessage); this.port.start(); - this.stated = false; + this.started = false; + this.totalPausedTime = 0; + this.lastPausedTime = 0; + this.startedAgainTime = 0; + this.wasStopped = false; } handleMessage = (message) => { @@ -14,14 +18,33 @@ class TransportProcessor extends AudioWorkletProcessor { this.started = true; } else if (message.data === "pause") { this.started = false; + if(this.lastPausedTime === 0) { + this.lastPausedTime = currentTime; + } } else if (message.data === "stop") { this.started = false; - this.currentTime = 0; + this.totalPausedTime = 0; + this.lastPausedTime = 0; + this.startedAgainTime = 0; + this.wasStopped = true; } }; process(inputs, outputs, parameters) { - if (this.started) this.port.postMessage({ type: "bang", currentTime }); + if (this.started) { + if(this.lastPausedTime>0) { + const pausedTime = currentTime-this.lastPausedTime; + this.totalPausedTime += pausedTime; + this.lastPausedTime = 0; + } + if(this.wasStopped) { + this.startedAgainTime = currentTime; + this.wasStopped = false; + } + const logicalTime = currentTime-this.totalPausedTime-this.startedAgainTime; + //console.log("Logical/Current:", logicalTime, currentTime); + this.port.postMessage({ type: "bang", logicalTime }); + } return true; } } diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts index 2486e08..ee4b9f9 100644 --- a/src/classes/ZPlayer.ts +++ b/src/classes/ZPlayer.ts @@ -9,12 +9,14 @@ import { RestEvent } from "./RestEvent"; export class Player extends Event { input: string; ziffers: Ziffers; - callTime: number = 0; + firstCallTime: number = 0; + lastCallTime: number = 0; + waitTime = 0; startBeat: number = 0; played: boolean = false; current!: Pitch|Chord|ZRest; retro: boolean = false; - tick: number = 0; + index: number = -1; constructor(input: string, options: object, public app: Editor) { super(app); @@ -22,6 +24,22 @@ export class Player extends Event { this.ziffers = new Ziffers(input, options); } + get ticks(): number { + const dur = this.ziffers.duration; + return dur * 4 * this.app.clock.ppqn; + } + + nextEndTime(): number { + return this.firstCallTime + this.ticks; + } + + updateLastCallTime(): void { + if (this.notStarted() || this.played) { + this.lastCallTime = this.app.clock.pulses_since_origin; + this.played = false; + } + } + notStarted(): boolean { return this.ziffers.notStarted(); } @@ -36,21 +54,38 @@ export class Player extends Event { return this.app.clock.convertPulseToSecond(pulse); } + // Check if it's time to play the event areWeThereYet = (): boolean => { + // If clock has stopped + if(this.app.clock.pulses_since_origin= this.app.clock.next_beat_in_ticks) && + (this.app.clock.pulses_since_origin+1 >= this.firstCallTime+this.waitTime) + ) + || + ( // If pattern is already playing this.current && - this.pulseToSecond(this.app.api.epulse()+1) >= - this.pulseToSecond(this.callTime) + + this.pulseToSecond(this.app.clock.pulses_since_origin+1) >= + this.pulseToSecond(this.lastCallTime) + (this.current.duration*4) * this.pulseToSecond(this.app.api.ppqn()) ) ); - if(howAboutNow) { - this.tick = 0; - } else { - this.tick++; + + // Increment index of how many times sound/midi have been called + this.index = howAboutNow ? this.index+1 : this.index; + + if(howAboutNow && this.notStarted()) { + this.firstCallTime = this.app.clock.pulses_since_origin+1; } + return howAboutNow; } @@ -99,12 +134,33 @@ export class Player extends Event { } retrograde() { - if(this.tick === 0 && this.ziffers.index === 0) { + if(this.index === -1 && this.ziffers.index === -1) { this.ziffers.retrograde(); } return this; } + wait(value: number) { + if(this.index === -1 && this.ziffers.index === -1) { + + // TODO: THIS LATER! + + /* if(typeof value === "string") { + const cueKey = this.app.api.patternCues.get(value); + if(cueKey) { + const waitedPatter = this.app.api.patternCache.get(cueKey) as Player; + if(waitedPatter) { + this.waitTime = waitedPatter.nextEndTime(); + } + } + } */ + + this.waitTime = Math.ceil(value*4*this.app.clock.ppqn); + + } + return this; + } + out = (): void => { // TODO? } diff --git a/yarn.lock b/yarn.lock index 799b986..347cbc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1446,10 +1446,10 @@ yaml@^2.1.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== -zifferjs@^0.0.13: - version "0.0.13" - resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.13.tgz#af155633357c95da6a4e5aaa84f23013b5574236" - integrity sha512-eNOQOn+NM4L3v2FqQEf0RSiJOKiZMaotGLGj1VBCPHi5WhHp3N61R7k9ZrnQKhPnfSI80NBoplhQ1Q1sdEjFlQ== +zifferjs@^0.0.14: + version "0.0.14" + resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.14.tgz#7876c799a08e799be7af22b65f4cb6f0b44f79ca" + integrity sha512-CpS3zTm8Btm8aTxd7sSUgVCF/S/jJ3hqwgp7uRzbZI8k6yJWhzo/rjMlEZoOmeBhs7Qy4XsVk7pfrLdS8AAIVA== zzfx@^1.2.0: version "1.2.0"