From 323c6821d31858a1640bfe163421eaee973722f0 Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Thu, 31 Aug 2023 19:42:45 +0300 Subject: [PATCH] Change clock starting point and add oncount method as alternative to onbeat --- src/API.ts | 9 ++++++--- src/ArrayExtensions.ts | 2 +- src/StringExtensions.ts | 18 +++++++++++++++--- src/documentation/synths.ts | 21 +++++++++++---------- src/documentation/time.ts | 27 ++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/API.ts b/src/API.ts index af9c9b8..b728ef8 100644 --- a/src/API.ts +++ b/src/API.ts @@ -983,11 +983,14 @@ 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 pulses = Math.floor(b * this.ppqn()); - return final_pulses.push(origin % pulses === 0); + const beat = b % this.nominator() || this.nominator(); + 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 + ); }); return final_pulses.some((p) => p == true); }; diff --git a/src/ArrayExtensions.ts b/src/ArrayExtensions.ts index 67d0ff2..3628fd0 100644 --- a/src/ArrayExtensions.ts +++ b/src/ArrayExtensions.ts @@ -107,7 +107,7 @@ export const makeArrayExtensions = (api: UserAPI) => { * * @param array - The array of values to pick from */ - return this[(api.app.clock.time_position.bar + 1) % this.length]; + return this[api.app.clock.time_position.bar % this.length]; }; Array.prototype.pulse = function () { diff --git a/src/StringExtensions.ts b/src/StringExtensions.ts index ecde428..65931c5 100644 --- a/src/StringExtensions.ts +++ b/src/StringExtensions.ts @@ -32,7 +32,6 @@ const stringObject = (str: string, params: object) => { export const makeStringExtensions = (api: UserAPI) => { String.prototype.speak = function () { const options = JSON.parse(this.valueOf()); - console.log("SPEAKING:", options); new Speaker({ ...options, text: options.text }).speak().then(() => { // Done }).catch((e) => { @@ -74,6 +73,8 @@ type SpeechOptions = { lang?: string; } +let speakerTimeout: number; + export class Speaker { constructor( public options: SpeechOptions @@ -83,7 +84,8 @@ export class Speaker { return new Promise((resolve, reject) => { if (this.options.text) { const synth = window.speechSynthesis; - synth.cancel(); + if(synth.speaking) synth.cancel(); + const utterance = new SpeechSynthesisUtterance(this.options.text); utterance.rate = this.options.rate || 1; utterance.pitch = this.options.pitch || 1; @@ -111,7 +113,17 @@ export class Speaker { reject(error); }; - synth.speak(utterance); + if(synth.speaking) { + // Cancel again? + synth.cancel(); + // Set timeout + if(speakerTimeout) clearTimeout(speakerTimeout); + speakerTimeout = setTimeout(() => { + synth.speak(utterance); + }, 200); + } else { + synth.speak(utterance); + } } else { reject("No text provided"); diff --git a/src/documentation/synths.ts b/src/documentation/synths.ts index 1e5fd00..c5842df 100644 --- a/src/documentation/synths.ts +++ b/src/documentation/synths.ts @@ -256,7 +256,7 @@ ${makeExample( ` mod(2) :: speak("Topos!","fr",irand(0,5)) `, - false + true )} @@ -265,7 +265,7 @@ You can also use speech by chaining methods to a string: ${makeExample( "Chaining string", ` - onbeat(1,3) :: "Foobaba".voice(irand(0,10)).speak() + onbeat(4) :: "Foobaba".voice(irand(0,10)).speak() `, true )} @@ -284,18 +284,19 @@ ${makeExample( )} ${makeExample( - "String chaining with array chaining", + "Live coded poetry with array and string chaining", ` - const croissant = ["Croissant!", "Volant", "Arc-en-ciel", "Chocolat", "Dansant", "Nuage", "Tournant", "Galaxie", "Chatoyant", "Flamboyant", "Cosmique"]; + bpm(70) - onbeat(1) :: croissant.bar() - .lang("fr") - .volume(rand(0.2,2.0)) - .rate(rand(.4,.6)) - .speak(); + const croissant = ["Volant", "Arc-en-ciel", "Chocolat", "Dansant", "Nuage", "Tournant", "Galaxie", "Chatoyant", "Flamboyant", "Cosmique","Croissant!"]; + onbeat(4) :: croissant.bar() + .lang("fr") + .volume(rand(0.2,2.0)) + .rate(rand(.4,.6)) + .speak(); `, - false + true )} `; }; diff --git a/src/documentation/time.ts b/src/documentation/time.ts index 21191f9..5489cf2 100644 --- a/src/documentation/time.ts +++ b/src/documentation/time.ts @@ -89,7 +89,32 @@ mod([.25, 1/8].div(1.5))::snd('hat').n(2) false )} - +- oncount(beats: number[], meter: number): This function is similar to onbeat but it allows you to specify a custom meter (time signature denominator) for the beats. + +${makeExample( + "Using oncount to create more variation in the rhythm", + ` + bpm(120) + z1('q (0 4 2 9)+(0 3 1 5)').sound('sine').cutoff(400).delay(0.5).out() + onbeat(1,1.5,2,3,4) :: sound('bd').gain(2.0).out() + oncount([1,3,5.5,7,7.5,8],8) :: sound('hh').gain(irand(1.0,4.0)).out() +`, + true +)} + +${makeExample( + "Using oncount to create rhythms with a custom meter", + ` + bpm(280) + oncount([1,5,9,13],16) :: sound('bd').gain(3.0).out() + oncount([5,6,13],16) :: sound('cp').gain(1.0).out() + oncount([2,3,3.5,6,7,10,15],16) :: sound('hh').n(8).gain(1.0).out() + oncount([1,4,5,8,9,10,11,12,13,14,15,16],16) :: + sound('hh').out() +`, + true +)} + ## Rhythm generators We included a bunch of popular rhythm generators in Topos such as the euclidian rhythms algorithms or the one to generate rhythms based on a binary sequence. They all work using _iterators_ that you will gradually learn to use for iterating over lists. Note that they are levaraging mod(...n:number[]) that you just learned about!