diff --git a/src/StringExtensions.ts b/src/StringExtensions.ts index b1a3261..ba85df0 100644 --- a/src/StringExtensions.ts +++ b/src/StringExtensions.ts @@ -1,3 +1,4 @@ +import { noteNameToMidi } from "zifferjs"; import { type UserAPI } from "./API"; import { Player } from "./classes/ZPlayer"; export {}; @@ -30,6 +31,7 @@ declare global { z14(): Player; z15(): Player; z16(): Player; + note(): number; } } @@ -79,7 +81,7 @@ export const makeStringExtensions = (api: UserAPI) => { String.prototype.z = function (options: {[key: string]: any} = {}) { return api.z(this.valueOf(), options); }; - + String.prototype.z0 = function (options: {[key: string]: any} = {}) { return api.z0(this.valueOf(), options); }; @@ -148,6 +150,13 @@ export const makeStringExtensions = (api: UserAPI) => { return api.z16(this.valueOf(), options); }; + String.prototype.note = function () { + try { + return parseInt(this.valueOf()); + } catch (e) { + return noteNameToMidi(this.valueOf()); + } + }; } type SpeechOptions = { diff --git a/src/classes/SoundEvent.ts b/src/classes/SoundEvent.ts index ccacdba..60ed0d9 100644 --- a/src/classes/SoundEvent.ts +++ b/src/classes/SoundEvent.ts @@ -1,6 +1,6 @@ import { type Editor } from "../main"; import { AudibleEvent } from "./AbstractEvents"; -import { midiToFreq, noteFromPc } from "zifferjs"; +import { chord as parseChord, midiToFreq, noteFromPc, noteNameToMidi } from "zifferjs"; import { superdough, @@ -230,13 +230,42 @@ export class SoundEvent extends AudibleEvent { // Frequency management public sound = (value: string) => this.updateValue("s", value); - public chord = (value: object[]) => this.updateValue("chord", value); + public chord = (value: string|object[]|number[]|number,...kwargs: number[]) => { + if(typeof value === "string") { + const chord = parseChord(value); + value = chord.map((note: number) => { return {note: note, freq: midiToFreq(note) } }); + } else if(value instanceof Array && typeof value[0] === "number") { + value = (value as number[]).map((note: number) => { return {note: note, freq: midiToFreq(note) } }); + } else if (typeof value === "number" && kwargs.length > 0) { + value = [value, ...kwargs].map((note: number) => { return {note: note, freq: midiToFreq(note) } }); + } + return this.updateValue("chord", value); + } + public invert = (howMany: number = 0) => { + if(this.values.chord) { + let notes = this.values.chord.map((obj: { [key: string]: number }) => obj.note); + notes = howMany < 0 ? [...notes].reverse() : notes; + for (let i = 0; i < Math.abs(howMany); i++) { + notes[i % notes.length] += howMany <= 0 ? -12 : 12; + } + const chord = notes.map((note: number) => { return {note: note, freq: midiToFreq(note) } }); + return this.updateValue("chord", chord); + } else { + return this; + } + } public snd = this.sound; public nudge = (value: number) => this.updateValue("nudge", value); public cut = (value: number) => this.updateValue("cut", value); public clip = (value: number) => this.updateValue("clip", value); public n = (value: number) => this.updateValue("n", value); - public note = (value: number) => this.updateValue("note", value); + public note = (value: number|string) => { + if(typeof value === "string") { + return this.updateValue("note", noteNameToMidi(value)); + } else { + return this.updateValue("note", value); + } + }; public speed = (value: number) => this.updateValue("speed", value); public spd = this.speed; diff --git a/src/documentation/synths.ts b/src/documentation/synths.ts index 63c0702..88c6241 100644 --- a/src/documentation/synths.ts +++ b/src/documentation/synths.ts @@ -22,16 +22,43 @@ beat(.5) && snd(['sine', 'triangle', 'sawtooth', 'square'].beat()).freq(100).out Two functions are primarily used to control the frequency of the synthesizer: - freq(hz: number): sets the frequency of the oscillator. -- note(note: number): sets the MIDI note of the oscillator (MIDI note converted to hertz). +- note(note: number|string): sets the MIDI note of the oscillator (MIDI note converted to hertz). ${makeExample( - "Selecting a pitch or note", + "Selecting a pitch", ` beat(.5) && snd('triangle').freq([100,200,400].beat(2)).out() `, true )} + ${makeExample( + "Selecting a note", + ` +beat(.5) && snd('triangle').note([60,"F4"].pick()).out() +`, + true + )} + +Chords can also played using different parameters: +-chord(string||number[]|...number): parses and sets notes for the chord + +${makeExample( + "Playing a named chord", + ` + beat(1) && snd('triangle').chord(["C","Em7","Fmaj7","Emin"].beat(2)).out() +`, + true + )} + +${makeExample( + "Playing a chord from a list of notes and doing inversions", + ` + beat(.5) && snd('triangle').chord(60,64,67,72).invert([1,-3,4,-5].pick()).out() +`, + true + )} + ## Vibrato You can also add some amount of vibrato to the sound using the vib and vibmod methods. These can turn any oscillator into something more lively and/or into a sound effect when used with a high amount of modulation.