From 818e1a62ef857c4e168732561c9649984838311a Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Tue, 12 Dec 2023 23:54:42 +0200 Subject: [PATCH] Added and documented new scale methods: semitones, cents and ratios --- package.json | 2 +- src/classes/AbstractEvents.ts | 31 +++++++++++++++++++++ src/classes/ZPlayer.ts | 20 +++++++++++++- src/documentation/patterns/patterns.ts | 38 ++++++++++++++++++++++++-- yarn.lock | 8 +++--- 5 files changed, 91 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index ea7857a..c1bc928 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.51", + "zifferjs": "^0.0.53", "zyklus": "^0.1.4", "zzfx": "^1.2.0" } diff --git a/src/classes/AbstractEvents.ts b/src/classes/AbstractEvents.ts index 6799cbe..2cdb769 100644 --- a/src/classes/AbstractEvents.ts +++ b/src/classes/AbstractEvents.ts @@ -8,6 +8,7 @@ import { } from "zifferjs"; import { SkipEvent } from "./SkipEvent"; import { SoundParams } from "./SoundEvent"; +import { centsToSemitones, ratiosToSemitones } from "zifferjs/src/scale"; export type EventOperation = (instance: T, ...args: any[]) => void; @@ -360,6 +361,36 @@ export abstract class AudibleEvent extends AbstractEvent { return this; }; + updateKeyAndPitch() { + if (!this.values.key) this.values.key = 60; + if (!(this.values.pitch || this.values.pitch === 0)) this.values.pitch = 0; + } + + semitones(values: number|number[], ...rest: number[]) { + const scaleValues = typeof values === "number" ? [values, ...rest] : values; + this.values.parsedScale = safeScale(scaleValues); + this.updateKeyAndPitch(); + this.update(); + return this; + } + steps = this.semitones; + + cents(values: number|number[], ...rest: number[]) { + const scaleValues = typeof values === "number" ? [values, ...rest] : values; + this.values.parsedScale = safeScale(centsToSemitones(scaleValues)); + this.updateKeyAndPitch(); + this.update(); + return this; + } + + ratios(values: number|number[], ...rest: number[]) { + const scaleValues = typeof values === "number" ? [values, ...rest] : values; + this.values.parsedScale = safeScale(ratiosToSemitones(scaleValues)); + this.updateKeyAndPitch(); + this.update(); + return this; + } + protected updateValue(key: string, value: T | T[] | null): this { if (value == null) return this; this.values[key] = value; diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts index 2281ff7..1ae14e8 100644 --- a/src/classes/ZPlayer.ts +++ b/src/classes/ZPlayer.ts @@ -258,11 +258,29 @@ export class Player extends AbstractEvent { } } - scale(name: string) { + scale(name: string|number[]) { if (this.atTheBeginning()) this.ziffers.scale(name); return this; } + semitones(values: number|number[], ...rest: number[]) { + values = typeof values === "number" ? [values, ...rest] : values; + if (this.atTheBeginning()) this.ziffers.semitones(values); + return this; + } + + cents(values: number|number[], ...rest: number[]) { + values = typeof values === "number" ? [values, ...rest] : values; + if (this.atTheBeginning()) this.ziffers.cents(values); + return this; + } + + ratios(values: number|number[], ...rest: number[]) { + values = typeof values === "number" ? [values, ...rest] : values; + if (this.atTheBeginning()) this.ziffers.ratios(values); + return this; + } + key(name: string) { if (this.atTheBeginning()) this.ziffers.key(name); return this; diff --git a/src/documentation/patterns/patterns.ts b/src/documentation/patterns/patterns.ts index 9aeb618..5e8a986 100644 --- a/src/documentation/patterns/patterns.ts +++ b/src/documentation/patterns/patterns.ts @@ -122,7 +122,6 @@ beat(1)::sound(['kick', 'fsnare'].dur(3, 1)) ## Manipulating notes and scales - - pitch(): convert a list of integers to pitch classes ${makeExample( @@ -136,7 +135,42 @@ beat(0.25) :: snd('sine') true, )} - - scale(scale: string, base note: number): Map each element of the list to the closest note of the slected scale. [0, 2, 3, 5 ].scale("major", 50) returns [50, 52, 54, 55]. You can use western scale names like (Major, Minor, Minor pentatonic ...) or [zeitler](https://ianring.com/musictheory/scales/traditions/zeitler) scale names. Alternatively you can also use the integers as used by Ian Ring in his [study of scales](https://ianring.com/musictheory/scales/). +- semitones(number[], ...args?): Create scale from semitone intervals. + +${makeExample( + "Play pitches from scale created from semitone intervals", + ` + beat(1) :: sound('gtr').pitch([0, 4, 3, 2].beat()).key(64) + .semitones(1, 1, 3, 1, 1, 2, 3).out() +`, + true, +)} + +- cents(number[], ...args?): Create scale from cent intervals. + +${makeExample( + "Play pitches from scale created from cent intervals", + ` + rhythm([0.5,0.25].beat(1),14,16) :: sound('pluck') + .stretch(r(1,5)).pitch(r(0,6)).key(57) +.cents(120,270,540,670,785,950,1215).out() +`, + true, +)} + +- ratios(number[], ...args?): Create scale from ratios. + +${makeExample( + "Play pitches from scale created from ratios", + ` + rhythm([0.5,0.25].beat(0.25),5,7) :: sound('east:3') + .pitch([0,1,2,3,4,5,6,7,8,9,10,11].beat(0.25)).key(67) +.ratios(2/11,4/11,6/11,8/11,10/11,11/11).out() +`, + true, +)} + +- scale(scale: string, base note: number): Map each element of the list to the closest note of the slected scale. [0, 2, 3, 5 ].scale("major", 50) returns [50, 52, 54, 55]. You can use western scale names like (Major, Minor, Minor pentatonic ...) or [zeitler](https://ianring.com/musictheory/scales/traditions/zeitler) scale names. Alternatively you can also use the integers as used by Ian Ring in his [study of scales](https://ianring.com/musictheory/scales/). ${makeExample( "Mapping the note array to the E3 major scale", diff --git a/yarn.lock b/yarn.lock index d17fe68..4a887a2 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.51: - version "0.0.51" - resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.51.tgz#567efb39a1675fa622a1edc54d671318b58c43c7" - integrity sha512-0uYFZNsdUL4wOv8x37HLenoEOKmcMi1hVpZIWXQwx9AsTeGvZqgVak0y02MSne5S5dMFmAO5s5ZXokc4kzbCeQ== +zifferjs@^0.0.53: + version "0.0.53" + resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.53.tgz#5e6e8a80ef90cd59ed607ab7cf02592efc03b08d" + integrity sha512-XGREU1ClhaatEZaek6gTBcwaNfFkUZLENWhXILPjLCODXzFoC6D/qaYCaIegcJdGgqB7At3DqID4yNwFLRPeqQ== zyklus@^0.1.4: version "0.1.4"