diff --git a/index.html b/index.html
index 305b2c1..0449411 100644
--- a/index.html
+++ b/index.html
@@ -196,6 +196,7 @@
Rhythm
Algorithmic
Tonnetz
+ Syncing
diff --git a/package.json b/package.json
index 39000ec..ea7857a 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"tone": "^14.8.49",
"unique-names-generator": "^4.7.1",
"vite-plugin-markdown": "^2.1.0",
- "zifferjs": "^0.0.50",
+ "zifferjs": "^0.0.51",
"zyklus": "^0.1.4",
"zzfx": "^1.2.0"
}
diff --git a/src/API.ts b/src/API.ts
index 957cd9c..423922f 100644
--- a/src/API.ts
+++ b/src/API.ts
@@ -87,6 +87,7 @@ export class UserAPI {
public currentSeed: string | undefined = undefined;
public localSeeds = new Map();
public patternCache = new LRUCache({ max: 1000, ttl: 1000 * 60 * 5 });
+ public cueTimes: { [key: string]: number } = {};
private errorTimeoutID: number = 0;
private printTimeoutID: number = 0;
public MidiConnection: MidiConnection;
@@ -2176,4 +2177,10 @@ export class UserAPI {
*/
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;
+ };
+
}
diff --git a/src/Documentation.ts b/src/Documentation.ts
index cfd6a9d..e7f3c97 100644
--- a/src/Documentation.ts
+++ b/src/Documentation.ts
@@ -34,8 +34,8 @@ import { ziffers_basics } from "./documentation/patterns/ziffers/ziffers_basics"
import { ziffers_scales } from "./documentation/patterns/ziffers/ziffers_scales";
import { ziffers_rhythm } from "./documentation/patterns/ziffers/ziffers_rhythm";
import { ziffers_algorithmic } from "./documentation/patterns/ziffers/ziffers_algorithmic";
-
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";
@@ -102,6 +102,7 @@ export const documentation_factory = (application: Editor) => {
ziffers_algorithmic: ziffers_algorithmic(application),
ziffers_rhythm: ziffers_rhythm(application),
ziffers_tonnetz: ziffers_tonnetz(application),
+ ziffers_syncing: ziffers_syncing(application),
midi: midi(application),
osc: osc(application),
lfos: lfos(application),
diff --git a/src/InterfaceLogic.ts b/src/InterfaceLogic.ts
index 6e4df61..7ba5733 100644
--- a/src/InterfaceLogic.ts
+++ b/src/InterfaceLogic.ts
@@ -508,6 +508,7 @@ export const installInterfaceLogic = (app: Editor) => {
"ziffers_rhythm",
"ziffers_algorithmic",
"ziffers_tonnetz",
+ "ziffers_syncing",
"midi",
"osc",
"functions",
diff --git a/src/classes/AbstractEvents.ts b/src/classes/AbstractEvents.ts
index 8fc2365..6799cbe 100644
--- a/src/classes/AbstractEvents.ts
+++ b/src/classes/AbstractEvents.ts
@@ -442,4 +442,9 @@ export abstract class AudibleEvent extends AbstractEvent {
update = (): void => {
// Overwrite in subclasses
};
+
+ cue = (functionName: string|Function): this => {
+ this.app.api.cue(functionName);
+ return this;
+ }
}
diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts
index 9792bcc..e8bfcec 100644
--- a/src/classes/ZPlayer.ts
+++ b/src/classes/ZPlayer.ts
@@ -127,8 +127,9 @@ export class Player extends AbstractEvent {
const patternIsStarting =
this.notStarted() &&
- (this.pulse() === 0 || this.origin() >= this.nextBeatInTicks()) &&
- this.origin() >= this.waitTime;
+ this.waitTime >= 0 &&
+ this.origin() >= this.waitTime &&
+ (this.pulse() === 0 || this.origin() >= this.nextBeatInTicks());
const timeToPlayNext =
this.current &&
@@ -315,16 +316,30 @@ export class Player extends AbstractEvent {
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 (typeof value === "function") {
const refPat = this.app.api.patternCache.get(value.name) as Player;
if (refPat) this.waitTime = refPat.nextEndTime();
return this;
+ } else if(typeof value === "number") {
+ this.waitTime =
+ this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn);
+ return this;
}
- this.waitTime =
- this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn);
}
+
return this;
}
diff --git a/src/documentation/patterns/ziffers/ziffers_syncing.ts b/src/documentation/patterns/ziffers/ziffers_syncing.ts
index 84fb8d3..933052c 100644
--- a/src/documentation/patterns/ziffers/ziffers_syncing.ts
+++ b/src/documentation/patterns/ziffers/ziffers_syncing.ts
@@ -4,11 +4,53 @@ import { makeExampleFactory } from "../../../Documentation";
export const ziffers_syncing = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
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 cue(name: string)) and wait(name: string) or by using sync(name: Function) and wait(name: Function) methods from the ziffers patterns.
+
+## Sync with cue
+
+The cue(name: string) methods can be used to send cue messages for ziffers patterns. The wait(name: string) 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 noteLength(number) method.
+
+${makeExample(
+ "Syncing with beat",
+ `
+beat(.5) :: z1(" hh:5").noteLength(0)
+ .sound().out()
- Numbered methods are synced automatically to **z0** method if it exsists. Syncing can also be done manually by using either the wait method, which will always wait for the current pattern to finish before starting the next cycle, or the sync method will only wait for the synced pattern to finish on the first time.
+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 wait method, which will always wait for the current pattern to finish before starting the next cycle, or the sync method will only wait for the synced pattern to finish on the first time.
${makeExample(
"Automatic sync to z0",
@@ -18,6 +60,23 @@ export const ziffers_syncing = (application: Editor): string => {
`,
true,
)}
+
+## Syncing patterns to each other
+
+ Patterns can also be synced together using the sync(name: Function) 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.
+
+ ${makeExample(
+ "Sync on first run",
+ `
+ z1('w __ 0 5 9 3').sound('bin').out()
+ z2('q __ 4 2 e 6 3 q 6').sync(z1).sound('east').out()
+ `,
+ true,
+ )}
+
+## Sync with wait
+
+ Syncing can also be done using wait(name: Function) method. This will wait for the referenced pattern to finish before starting the next cycle.
${makeExample(
"Sync with wait",
@@ -27,15 +86,6 @@ export const ziffers_syncing = (application: Editor): string => {
`,
true,
)}
-
- ${makeExample(
- "Sync on first run",
- `
- z1('w __ 0 5 9 3').sound('bin').out()
- z2('q __ 4 2 e 6 3 q 6').sync(z1).sound('east').out()
- `,
- true,
- )}
`;
};
diff --git a/yarn.lock b/yarn.lock
index c1c3280..d17fe68 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4028,10 +4028,10 @@ yaml@^2.1.1:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
-zifferjs@^0.0.50:
- version "0.0.50"
- resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.50.tgz#5e8c31ba3aed00d23f9add3c7d21cfe1dc2b92bf"
- integrity sha512-tXFqu5RfYVK5Epc1evf0OZdeX6hMKcbHSh5ZPO/XFfSBCMnnabAGde3M6eZk3SDcB6vhVt2OyiF91tYi3SSpbw==
+zifferjs@^0.0.51:
+ version "0.0.51"
+ resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.51.tgz#567efb39a1675fa622a1edc54d671318b58c43c7"
+ integrity sha512-0uYFZNsdUL4wOv8x37HLenoEOKmcMi1hVpZIWXQwx9AsTeGvZqgVak0y02MSne5S5dMFmAO5s5ZXokc4kzbCeQ==
zyklus@^0.1.4:
version "0.1.4"