Documented syncing and updated zifferjs

This commit is contained in:
2023-12-11 22:36:28 +02:00
parent 491461e354
commit 2d3c48c1c1
9 changed files with 103 additions and 23 deletions

View File

@ -196,6 +196,7 @@
<p rel="noopener noreferrer" id="docs_ziffers_rhythm" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Rhythm</p> <p rel="noopener noreferrer" id="docs_ziffers_rhythm" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Rhythm</p>
<p rel="noopener noreferrer" id="docs_ziffers_algorithmic" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Algorithmic</p> <p rel="noopener noreferrer" id="docs_ziffers_algorithmic" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Algorithmic</p>
<p rel="noopener noreferrer" id="docs_ziffers_tonnetz" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Tonnetz</p> <p rel="noopener noreferrer" id="docs_ziffers_tonnetz" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Tonnetz</p>
<p rel="noopener noreferrer" id="docs_ziffers_syncing" class="ml-8 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Syncing</p>
</div> </div>
</details> </details>

View File

@ -43,7 +43,7 @@
"tone": "^14.8.49", "tone": "^14.8.49",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"vite-plugin-markdown": "^2.1.0", "vite-plugin-markdown": "^2.1.0",
"zifferjs": "^0.0.50", "zifferjs": "^0.0.51",
"zyklus": "^0.1.4", "zyklus": "^0.1.4",
"zzfx": "^1.2.0" "zzfx": "^1.2.0"
} }

View File

@ -87,6 +87,7 @@ export class UserAPI {
public currentSeed: string | undefined = undefined; public currentSeed: string | undefined = undefined;
public localSeeds = new Map<string, Function>(); public localSeeds = new Map<string, Function>();
public patternCache = new LRUCache({ max: 1000, ttl: 1000 * 60 * 5 }); public patternCache = new LRUCache({ max: 1000, ttl: 1000 * 60 * 5 });
public cueTimes: { [key: string]: number } = {};
private errorTimeoutID: number = 0; private errorTimeoutID: number = 0;
private printTimeoutID: number = 0; private printTimeoutID: number = 0;
public MidiConnection: MidiConnection; public MidiConnection: MidiConnection;
@ -2176,4 +2177,10 @@ export class UserAPI {
*/ */
this.app.clock.time_signature = [numerator, denominator]; this.app.clock.time_signature = [numerator, denominator];
}; };
public cue = (functionName: string|Function): void => {
functionName = typeof functionName === "function" ? functionName.name : functionName;
this.cueTimes[functionName] = this.app.clock.pulses_since_origin;
};
} }

View File

@ -34,8 +34,8 @@ import { ziffers_basics } from "./documentation/patterns/ziffers/ziffers_basics"
import { ziffers_scales } from "./documentation/patterns/ziffers/ziffers_scales"; import { ziffers_scales } from "./documentation/patterns/ziffers/ziffers_scales";
import { ziffers_rhythm } from "./documentation/patterns/ziffers/ziffers_rhythm"; import { ziffers_rhythm } from "./documentation/patterns/ziffers/ziffers_rhythm";
import { ziffers_algorithmic } from "./documentation/patterns/ziffers/ziffers_algorithmic"; import { ziffers_algorithmic } from "./documentation/patterns/ziffers/ziffers_algorithmic";
import { ziffers_tonnetz } from "./documentation/patterns/ziffers/ziffers_tonnetz"; import { ziffers_tonnetz } from "./documentation/patterns/ziffers/ziffers_tonnetz";
import { ziffers_syncing } from "./documentation/patterns/ziffers/ziffers_syncing";
import { synths } from "./documentation/learning/audio_engine/synths"; import { synths } from "./documentation/learning/audio_engine/synths";
@ -102,6 +102,7 @@ export const documentation_factory = (application: Editor) => {
ziffers_algorithmic: ziffers_algorithmic(application), ziffers_algorithmic: ziffers_algorithmic(application),
ziffers_rhythm: ziffers_rhythm(application), ziffers_rhythm: ziffers_rhythm(application),
ziffers_tonnetz: ziffers_tonnetz(application), ziffers_tonnetz: ziffers_tonnetz(application),
ziffers_syncing: ziffers_syncing(application),
midi: midi(application), midi: midi(application),
osc: osc(application), osc: osc(application),
lfos: lfos(application), lfos: lfos(application),

View File

@ -508,6 +508,7 @@ export const installInterfaceLogic = (app: Editor) => {
"ziffers_rhythm", "ziffers_rhythm",
"ziffers_algorithmic", "ziffers_algorithmic",
"ziffers_tonnetz", "ziffers_tonnetz",
"ziffers_syncing",
"midi", "midi",
"osc", "osc",
"functions", "functions",

View File

@ -442,4 +442,9 @@ export abstract class AudibleEvent extends AbstractEvent {
update = (): void => { update = (): void => {
// Overwrite in subclasses // Overwrite in subclasses
}; };
cue = (functionName: string|Function): this => {
this.app.api.cue(functionName);
return this;
}
} }

View File

@ -127,8 +127,9 @@ export class Player extends AbstractEvent {
const patternIsStarting = const patternIsStarting =
this.notStarted() && this.notStarted() &&
(this.pulse() === 0 || this.origin() >= this.nextBeatInTicks()) && this.waitTime >= 0 &&
this.origin() >= this.waitTime; this.origin() >= this.waitTime &&
(this.pulse() === 0 || this.origin() >= this.nextBeatInTicks());
const timeToPlayNext = const timeToPlayNext =
this.current && this.current &&
@ -315,16 +316,30 @@ export class Player extends AbstractEvent {
return this; return this;
} }
wait(value: number | Function) { wait(value: number | string | Function) {
if(typeof value === "string") {
const cueTime = this.app.api.cueTimes[value];
if(cueTime && this.app.clock.pulses_since_origin <= cueTime) {
this.waitTime = cueTime;
} else {
this.waitTime = -1;
}
return this;
}
if (this.atTheBeginning()) { if (this.atTheBeginning()) {
if (typeof value === "function") { if (typeof value === "function") {
const refPat = this.app.api.patternCache.get(value.name) as Player; const refPat = this.app.api.patternCache.get(value.name) as Player;
if (refPat) this.waitTime = refPat.nextEndTime(); if (refPat) this.waitTime = refPat.nextEndTime();
return this; return this;
} } else if(typeof value === "number") {
this.waitTime = this.waitTime =
this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn); this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn);
return this;
} }
}
return this; return this;
} }

View File

@ -4,11 +4,53 @@ import { makeExampleFactory } from "../../../Documentation";
export const ziffers_syncing = (application: Editor): string => { export const ziffers_syncing = (application: Editor): string => {
const makeExample = makeExampleFactory(application); const makeExample = makeExampleFactory(application);
return ` return `
# Synchronization # Synchronization
Ziffers numbered methods **(z0-z16)** can be used to parse and play patterns. Each method is individually cached and can be used to play multiple patterns simultaneously. By default, each Ziffers expression can have a different duration. This system is thus necessary to make everything fit together in a loop-based environment like Topos. Ziffers patterns can be synced to any event using <ic>cue(name: string))</ic> and <ic>wait(name: string)</ic> or by using <ic>sync(name: Function)</ic> and <ic>wait(name: Function)</ic> methods from the ziffers patterns.
Numbered methods are synced automatically to **z0** method if it exsists. Syncing can also be done manually by using either the <ic>wait</ic> method, which will always wait for the current pattern to finish before starting the next cycle, or the <ic>sync</ic> method will only wait for the synced pattern to finish on the first time. ## Sync with cue
The <ic>cue(name: string)</ic> methods can be used to send cue messages for ziffers patterns. The <ic>wait(name: string)</ic> method is used to wait for the cue message to start the pattern.
${makeExample(
"Sending cue from event",
`
beat(4.0) :: sound("bd").cue("foo").out();
z1("q 0 3 e 2 1 2 1").wait("foo").sound("sine").out();
`,
true,
)}
${makeExample(
"Delayed start using individual cue",
`
onbar(3) :: cue("bar")
z1("0 4 2 -2").wait("bar")
.sound("ST40:3").stretch([2,1,3,.1].beat(0.5)).out();
`,
true,
)}
## Sync with beat
Patterns can also be synced using beat and setting the note length of events to zero using **z** duration character or <ic>noteLength(number)</ic> method.
${makeExample(
"Syncing with beat",
`
beat(.5) :: z1("<bd sn:3> hh:5").noteLength(0)
.sound().out()
beat([2.0,0.5,1.5].bar(1)) ::
z2("z _ 0 0 <2 1>").sound("bass:5")
.dur(0.5).out()
`,
true,
)}
## Automatic sync for ziffers patterns
Numbered methods **(z0-z16)** are synced automatically to **z0** method if it exsists. Syncing can also be done manually by using either the <ic>wait</ic> method, which will always wait for the current pattern to finish before starting the next cycle, or the <ic>sync</ic> method will only wait for the synced pattern to finish on the first time.
${makeExample( ${makeExample(
"Automatic sync to z0", "Automatic sync to z0",
@ -19,14 +61,9 @@ export const ziffers_syncing = (application: Editor): string => {
true, true,
)} )}
${makeExample( ## Syncing patterns to each other
"Sync with wait",
` Patterns can also be synced together using the <ic>sync(name: Function)</ic> method. This will sync the pattern to the start of the referenced pattern. Copy this example and first run z1 and then z2 at random position.
z1('w 0 5').sound('pluck').release(0.1).sustain(0.25).out()
z2('q 6 3').wait(z1).sound('sine').release(0.16).sustain(0.55).out()
`,
true,
)}
${makeExample( ${makeExample(
"Sync on first run", "Sync on first run",
@ -37,6 +74,19 @@ export const ziffers_syncing = (application: Editor): string => {
true, true,
)} )}
## Sync with wait
Syncing can also be done using <ic>wait(name: Function)</ic> method. This will wait for the referenced pattern to finish before starting the next cycle.
${makeExample(
"Sync with wait",
`
z1('w 0 5').sound('pluck').release(0.1).sustain(0.25).out()
z2('q 6 3').wait(z1).sound('sine').release(0.16).sustain(0.55).out()
`,
true,
)}
`; `;
}; };

View File

@ -4028,10 +4028,10 @@ yaml@^2.1.1:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
zifferjs@^0.0.50: zifferjs@^0.0.51:
version "0.0.50" version "0.0.51"
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.50.tgz#5e8c31ba3aed00d23f9add3c7d21cfe1dc2b92bf" resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.51.tgz#567efb39a1675fa622a1edc54d671318b58c43c7"
integrity sha512-tXFqu5RfYVK5Epc1evf0OZdeX6hMKcbHSh5ZPO/XFfSBCMnnabAGde3M6eZk3SDcB6vhVt2OyiF91tYi3SSpbw== integrity sha512-0uYFZNsdUL4wOv8x37HLenoEOKmcMi1hVpZIWXQwx9AsTeGvZqgVak0y02MSne5S5dMFmAO5s5ZXokc4kzbCeQ==
zyklus@^0.1.4: zyklus@^0.1.4:
version "0.1.4" version "0.1.4"