From 1a95fb13390fbbcd9be20d3a31d2030839b5b0ec Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Thu, 31 Aug 2023 21:28:10 +0300 Subject: [PATCH 1/4] Added oneuclid for testing --- src/API.ts | 13 +++++++++++++ src/ArrayExtensions.ts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/API.ts b/src/API.ts index b728ef8..380f1e2 100644 --- a/src/API.ts +++ b/src/API.ts @@ -1017,6 +1017,19 @@ export class UserAPI { return final_pulses.some((p) => p == true); }; + oneuclid = (pulses: number, length: number, rotate: number=0): boolean => { + /** + * Returns true if the current beat is in the given euclid sequence. + * @param pulses - The number of pulses in the cycle + * @param length - The length of the cycle + * @param rotate - Rotation of the euclidian sequence + * @returns True if the current beat is in the given euclid sequence + */ + const cycle = this._euclidean_cycle(pulses, length, rotate); + const beats = cycle.reduce((acc:number[], x:boolean, i:number) => { if(x) acc.push(i+1); return acc; }, []); + return this.oncount(beats, length); + }; + // ====================================================================== // Delay related functions // ====================================================================== diff --git a/src/ArrayExtensions.ts b/src/ArrayExtensions.ts index 3628fd0..905b188 100644 --- a/src/ArrayExtensions.ts +++ b/src/ArrayExtensions.ts @@ -121,7 +121,7 @@ export const makeArrayExtensions = (api: UserAPI) => { Array.prototype.div = function (divisor: number) { const chunk_size = divisor; // Get the first argument (chunk size) - const timepos = api.epulse(); + const timepos = api.app.clock.pulses_since_origin; const slice_count = Math.floor( timepos / Math.floor(chunk_size * api.ppqn()) ); From 109d7283b8b5b5ebdade684c422b1afbe7016fcd Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Thu, 31 Aug 2023 22:37:20 +0300 Subject: [PATCH 2/4] Adding back removed modify function --- src/classes/SoundEvent.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/classes/SoundEvent.ts b/src/classes/SoundEvent.ts index c726b96..b4222a6 100644 --- a/src/classes/SoundEvent.ts +++ b/src/classes/SoundEvent.ts @@ -133,6 +133,20 @@ export class SoundEvent extends AudibleEvent { velocity = (value: number) => this.updateValue("velocity", value); vel = this.velocity; + // ================================================================================ + // AbstactEvent overrides + // ================================================================================ + + modify = (func: Function): this => { + const funcResult = func(this); + if (funcResult instanceof Object) return funcResult; + else { + func(this.values); + this.update(); + return this; + } + }; + update = (): void => { const [note, _] = noteFromPc( this.values.key || "C4", From 97f6c219931ec072d210f8b4bb54b3ea02dc99db Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Fri, 1 Sep 2023 00:13:26 +0300 Subject: [PATCH 3/4] Added sync and wait --- src/API.ts | 5 +++++ src/classes/AbstractEvents.ts | 4 ++-- src/classes/RestEvent.ts | 10 +++++----- src/classes/ZPlayer.ts | 24 +++++++++++++++++++++--- src/documentation/ziffers.ts | 35 +++++++++++++++++++++++++++++++++-- 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/API.ts b/src/API.ts index cd94a7c..6de4545 100644 --- a/src/API.ts +++ b/src/API.ts @@ -383,6 +383,11 @@ export class UserAPI { player.updateLastCallTime(); + if(id !== "") { + // Sync named patterns to z0 by default + player.sync("z0"); + } + return player; }; diff --git a/src/classes/AbstractEvents.ts b/src/classes/AbstractEvents.ts index 9c457d2..58417f9 100644 --- a/src/classes/AbstractEvents.ts +++ b/src/classes/AbstractEvents.ts @@ -74,8 +74,8 @@ export abstract class Event { return this.modify(func); }; - duration = (value: number): Event => { - this.values["duration"] = value; + length = (value: number): Event => { + this.values["length"] = value; return this; }; } diff --git a/src/classes/RestEvent.ts b/src/classes/RestEvent.ts index 9d52711..4f4ec53 100644 --- a/src/classes/RestEvent.ts +++ b/src/classes/RestEvent.ts @@ -2,20 +2,20 @@ import { type Editor } from "../main"; import { Event } from "./AbstractEvents"; export class RestEvent extends Event { - constructor(duration: number, app: Editor) { + constructor(length: number, app: Editor) { super(app); - this.values["duration"] = duration; + this.values["length"] = length; } _fallbackMethod = (): Event => { - return RestEvent.createRestProxy(this.values["duration"], this.app); + return RestEvent.createRestProxy(this.values["length"], this.app); }; public static createRestProxy = ( - duration: number, + length: number, app: Editor ): RestEvent => { - const instance = new RestEvent(duration, app); + const instance = new RestEvent(length, app); return new Proxy(instance, { // @ts-ignore get(target, propKey, receiver) { diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts index 248ba50..fc573aa 100644 --- a/src/classes/ZPlayer.ts +++ b/src/classes/ZPlayer.ts @@ -11,9 +11,9 @@ export type InputOptions = { [key: string]: string | number }; export class Player extends Event { input: string; ziffers: Ziffers; - initCallTime: number = 1; - startCallTime: number = 1; - lastCallTime: number = 1; + initCallTime: number = 0; + startCallTime: number = 0; + lastCallTime: number = 0; waitTime = 0; startBeat: number = 0; played: boolean = false; @@ -98,6 +98,7 @@ export class Player extends Event { this.startCallTime = 0; this.index = 0; this.waitTime = 0; + this.skipIndex = 0; } // Main logic @@ -207,6 +208,23 @@ export class Player extends Event { return this; } + sync(value: string|Function) { + if(this.atTheBeginning() && this.notStarted()) { + const origin = this.app.clock.pulses_since_origin; + const syncId = typeof value === "function" ? value.name : value; + if(origin>0) { + const syncPattern = this.app.api.patternCache.get(syncId) as Player; + if(syncPattern) { + const syncPatternDuration = syncPattern.ziffers.duration; + const syncPatternStart = syncPattern.startCallTime; + const syncInPulses = syncPatternDuration*4*this.app.clock.ppqn; + this.waitTime = syncPatternStart + syncInPulses; + } + } + } + return this; + } + out = (): void => { // TODO? }; diff --git a/src/documentation/ziffers.ts b/src/documentation/ziffers.ts index c344070..e4d492e 100644 --- a/src/documentation/ziffers.ts +++ b/src/documentation/ziffers.ts @@ -86,7 +86,7 @@ Ziffers provides shorthands for **many** numeric and algorithimic operations suc * **List operations:** Cartesian operation (_e.g._ (3 2 1)+(2 5)) using the + operator. All the arithmetic operators are supported. ${makeExample( - "Cartesian operation for melodic generation", + "Element-wise operations for melodic generation", ` z1("q 0 s (3 2 1)+(2 5) q 0 s (4 5 6)-(2 3)").sound('sine') .scale('minor').fmi(2).fmh(2).room(0.5).size(0.5).sustain(0.1) @@ -192,7 +192,38 @@ mod(1, 1.75) :: snd(['kick', 'hat'].div(1)).out() ## 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. They can be synchronized together by using a **cue** system. By default, each Ziffers expression will have a different duration. This system is thus necessary to make everything fit together in a loop-based environment like Topos. +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. + +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. + +${makeExample( + "Automatic sync to z0", + ` +z0('w 0 8').sound('peri').gain(3.0).out() +z1('e 0 4 5 9').sound('bell').gain(6.0).out() +`, + true +)} + +${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 +)} + +${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 +)} + + ## Examples From 09951732c3aebc9ea5af28fb3ab924b1692d8a55 Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Fri, 1 Sep 2023 00:28:25 +0300 Subject: [PATCH 4/4] Documentation updates --- src/documentation/time.ts | 13 +++++++++++++ src/documentation/ziffers.ts | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/documentation/time.ts b/src/documentation/time.ts index 57e8fdf..a0c5ae4 100644 --- a/src/documentation/time.ts +++ b/src/documentation/time.ts @@ -152,6 +152,19 @@ mod(.25) && euclid($(4), 7, 9, 1) && snd('hh').out() false )} +Alternatively, you can oneuclid or rhythm without the _iterators_: + +- oneuclid(pulses: number, length: number, rotate: number): boolean: generates true or false values from an euclidian rhythm sequence. This is another version of euclid that does not take an iterator. +${makeExample( + "Using oneuclid to create a rhythm without iterators", + ` + // Change speed using bpm + // bpm(250) + oneuclid(5, 9) :: snd('kick').out() + oneuclid(7,16) :: snd('east').end(0.5).n(irand(3,5)).out() +`, + false +)} - rhythm(divisor: number, pulses: number, length: number, rotate: number): boolean: generates true or false values from an euclidian rhythm sequence. This is another version of euclid that does not take an iterator. ${makeExample( diff --git a/src/documentation/ziffers.ts b/src/documentation/ziffers.ts index e4d492e..7210b28 100644 --- a/src/documentation/ziffers.ts +++ b/src/documentation/ziffers.ts @@ -199,8 +199,8 @@ Numbered methods are synced automatically to **z0** method if it exsists. Syncin ${makeExample( "Automatic sync to z0", ` -z0('w 0 8').sound('peri').gain(3.0).out() -z1('e 0 4 5 9').sound('bell').gain(6.0).out() +z0('w 0 8').sound('peri').out() +z1('e 0 4 5 9').sound('bell').out() `, true )}