From 356e3c8dd0497747b741f3da6385b10f737d7220 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 13 Oct 2023 18:53:38 +0200 Subject: [PATCH] add offset to basic rhythmic functions --- src/API.ts | 46 +++++++++++++++++++++++++++++---------- src/documentation/time.ts | 37 ++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/API.ts b/src/API.ts index a182065..ce27432 100644 --- a/src/API.ts +++ b/src/API.ts @@ -1259,33 +1259,55 @@ export class UserAPI { // Time Filters // ============================================================= - public beat = (...n: number[]): boolean => { - const results: boolean[] = n.map( + public beat = (n: number | number[] = 1, nudge: number = 0): boolean => { + /** + * Determine if the current pulse is on a specified beat, with optional nudge. + * @param n Single beat multiplier or array of beat multipliers + * @param nudge Offset in pulses to nudge the beat forward or backward + * @returns True if the current pulse is on one of the specified beats (considering nudge), false otherwise + */ + const nArray = Array.isArray(n) ? n : [n]; + const results: boolean[] = nArray.map( (value) => - this.app.clock.pulses_since_origin % Math.floor(value * this.ppqn()) === - 0 + (this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) % Math.floor(value * this.ppqn()) === 0 ); return results.some((value) => value === true); }; b = this.beat; - - public bar = (...n: number[]): boolean => { - const results: boolean[] = n.map( + public bar = (n: number | number[] = 1, nudge: number = 0): boolean => { + /** + * Determine if the current pulse is on a specified bar, with optional nudge. + * @param n Single bar multiplier or array of bar multipliers + * @param nudge Offset in bars to nudge the bar forward or backward + * @returns True if the current pulse is on one of the specified bars (considering nudge), false otherwise + */ + const nArray = Array.isArray(n) ? n : [n]; + const barLength = this.app.clock.time_signature[1] * this.ppqn(); + const nudgeInPulses = Math.floor(nudge * barLength); + const results: boolean[] = nArray.map( (value) => - this.app.clock.pulses_since_origin % Math.floor((value * this.ppqn()) * this.app.clock.time_signature[1]) === - 0 + (this.app.clock.pulses_since_origin - nudgeInPulses) % Math.floor(value * barLength) === 0 ); return results.some((value) => value === true); }; B = this.bar; - public pulse = (...n: number[]): boolean => { - const results: boolean[] = n.map( - (value) => this.app.clock.pulses_since_origin % value === 0 + public pulse = (n: number | number[] = 1, nudge: number = 0): boolean => { + /** + * Determine if the current pulse is on a specified pulse count, with optional nudge. + * @param n Single pulse count or array of pulse counts + * @param nudge Offset in pulses to nudge the pulse forward or backward + * @returns True if the current pulse is on one of the specified pulse counts (considering nudge), false otherwise + */ + const nArray = Array.isArray(n) ? n : [n]; + const results: boolean[] = nArray.map( + (value) => (this.app.clock.pulses_since_origin - nudge) % value === 0 ); return results.some((value) => value === true); }; + p = this.pulse; + // ============================================================= // Modulo based time filters diff --git a/src/documentation/time.ts b/src/documentation/time.ts index 0a2b48b..c46cd52 100644 --- a/src/documentation/time.ts +++ b/src/documentation/time.ts @@ -18,9 +18,9 @@ To change the tempo, use the bpm(number) function. The transport is con ## Simple rhythms -Let's study two very simple rhythmic functions, mod(n: ...number[]) and onbeat(...n:number[]). They are both easy to understand and powerful enough to get you to play your first rhythms. - -- beat(...n: number[]): return true every _n_ beats. The value 1 will return true at the beginning of each beat. Floating point numbers like 0.5 or 0.25 are also accepted. Multiple values can be passed to beat to generate more complex rhythms. +- beat(n: number | number[] = 1, offset: number = 1): return true every _n_ beats. + - number: if number = 1, the function will return true every beat. Lists can be used too. + - offset: offset (in beats) to apply. An offset of 0.5 will return true against the beat. ${makeExample( "Using different mod values", @@ -51,7 +51,19 @@ beat([1,0.75].beat(2)) :: blip([50, 100].beat(2)).pan(r(0,1)).out(); false )} -- pulse(...n: number[]): faster version of the beat function. Instead of returning true for every beat, this function is returning true every _n_ clock ticks! It can be used to generate very unexpected rhythms. +${makeExample( + "Multiple values for creating rhythms", ` +beat([1,2.5,3.25], 0)::sound('hat').out() +beat([1,2.25,4])::sound('kick').speed( + [1.25,1].beat(0.5)).out() +onbeat(3, 3.25, 3.125)::sound('shaker').out() +`, false, + )} + +- pulse(n: number | number[] = 1, offset: number = 1): return true every _n_ pulses. A pulse is the tiniest possible rhythmic value. + - number: if number = 1, the function will return true every pulse. Lists can be used too. + - offset: offset (in pulses) to apply. + ${makeExample( "Intriguing rhythms", @@ -73,7 +85,10 @@ beat(1)::snd(['bd', '808oh'].beat(1)).out() false )}; -- bar(...n: number[]): return true every _n_ bars. + +- bar(n: number | number[] = 1, offset: number = 1): return true every _n_ bars. + - number: if number = 1, the function will return true every bar. Lists can be used too. + - offset: offset (in bars) to apply. ${makeExample( "Four beats per bar: proof", @@ -84,6 +99,16 @@ beat(1)::sound('hat').speed(2).out() )} +${makeExample( + "Offsettings trigger signal", + ` +bar(1)::sound('kick').out() +beat(1)::sound('hat').speed(2).out() +beat(1,0.5)::sound('hat').speed(4).out() +bar(1,0.5)::sound('sn').out() +`, false + )} + - onbeat(...n: number[]): The onbeat function allows you to lock on to a specific beat from the clock to execute code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass either integers or floating point numbers. By default, topos is using a 4/4 bar meaning that you can target any of these beats (or in-between) with this function. ${makeExample( @@ -388,7 +413,7 @@ ${makeExample( "Thinking music over bars", ` let roomy = (n) => n.room(1).size(1).cutoff(500 + usaw(1/8) * 5000); -function a() { +function a() { beat(1) && roomy(sound('kick')).out() beat(.5) && roomy(sound('hat')).out() }