From 4913dde4a194576881a1579efc1529f6485237ef Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Mon, 25 Dec 2023 18:06:32 +0200 Subject: [PATCH] Added new immediate mode for Ziffers evaluation using Ctrl+Shift+Enter. --- package.json | 2 +- src/API.ts | 25 +++++++++++++------ src/classes/ZPlayer.ts | 21 +++++++++++++--- .../patterns/ziffers/ziffers_algorithmic.ts | 1 + .../patterns/ziffers/ziffers_basics.ts | 4 +++ yarn.lock | 8 +++--- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3fb6780..c80c19f 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "tone": "^14.8.49", "unique-names-generator": "^4.7.1", "vite-plugin-markdown": "^2.1.0", - "zifferjs": "^0.0.58", + "zifferjs": "^0.0.59", "zyklus": "^0.1.4", "zzfx": "^1.2.0" } diff --git a/src/API.ts b/src/API.ts index 067824e..ad44b95 100644 --- a/src/API.ts +++ b/src/API.ts @@ -800,18 +800,27 @@ export class UserAPI { if (typeof input === "string" && player.input !== input && - player.atTheBeginning()) { - replace = true; + (player.atTheBeginning() || this.forceEvaluator)) { + replace = true; } } if ((typeof input !== "string" || validSyntax) && (!player || replace)) { - const newPlayer = new Player(input, options, this.app, zid); - if (newPlayer.isValid()) { - player = newPlayer - this.patternCache.set(key, player); - } else if (typeof input === "string") { - this.invalidPatterns[input] = true; + if(typeof input === "string" && player && this.forceEvaluator) { + // If pattern change is forced in the middle of the cycle + if(!player.updatePattern(input, options)) { + this.logOnce(`Invalid syntax: ${input}`); + }; + this.forceEvaluator = false; + } else { + // If pattern is not in cache or is to be replaced + const newPlayer = player ? new Player(input, options, this.app, zid, player.nextEndTime()) : new Player(input, options, this.app, zid); + if (newPlayer.isValid()) { + player = newPlayer + this.patternCache.set(key, player); + } else if (typeof input === "string") { + this.invalidPatterns[input] = true; + } } } diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts index 710e45c..9ff4d0e 100644 --- a/src/classes/ZPlayer.ts +++ b/src/classes/ZPlayer.ts @@ -7,6 +7,7 @@ import { MidiEvent, MidiParams } from "./MidiEvent"; import { RestEvent } from "./RestEvent"; import { arrayOfObjectsToObjectWithArrays, isGenerator } from "../Utils/Generic"; import { TonnetzSpaces } from "zifferjs/src/tonnetz"; +import { safeMod } from "zifferjs/src/utils"; export type InputOptions = { [key: string]: string | number }; @@ -31,6 +32,7 @@ export class Player extends AbstractEvent { options: InputOptions, public app: Editor, zid: string = "", + waitTime: number = 0, ) { super(app); this.options = options; @@ -46,9 +48,24 @@ export class Player extends AbstractEvent { } else { throw new Error("Invalid input"); } + if(waitTime) this.waitTime = waitTime; this.zid = zid; } + updatePattern(input: string, options: InputOptions): boolean { + const oldIndex = this.ziffers.index; + const newPattern = new Ziffers(input, options); + if(newPattern.values.length > 0) { + this.ziffers = newPattern; + this.ziffers.update(); + this.ziffers.index = oldIndex; + this.input = input; + this.options = options; + return true; + } + return false; + } + isValid() { return this.ziffers.values.length > 0; } @@ -417,10 +434,8 @@ export class Player extends AbstractEvent { } rotate(amount: number = 1) { - // TODO: Only works for evaluated patterns (setRedo). Fix this for generative patterns. Mod by current cycle? if (this.atTheBeginning()) { - this.ziffers.setRedo(0); - this.ziffers.rotate(amount); + this.ziffers.rotate(amount+safeMod(this.ziffers.cycleIndex,this.ziffers.evaluated.length)); } return this; } diff --git a/src/documentation/patterns/ziffers/ziffers_algorithmic.ts b/src/documentation/patterns/ziffers/ziffers_algorithmic.ts index c7560d0..a1559ba 100644 --- a/src/documentation/patterns/ziffers/ziffers_algorithmic.ts +++ b/src/documentation/patterns/ziffers/ziffers_algorithmic.ts @@ -85,6 +85,7 @@ z1("s A=(0 (1,4)) B~(2 (3,8)) A B A B A") * deal(amount: number): Shuffle the generated pattern and deal given number of elements * retrograde() Reverse the generated pattern * invert() Invert the generated pattern + * rotate(amount: number) Rotate the generated pattern by given amount * between(start: number, end: number) Select a range of elements from the generated pattern * from(start: number) Select a range of elements from the start index to the end of the pattern * to(end: number) Select a range of elements from the beginning of the pattern to the end index diff --git a/src/documentation/patterns/ziffers/ziffers_basics.ts b/src/documentation/patterns/ziffers/ziffers_basics.ts index 21bd6ab..b56bdb2 100644 --- a/src/documentation/patterns/ziffers/ziffers_basics.ts +++ b/src/documentation/patterns/ziffers/ziffers_basics.ts @@ -32,6 +32,10 @@ z4('1/4 kick kick snare kick').sound().gain(1).cutoff(osci).out() true, )} +## Evaluation + +Evaluation of live coded Ziffers patterns can be done in 3 different ways. Normal evaluation using Ctrl+Enter updates the pattern after the current cycle is finished. Evaluation using Ctrl+Shift+Enter updates the pattern immediately keeping the current position, which enables to modify future events even within the current cycle. Evaluation using Ctrl+Shift+Backspace resets the current pattern and starts from the beginning immediately. + ## Notation The basic Ziffer notation is entirely written in JavaScript strings (_e.g_ "0 1 2"). It consists mostly of numbers and letters. The whitespace character is used as a separator. Instead of note names, Ziffer is using numbers to represent musical pitch and letters to represent musical durations. Alternatively, _floating point numbers_ can also be used to represent durations. diff --git a/yarn.lock b/yarn.lock index 654a735..e327bd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4033,10 +4033,10 @@ yaml@^2.1.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== -zifferjs@^0.0.58: - version "0.0.58" - resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.58.tgz#206f52479807c6c965b77ec8ff9961847a7f6108" - integrity sha512-7CTrQkJrlVqmq5CzGK2OAQ0Fof/yLcloRBNPsprZbPQGE7bEUtupnvpFFFKFtt77pUzzbxUG56zyZsjj/HENlA== +zifferjs@^0.0.59: + version "0.0.59" + resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.59.tgz#e18ad4f967f98092f6782abbe0a38c5a148c2bac" + integrity sha512-bssE9Vtgmcoz2d6390pnX6YWQxsewtXi+3HWdNAmzoM+0bupOGOvpGat79fQ5FqXhV5jjrZl2JzDzLCfcwnM5w== zyklus@^0.1.4: version "0.1.4"