Change clock starting point and add oncount method as alternative to onbeat
This commit is contained in:
@ -983,11 +983,14 @@ export class UserAPI {
|
|||||||
* @param beat - The beats to check
|
* @param beat - The beats to check
|
||||||
* @returns True if the current beat is in the given list of beats
|
* @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[] = [];
|
let final_pulses: boolean[] = [];
|
||||||
beat.forEach((b) => {
|
beat.forEach((b) => {
|
||||||
const pulses = Math.floor(b * this.ppqn());
|
const beat = b % this.nominator() || this.nominator();
|
||||||
return final_pulses.push(origin % pulses === 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
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return final_pulses.some((p) => p == true);
|
return final_pulses.some((p) => p == true);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -107,7 +107,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
|
|||||||
*
|
*
|
||||||
* @param array - The array of values to pick from
|
* @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 () {
|
Array.prototype.pulse = function () {
|
||||||
|
|||||||
@ -32,7 +32,6 @@ const stringObject = (str: string, params: object) => {
|
|||||||
export const makeStringExtensions = (api: UserAPI) => {
|
export const makeStringExtensions = (api: UserAPI) => {
|
||||||
String.prototype.speak = function () {
|
String.prototype.speak = function () {
|
||||||
const options = JSON.parse(this.valueOf());
|
const options = JSON.parse(this.valueOf());
|
||||||
console.log("SPEAKING:", options);
|
|
||||||
new Speaker({ ...options, text: options.text }).speak().then(() => {
|
new Speaker({ ...options, text: options.text }).speak().then(() => {
|
||||||
// Done
|
// Done
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
@ -74,6 +73,8 @@ type SpeechOptions = {
|
|||||||
lang?: string;
|
lang?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let speakerTimeout: number;
|
||||||
|
|
||||||
export class Speaker {
|
export class Speaker {
|
||||||
constructor(
|
constructor(
|
||||||
public options: SpeechOptions
|
public options: SpeechOptions
|
||||||
@ -83,7 +84,8 @@ export class Speaker {
|
|||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (this.options.text) {
|
if (this.options.text) {
|
||||||
const synth = window.speechSynthesis;
|
const synth = window.speechSynthesis;
|
||||||
synth.cancel();
|
if(synth.speaking) synth.cancel();
|
||||||
|
|
||||||
const utterance = new SpeechSynthesisUtterance(this.options.text);
|
const utterance = new SpeechSynthesisUtterance(this.options.text);
|
||||||
utterance.rate = this.options.rate || 1;
|
utterance.rate = this.options.rate || 1;
|
||||||
utterance.pitch = this.options.pitch || 1;
|
utterance.pitch = this.options.pitch || 1;
|
||||||
@ -111,7 +113,17 @@ export class Speaker {
|
|||||||
reject(error);
|
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 {
|
} else {
|
||||||
reject("No text provided");
|
reject("No text provided");
|
||||||
|
|||||||
@ -256,7 +256,7 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
mod(2) :: speak("Topos!","fr",irand(0,5))
|
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(
|
${makeExample(
|
||||||
"Chaining string",
|
"Chaining string",
|
||||||
`
|
`
|
||||||
onbeat(1,3) :: "Foobaba".voice(irand(0,10)).speak()
|
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
|
||||||
`,
|
`,
|
||||||
true
|
true
|
||||||
)}
|
)}
|
||||||
@ -284,18 +284,19 @@ ${makeExample(
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
${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()
|
const croissant = ["Volant", "Arc-en-ciel", "Chocolat", "Dansant", "Nuage", "Tournant", "Galaxie", "Chatoyant", "Flamboyant", "Cosmique","Croissant!"];
|
||||||
.lang("fr")
|
|
||||||
.volume(rand(0.2,2.0))
|
|
||||||
.rate(rand(.4,.6))
|
|
||||||
.speak();
|
|
||||||
|
|
||||||
|
onbeat(4) :: croissant.bar()
|
||||||
|
.lang("fr")
|
||||||
|
.volume(rand(0.2,2.0))
|
||||||
|
.rate(rand(.4,.6))
|
||||||
|
.speak();
|
||||||
`,
|
`,
|
||||||
false
|
true
|
||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -89,7 +89,32 @@ mod([.25, 1/8].div(1.5))::snd('hat').n(2)
|
|||||||
false
|
false
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
- <ic>oncount(beats: number[], meter: number)</ic>: This function is similar to <ic>onbeat</ic> 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
|
## 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 <ic>mod(...n:number[])</ic> that you just learned about!
|
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 <ic>mod(...n:number[])</ic> that you just learned about!
|
||||||
|
|||||||
Reference in New Issue
Block a user