From 86739faa7788a84e1fea03a5f4912af944edbe88 Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Thu, 31 Aug 2023 18:03:07 +0300 Subject: [PATCH] Change clock starting point and add oncount method as alternative to onbeat --- src/API.ts | 76 ++++++++++++++++++++++++++------------- src/ArrayExtensions.ts | 2 +- src/Clock.ts | 6 ++-- src/TransportNode.js | 11 +++--- src/TransportProcessor.js | 4 +-- src/classes/ZPlayer.ts | 6 ++-- 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/src/API.ts b/src/API.ts index 4d4b373..1e5e1da 100644 --- a/src/API.ts +++ b/src/API.ts @@ -862,7 +862,7 @@ export class UserAPI { * * @returns The current bar number */ - return this.app.clock.time_position.bar; + return this.app.clock.time_position.bar + 1; }; tick = (): number => { @@ -871,7 +871,7 @@ export class UserAPI { * * @returns The current tick number */ - return this.app.clock.tick; + return this.app.clock.tick + 1; }; pulse = (): number => { @@ -880,7 +880,7 @@ export class UserAPI { * * @returns The current pulse number */ - return this.app.clock.time_position.pulse; + return this.app.clock.time_position.pulse + 1; }; beat = (): number => { @@ -889,57 +889,66 @@ export class UserAPI { * * @returns The current beat number */ - return this.app.clock.time_position.beat; + return this.app.clock.time_position.beat + 1; }; ebeat = (): number => { /** * Returns the current beat number since the origin of time */ - return this.app.clock.beats_since_origin; + return this.app.clock.beats_since_origin + 1; }; epulse = (): number => { /** * Returns the current number of pulses elapsed since origin of time */ - return this.app.clock.pulses_since_origin; + return this.app.clock.pulses_since_origin + 1; }; - signature = (): number[] => { + nominator = (): number => { /** - * Returns the current time signature + * Returns the current nominator of the time signature */ - return this.app.clock.time_signature; + return this.app.clock.time_signature[0]; + } + + meter = (): number => { + /** + * Returns the current meter (denominator of the time signature) + */ + return this.app.clock.time_signature[1]; }; + denominator = this.meter; + // ============================================================= // Time Filters // ============================================================= public mod = (...n: number[]): boolean => { const results: boolean[] = n.map( - (value) => this.epulse() % Math.floor(value * this.ppqn()) === 0 + (value) => this.app.clock.pulses_since_origin % Math.floor(value * this.ppqn()) === 0 ); return results.some((value) => value === true); }; public modpulse = (...n: number[]): boolean => { - const results: boolean[] = n.map((value) => this.epulse() % value === 0); + const results: boolean[] = n.map((value) => this.app.clock.pulses_since_origin % value === 0); return results.some((value) => value === true); }; - modp = this.modpulse; + pmod = this.modpulse; public modbar = (...n: number[]): boolean => { const results: boolean[] = n.map( - (value) => this.bar() % Math.floor(value * this.ppqn()) === 0 + (value) => this.app.clock.time_position.bar % Math.floor(value * this.ppqn()) === 0 ); return results.some((value) => value === true); }; bmod = this.modbar; public div = (chunk: number): boolean => { - const time_pos = this.epulse(); + const time_pos = this.app.clock.pulses_since_origin; const current_chunk = Math.floor( time_pos / Math.floor(chunk * this.ppqn()) ); @@ -947,7 +956,7 @@ export class UserAPI { }; public divbar = (chunk: number): boolean => { - const time_pos = this.bar() - 1; + const time_pos = this.app.clock.time_position.bar - 1; const current_chunk = Math.floor(time_pos / chunk); return current_chunk % 2 === 0; }; @@ -956,7 +965,7 @@ export class UserAPI { bars: number[] | number, n: number = this.app.clock.time_signature[0] ): boolean => { - let current_bar = (this.bar() % n) + 1; + let current_bar = (this.app.clock.time_position.bar % n) + 1; return typeof bars === "number" ? bars === current_bar : bars.some((b) => b == current_bar); @@ -972,14 +981,33 @@ export class UserAPI { * @param beat - The beats to check * @returns True if the current beat is in the given list of beats */ + const origin = this.app.clock.pulses_since_origin; let final_pulses: boolean[] = []; beat.forEach((b) => { - const beat = b % this.signature()[0] || this.signature()[0]; - const integral_part = Math.floor(beat); - const decimal_part = (beat - integral_part) * this.ppqn() + 1; - final_pulses.push( - integral_part === this.beat() && this.pulse() === decimal_part - ); + const pulses = Math.floor(b * this.ppqn()); + return final_pulses.push(origin % pulses === 0); + }); + return final_pulses.some((p) => p == true); + }; + + oncount = (beats: number[]|number, count: number): boolean => { + /** + * Returns true if the current beat is in the given list of beats. + * + * @remarks + * This function can also operate with decimal beats! + * + * @param beat - The beats to check + * @returns True if the current beat is in the given list of beats + */ + if(typeof beats === "number") beats = [beats]; + const origin = this.app.clock.pulses_since_origin; + let final_pulses: boolean[] = []; + beats.forEach((b) => { + b = b<1 ? 0 : b-1; + const beatInTicks = Math.ceil(b * this.ppqn()); + const meterPosition = origin % (this.ppqn() * count); + return final_pulses.push(meterPosition === beatInTicks); }); return final_pulses.some((p) => p == true); }; @@ -1342,7 +1370,7 @@ export class UserAPI { public divseq = (...args: any): any => { const chunk_size = args[0]; // Get the first argument (chunk size) const elements = args.slice(1); // Get the rest of the arguments as an array - const timepos = this.epulse(); + const timepos = this.app.clock.pulses_since_origin; const slice_count = Math.floor( timepos / Math.floor(chunk_size * this.ppqn()) ); @@ -1355,7 +1383,7 @@ export class UserAPI { * * @param array - The array of values to pick from */ - return array[this.ebeat() % array.length]; + return array[this.app.clock.time_position.beat % array.length]; }; public seqbar = (...array: T[]): T => { diff --git a/src/ArrayExtensions.ts b/src/ArrayExtensions.ts index a46b015..67d0ff2 100644 --- a/src/ArrayExtensions.ts +++ b/src/ArrayExtensions.ts @@ -98,7 +98,7 @@ export const makeArrayExtensions = (api: UserAPI) => { * * @returns The element corresponding to the current beat */ - return this[(api.ebeat() / beat) % this.length]; + return this[(api.app.clock.beats_since_origin / beat) % this.length]; }; Array.prototype.bar = function () { diff --git a/src/Clock.ts b/src/Clock.ts index aa93ee6..e50029d 100644 --- a/src/Clock.ts +++ b/src/Clock.ts @@ -40,9 +40,9 @@ export class Clock { tick: number; constructor(public app: Editor, ctx: AudioContext) { - this.time_position = { bar: 0, beat: 0, pulse: 0 }; + this.time_position = { bar: -1, beat: -1, pulse: -1 }; this.time_signature = [4, 4]; - this.tick = 0; + this.tick = -1; this._bpm = 120; this._ppqn = 48; this.transportNode = null; @@ -157,7 +157,7 @@ export class Clock { /** * Stops the TransportNode (stops the clock). */ - this.app.clock.tick = 0; + this.app.clock.tick = -1; this.transportNode?.stop(); } } diff --git a/src/TransportNode.js b/src/TransportNode.js index 8a28251..a515b5e 100644 --- a/src/TransportNode.js +++ b/src/TransportNode.js @@ -15,9 +15,11 @@ export class TransportNode extends AudioWorkletNode { handleMessage = (message) => { if (message.data && message.data.type === "bang") { this.logicalTime = message.data.logicalTime; + + this.app.clock.tick++ const futureTimeStamp = this.convertTicksToTimeposition(this.app.clock.tick); - // console.log("BANG", this.logicalTime, futureTimeStamp); + //console.log("BANG", this.logicalTime, futureTimeStamp); this.app.clock.time_position = futureTimeStamp; tryEvaluate(this.app, this.app.global_buffer); @@ -27,11 +29,10 @@ export class TransportNode extends AudioWorkletNode { convertTicksToTimeposition(ticks) { const beatsPerBar = this.app.clock.time_signature[0]; - const ppqnPosition = (ticks % this.app.clock.ppqn)+1; + const ppqnPosition = (ticks % this.app.clock.ppqn); const beatNumber = Math.floor(ticks / this.app.clock.ppqn); - const barNumber = Math.floor(beatNumber / beatsPerBar)+1; - const beatWithinBar = Math.floor(beatNumber % beatsPerBar)+1; - this.app.clock.tick++ + const barNumber = Math.floor(beatNumber / beatsPerBar); + const beatWithinBar = Math.floor(beatNumber % beatsPerBar); return {bar: barNumber, beat: beatWithinBar, pulse: ppqnPosition}; } diff --git a/src/TransportProcessor.js b/src/TransportProcessor.js index de6f6d5..19dc8b3 100644 --- a/src/TransportProcessor.js +++ b/src/TransportProcessor.js @@ -30,9 +30,9 @@ class TransportProcessor extends AudioWorkletProcessor { this.lastPausedTime = 0; this.wasStopped = true; this.currentPulsePosition = 0; - } else if(message.data === 'bpm') { + } else if(message.data.type === 'bpm') { this.bpm = message.data.value; - } else if(message.data === 'ppqn') { + } else if(message.data.type === 'ppqn') { this.ppqn = message.data.value; } }; diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts index ebcd3fc..248ba50 100644 --- a/src/classes/ZPlayer.ts +++ b/src/classes/ZPlayer.ts @@ -94,8 +94,8 @@ export class Player extends Event { areWeThereYet = (): boolean => { // If clock has stopped if (this.app.clock.pulses_since_origin < this.lastCallTime) { - this.lastCallTime = 1; - this.startCallTime = 1; + this.lastCallTime = 0; + this.startCallTime = 0; this.index = 0; this.waitTime = 0; } @@ -104,7 +104,7 @@ export class Player extends Event { const howAboutNow = // If pattern is just starting (this.notStarted() && - (this.pulse() === 1 || this.origin() >= this.nextBeatInTicks()) && + (this.pulse() === 0 || this.origin() >= this.nextBeatInTicks()) && this.origin() >= this.waitTime) || // If pattern is already playing (this.current &&