From f526fc943a90ac8180af5e70097c32eab300fcd7 Mon Sep 17 00:00:00 2001 From: danigb Date: Sat, 7 Sep 2024 18:19:03 +0200 Subject: [PATCH 1/5] feat: arpeggiator --- packages/arp/README.md | 5 ++ packages/arp/package.json | 35 ++++++++++ packages/arp/src/_worklet.ts | 119 ++++++++++++++++++++++++++++++++++ packages/arp/src/dsp.ts | 73 +++++++++++++++++++++ packages/arp/src/index.ts | 34 ++++++++++ packages/arp/src/processor.ts | 1 + packages/arp/src/worklet.ts | 50 ++++++++++++++ 7 files changed, 317 insertions(+) create mode 100644 packages/arp/README.md create mode 100644 packages/arp/package.json create mode 100644 packages/arp/src/_worklet.ts create mode 100644 packages/arp/src/dsp.ts create mode 100644 packages/arp/src/index.ts create mode 100644 packages/arp/src/processor.ts create mode 100644 packages/arp/src/worklet.ts diff --git a/packages/arp/README.md b/packages/arp/README.md new file mode 100644 index 0000000..c625a8e --- /dev/null +++ b/packages/arp/README.md @@ -0,0 +1,5 @@ +# @synthlet/arp + +> An arpeggiator audio worklet + +Part of [Synthlet](https://github.com/danigb/synthlet) diff --git a/packages/arp/package.json b/packages/arp/package.json new file mode 100644 index 0000000..0d07aca --- /dev/null +++ b/packages/arp/package.json @@ -0,0 +1,35 @@ +{ + "name": "@synthlet/arp", + "version": "0.1.0", + "description": "Arpeggiator audio worklet", + "keywords": [ + "arpeggiator", + "modular", + "synthesis", + "synthlet" + ], + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/jest": "^29.5.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.1" + }, + "jest": { + "preset": "ts-jest" + }, + "scripts": { + "worklet": "esbuild src/worklet.ts --bundle --minify | sed -e 's/^/export const PROCESSOR = \\`/' -e 's/$/\\`;/' > src/processor.ts", + "lib": "tsup src/index.ts --sourcemap --dts --format esm,cjs", + "build": "npm run worklet && npm run lib" + } +} diff --git a/packages/arp/src/_worklet.ts b/packages/arp/src/_worklet.ts new file mode 100644 index 0000000..9a26891 --- /dev/null +++ b/packages/arp/src/_worklet.ts @@ -0,0 +1,119 @@ +// DON'T EDIT THIS FILE unless inside scripts/_worklet.ts +// use ./scripts/copy_files.ts to copy this file to the right place +// the goal is to avoid external dependencies on packages + +// A "Connector" is a function that takes an AudioContext and returns an AudioNode +// or an custom object with a connect method (that returns a disconnect method) +export type Connector = (context: AudioContext) => N; + +export type ParamInput = number | Connector | AudioNode; + +type CreateWorkletOptions = { + processorName: string; + paramNames: readonly string[]; + workletOptions: (params: Partial

) => AudioWorkletNodeOptions; + postCreate?: (node: N) => void; +}; + +export type Disposable = N & { dispose: () => void }; + +export function createWorkletConstructor< + N extends AudioWorkletNode, + P extends Record +>(options: CreateWorkletOptions) { + return ( + audioContext: AudioContext, + inputs: Partial

= {} + ): Disposable => { + const node = new AudioWorkletNode( + audioContext, + options.processorName, + options.workletOptions(inputs) + ) as N; + + (node as any).__PROCESSOR_NAME__ = options.processorName; + const connected = connectParams(node, options.paramNames, inputs); + options.postCreate?.(node); + return disposable(node, connected); + }; +} + +type ConnectedUnit = AudioNode | (() => void); + +export function connectParams( + node: any, + paramNames: readonly string[], + inputs: any +): ConnectedUnit[] { + const connected: ConnectedUnit[] = []; + + for (const paramName of paramNames) { + if (node.parameters) { + node[paramName] = node.parameters.get(paramName); + } + const param = node[paramName]; + if (!param) throw Error("Invalid param name: " + paramName); + const input = inputs[paramName]; + if (typeof input === "number") { + param.value = input; + } else if (input instanceof AudioNode) { + param.value = 0; + input.connect(param); + connected.push(input); + } else if (typeof input === "function") { + param.value = 0; + const source = input(node.context); + source.connect(param); + connected.push(source); + } + } + + return connected; +} + +export function disposable( + node: N, + dependencies?: ConnectedUnit[] +): Disposable { + let disposed = false; + return Object.assign(node, { + dispose() { + if (disposed) return; + disposed = true; + + node.disconnect(); + (node as any).port?.postMessage({ type: "DISPOSE" }); + if (!dependencies) return; + + while (dependencies.length) { + const conn = dependencies.pop(); + if (conn instanceof AudioNode) { + if (typeof (conn as any).dispose === "function") { + (conn as any).dispose?.(); + } else { + conn.disconnect(); + } + } else if (typeof conn === "function") { + conn(); + } + } + }, + }); +} + +export function createRegistrar(processorName: string, processor: string) { + return function (context: AudioContext): Promise { + const key = "__" + processorName + "__"; + if (key in context) return (context as any)[key]; + + if (!context.audioWorklet || !context.audioWorklet.addModule) { + throw Error("AudioWorklet not supported"); + } + + const blob = new Blob([processor], { type: "application/javascript" }); + const url = URL.createObjectURL(blob); + const promise = context.audioWorklet.addModule(url); + (context as any)[key] = promise; + return promise; + }; +} diff --git a/packages/arp/src/dsp.ts b/packages/arp/src/dsp.ts new file mode 100644 index 0000000..a83e5e5 --- /dev/null +++ b/packages/arp/src/dsp.ts @@ -0,0 +1,73 @@ +export enum ArpType { + Random = 0, +} + +export enum ArpChord { + Major = 145, +} + +export function createArpeggiator() { + let $note = 60; + let $chord = 0; + let $octaves = 1; + + let chordNotes = [0]; + let position = 0; + let len = 1; + let active = false; + let current = $note; + + return function update( + trigger: number, + type: number, + baseNote: number, + chord: number, + octaves: number + ): number { + $note = baseNote; + $octaves = octaves; + + if ($chord !== chord) { + $chord = chord; + chordNotes = getPitchClasses(chord); + len = chordNotes.length; + position = position % len; + } + + if (trigger === 1) { + if (!active) { + active = true; + current = getNextNote(type); + } + } else { + active = false; + } + + return current; + }; + + function getNextNote(type: number) { + switch (type) { + case ArpType.Random: + default: + return nextRandom(); + } + } + + function nextRandom() { + const octave = Math.floor(Math.random() * ($octaves - 1)); + const randomFromChord = chordNotes[Math.floor(Math.random() * len)]; + return $note + randomFromChord + octave * 12; + } +} + +function getPitchClasses(chord: number) { + const binary = chord.toString(2); + const pitchClasses: number[] = []; + for (let i = 0; i < binary.length; i++) { + if (binary[i] === "1") { + pitchClasses.push(i); + } + } + return pitchClasses; +} diff --git a/packages/arp/src/index.ts b/packages/arp/src/index.ts new file mode 100644 index 0000000..6c5a878 --- /dev/null +++ b/packages/arp/src/index.ts @@ -0,0 +1,34 @@ +import { + createRegistrar, + createWorkletConstructor, + ParamInput, +} from "./_worklet"; +import { PROCESSOR } from "./processor"; + +export const registerArpWorklet = createRegistrar("ARP", PROCESSOR); + +export type ArpInputs = { + trigger?: ParamInput; + type?: ParamInput; + baseNote?: ParamInput; + chord?: ParamInput; + octaves?: ParamInput; +}; + +export type ArpWorkletNode = AudioWorkletNode & { + trigger: AudioParam; + type: AudioParam; + baseNote: AudioParam; + chord: AudioParam; + octaves: AudioParam; + dispose(): void; +}; + +export const Arp = createWorkletConstructor({ + processorName: "ArpProcessor", + paramNames: ["trigger", "type", "baseNote", "chord", "octaves"], + workletOptions: () => ({ + numberOfInputs: 0, + numberOfOutputs: 1, + }), +}); diff --git a/packages/arp/src/processor.ts b/packages/arp/src/processor.ts new file mode 100644 index 0000000..f17c163 --- /dev/null +++ b/packages/arp/src/processor.ts @@ -0,0 +1 @@ +export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,a=1,i=!1,f=o;return function(u,p,b,c,d){return o=b,r=d,e!==c&&(e=c,t=y(c),a=t.length,n=n%a),u===1?i||(i=!0,f=g(p)):i=!1,f};function g(s){switch(s){case 0:default:return h()}}function h(){let s=Math.floor(Math.random()*(r-1)),u=t[Math.floor(Math.random()*a)];return o+u+s*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.chord[0],t.octaves[0]),a=r[0][0];return a&&a.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["chord",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; diff --git a/packages/arp/src/worklet.ts b/packages/arp/src/worklet.ts new file mode 100644 index 0000000..b01cdab --- /dev/null +++ b/packages/arp/src/worklet.ts @@ -0,0 +1,50 @@ +import { createArpeggiator } from "./dsp"; + +export class ArpProcessor extends AudioWorkletProcessor { + r: boolean; // running + a: ReturnType; + + constructor() { + super(); + this.r = true; + this.a = createArpeggiator(); + this.port.onmessage = (event) => { + switch (event.data.type) { + case "DISPOSE": + this.r = false; + break; + } + }; + } + + process(inputs: Float32Array[][], outputs: Float32Array[][], params: any) { + const note = this.a( + params.trigger[0], + params.type[0], + params.baseNote[0], + params.chord[0], + params.octaves[0] + ); + const output = outputs[0][0]; + if (output) output.fill(note); + return this.r; + } + + static get parameterDescriptors() { + return [ + ["trigger", 0, 0, 1], + ["type", 0, 0, 1], + ["baseNote", 60, 0, 200], + ["chord", 1, 1, 2047], + ["octaves", 1, 1, 10], + ].map(([name, defaultValue, minValue, maxValue]) => ({ + name, + defaultValue, + minValue, + maxValue, + automationRate: "k-rate", + })); + } +} + +registerProcessor("ArpProcessor", ArpProcessor); From d5f28c249022e7b5ff704bb95e3ccbc9d120d6a0 Mon Sep 17 00:00:00 2001 From: danigb Date: Sat, 7 Sep 2024 18:59:32 +0200 Subject: [PATCH 2/5] feat: arp example --- package-lock.json | 21 ++++++- packages/arp/package.json | 2 +- packages/arp/src/dsp.ts | 4 +- packages/arp/src/index.ts | 2 + packages/arp/src/processor.ts | 2 +- packages/synthlet/package.json | 1 + packages/synthlet/src/index.ts | 3 + packages/synthlet/src/synths/mono.ts | 23 ++++---- site/.source/index.js | 43 +++++++------- site/content/docs/(sequencers)/arp.mdx | 41 ++++++++++++++ site/examples/ArpExample.tsx | 78 ++++++++++++++++++++++++++ 11 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 site/content/docs/(sequencers)/arp.mdx create mode 100644 site/examples/ArpExample.tsx diff --git a/package-lock.json b/package-lock.json index a453b9c..be0c1aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2308,6 +2308,10 @@ "resolved": "packages/adsr", "link": true }, + "node_modules/@synthlet/arp": { + "resolved": "packages/arp", + "link": true + }, "node_modules/@synthlet/chorus-t": { "resolved": "packages/chorus-t", "link": true @@ -7745,6 +7749,15 @@ "ts-jest": "^29.1.1" } }, + "packages/arp": { + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "@types/jest": "^29.5.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.1" + } + }, "packages/chorus": { "version": "0.0.0", "extraneous": true, @@ -7786,7 +7799,8 @@ } }, "packages/dattorro-reverb": { - "version": "0.0.0", + "name": "@synthlet/dattorro-reverb", + "version": "0.1.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.11", @@ -7876,15 +7890,16 @@ } }, "packages/synthlet": { - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "dependencies": { "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", + "@synthlet/arp": "^0.0.0", "@synthlet/chorus-t": "^0.1.0", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", - "@synthlet/dattorro-reverb": "^0.0.0", + "@synthlet/dattorro-reverb": "^0.1.0", "@synthlet/euclid": "^0.1.0", "@synthlet/impulse": "^0.1.0", "@synthlet/lfo": "^0.1.0", diff --git a/packages/arp/package.json b/packages/arp/package.json index 0d07aca..eb928f8 100644 --- a/packages/arp/package.json +++ b/packages/arp/package.json @@ -1,6 +1,6 @@ { "name": "@synthlet/arp", - "version": "0.1.0", + "version": "0.0.0", "description": "Arpeggiator audio worklet", "keywords": [ "arpeggiator", diff --git a/packages/arp/src/dsp.ts b/packages/arp/src/dsp.ts index a83e5e5..061495d 100644 --- a/packages/arp/src/dsp.ts +++ b/packages/arp/src/dsp.ts @@ -43,7 +43,9 @@ export function createArpeggiator() { active = false; } - return current; + const freq = 440 * Math.pow(2, (current - 69) / 12); + + return freq; }; function getNextNote(type: number) { diff --git a/packages/arp/src/index.ts b/packages/arp/src/index.ts index 6c5a878..d8a2967 100644 --- a/packages/arp/src/index.ts +++ b/packages/arp/src/index.ts @@ -5,6 +5,8 @@ import { } from "./_worklet"; import { PROCESSOR } from "./processor"; +export { ArpChord, ArpType } from "./dsp"; + export const registerArpWorklet = createRegistrar("ARP", PROCESSOR); export type ArpInputs = { diff --git a/packages/arp/src/processor.ts b/packages/arp/src/processor.ts index f17c163..3294b37 100644 --- a/packages/arp/src/processor.ts +++ b/packages/arp/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,a=1,i=!1,f=o;return function(u,p,b,c,d){return o=b,r=d,e!==c&&(e=c,t=y(c),a=t.length,n=n%a),u===1?i||(i=!0,f=g(p)):i=!1,f};function g(s){switch(s){case 0:default:return h()}}function h(){let s=Math.floor(Math.random()*(r-1)),u=t[Math.floor(Math.random()*a)];return o+u+s*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.chord[0],t.octaves[0]),a=r[0][0];return a&&a.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["chord",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; +export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,a=1,i=!1,f=o;return function(c,p,b,u,d){return o=b,r=d,e!==u&&(e=u,t=y(u),a=t.length,n=n%a),c===1?i||(i=!0,f=h(p)):i=!1,440*Math.pow(2,(f-69)/12)};function h(s){switch(s){case 0:default:return g()}}function g(){let s=Math.floor(Math.random()*(r-1)),c=t[Math.floor(Math.random()*a)];return o+c+s*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.chord[0],t.octaves[0]),a=r[0][0];return a&&a.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["chord",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; diff --git a/packages/synthlet/package.json b/packages/synthlet/package.json index 42958f2..4e76a79 100644 --- a/packages/synthlet/package.json +++ b/packages/synthlet/package.json @@ -22,6 +22,7 @@ "dependencies": { "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", + "@synthlet/arp": "^0.0.0", "@synthlet/chorus-t": "^0.1.0", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", diff --git a/packages/synthlet/src/index.ts b/packages/synthlet/src/index.ts index 3720771..164bd7b 100644 --- a/packages/synthlet/src/index.ts +++ b/packages/synthlet/src/index.ts @@ -1,5 +1,6 @@ import { registerAdWorklet } from "@synthlet/ad"; import { registerAdsrWorklet } from "@synthlet/adsr"; +import { registerArpWorklet } from "@synthlet/arp"; import { registerChorusTWorklet } from "@synthlet/chorus-t"; import { registerClipAmpWorklet } from "@synthlet/clip-amp"; import { registerClockWorklet } from "@synthlet/clock"; @@ -15,6 +16,7 @@ import { registerWavetableOscillatorWorklet } from "@synthlet/wavetable-oscillat export * from "@synthlet/ad"; export * from "@synthlet/adsr"; +export * from "@synthlet/arp"; export * from "@synthlet/chorus-t"; export * from "@synthlet/clip-amp"; export * from "@synthlet/clock"; @@ -40,6 +42,7 @@ export function registerAllWorklets( return Promise.all([ registerAdWorklet(context), registerAdsrWorklet(context), + registerArpWorklet(context), registerChorusTWorklet(context), registerClipAmpWorklet(context), registerClockWorklet(context), diff --git a/packages/synthlet/src/synths/mono.ts b/packages/synthlet/src/synths/mono.ts index 933cf57..076a1ef 100644 --- a/packages/synthlet/src/synths/mono.ts +++ b/packages/synthlet/src/synths/mono.ts @@ -1,4 +1,6 @@ -import { LfoType } from "@synthlet/lfo"; +import { AdsrInputs } from "@synthlet/adsr"; +import { PolyblepOscillatorInputs } from "@synthlet/polyblep-oscillator"; +import { StateVariableFilterInputs } from "@synthlet/state-variable-filter"; import { ParamInput } from "../_worklet"; import { getSynthlet } from "../synthlet"; @@ -6,29 +8,26 @@ export type MonoSynthInputs = { gate?: ParamInput; frequency?: ParamInput; volume?: ParamInput; + osc?: PolyblepOscillatorInputs; + filter?: StateVariableFilterInputs; + amp?: AdsrInputs; }; export function MonoSynth(context: AudioContext, inputs: MonoSynthInputs = {}) { const s = getSynthlet(context); // Params const gate = s.param(inputs.gate); - const frequency = s.param(inputs.frequency ?? 440); - const vibrato = s.lfo({ - type: LfoType.Sine, - offset: frequency, - gain: 5, - frequency: 10, - }); const volume = s.param.db(inputs.volume ?? 0); + // Modules - const osc = s.polyblep({ frequency: vibrato }); + const osc = s.polyblep({ frequency: inputs.frequency, ...inputs.osc }); const filterEnv = s.env.adsr(gate, { gain: 3000, offset: 2000 }); const filter = s.svf({ frequency: filterEnv }); - const amp = s.amp.adsr(gate); + const amp = s.amp.adsr(gate, { ...inputs.amp }); return s.synth({ out: s.conn.serial(osc, filter, amp, s.amp(volume)), - params: { gate, frequency, volume }, - modules: { osc, vibrato, filterEnv, filter, amp }, + params: { gate, volume }, + modules: { osc, filterEnv, filter, amp }, }); } diff --git a/site/.source/index.js b/site/.source/index.js index a2ff136..fcb8f30 100644 --- a/site/.source/index.js +++ b/site/.source/index.js @@ -6,24 +6,25 @@ import * as file_3 from "../content/docs/synths.mdx?collection=docs&hash=a0e5c83 import * as file_4 from "../content/docs/troubleshoo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_5 from "../content/docs/(effects)/chorus-t.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_6 from "../content/docs/(effects)/dattorro.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_7 from "../content/docs/(sequencers)/clock.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_8 from "../content/docs/(sequencers)/euclid.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_9 from "../content/docs/(modifiers)/clip-amp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_10 from "../content/docs/(modifiers)/state-variable-filter.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_11 from "../content/docs/(modifiers)/vca.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_12 from "../content/docs/(modulators)/ad.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_13 from "../content/docs/(modulators)/adsr.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_14 from "../content/docs/(modulators)/lfo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_15 from "../content/docs/(modulators)/param.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_16 from "../content/docs/(sources)/impulse.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_17 from "../content/docs/(sources)/noise.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_18 from "../content/docs/(sources)/polyblep.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_19 from "../content/docs/(sources)/wavetable.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_20 from "../content/docs/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_21 from "../content/docs/(effects)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_22 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_23 from "../content/docs/(modulators)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_24 from "../content/docs/(sequencers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_25 from "../content/docs/(sources)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -export const docs = [toRuntime("doc", file_0, {"path":"dsl.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/dsl.mdx"}),toRuntime("doc", file_1, {"path":"guide.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/guide.mdx"}),toRuntime("doc", file_2, {"path":"quick-start.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/quick-start.mdx"}),toRuntime("doc", file_3, {"path":"synths.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/synths.mdx"}),toRuntime("doc", file_4, {"path":"troubleshoo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/troubleshoo.mdx"}),toRuntime("doc", file_5, {"path":"(effects)/chorus-t.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus-t.mdx"}),toRuntime("doc", file_6, {"path":"(effects)/dattorro.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/dattorro.mdx"}),toRuntime("doc", file_7, {"path":"(sequencers)/clock.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/clock.mdx"}),toRuntime("doc", file_8, {"path":"(sequencers)/euclid.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/euclid.mdx"}),toRuntime("doc", file_9, {"path":"(modifiers)/clip-amp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/clip-amp.mdx"}),toRuntime("doc", file_10, {"path":"(modifiers)/state-variable-filter.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/state-variable-filter.mdx"}),toRuntime("doc", file_11, {"path":"(modifiers)/vca.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/vca.mdx"}),toRuntime("doc", file_12, {"path":"(modulators)/ad.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/ad.mdx"}),toRuntime("doc", file_13, {"path":"(modulators)/adsr.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/adsr.mdx"}),toRuntime("doc", file_14, {"path":"(modulators)/lfo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/lfo.mdx"}),toRuntime("doc", file_15, {"path":"(modulators)/param.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/param.mdx"}),toRuntime("doc", file_16, {"path":"(sources)/impulse.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/impulse.mdx"}),toRuntime("doc", file_17, {"path":"(sources)/noise.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/noise.mdx"}),toRuntime("doc", file_18, {"path":"(sources)/polyblep.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/polyblep.mdx"}),toRuntime("doc", file_19, {"path":"(sources)/wavetable.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/wavetable.mdx"})] -export const meta = [toRuntime("meta", file_20, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_21, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_22, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_23, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_24, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_25, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file +import * as file_7 from "../content/docs/(modifiers)/clip-amp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_8 from "../content/docs/(modifiers)/state-variable-filter.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_9 from "../content/docs/(modifiers)/vca.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_10 from "../content/docs/(modulators)/ad.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_11 from "../content/docs/(modulators)/adsr.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_12 from "../content/docs/(modulators)/lfo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_13 from "../content/docs/(modulators)/param.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_14 from "../content/docs/(sequencers)/arp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_15 from "../content/docs/(sequencers)/clock.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_16 from "../content/docs/(sequencers)/euclid.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_17 from "../content/docs/(sources)/impulse.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_18 from "../content/docs/(sources)/noise.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_19 from "../content/docs/(sources)/polyblep.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_20 from "../content/docs/(sources)/wavetable.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_21 from "../content/docs/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_22 from "../content/docs/(effects)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_23 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_24 from "../content/docs/(modulators)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_25 from "../content/docs/(sequencers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_26 from "../content/docs/(sources)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +export const docs = [toRuntime("doc", file_0, {"path":"dsl.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/dsl.mdx"}),toRuntime("doc", file_1, {"path":"guide.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/guide.mdx"}),toRuntime("doc", file_2, {"path":"quick-start.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/quick-start.mdx"}),toRuntime("doc", file_3, {"path":"synths.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/synths.mdx"}),toRuntime("doc", file_4, {"path":"troubleshoo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/troubleshoo.mdx"}),toRuntime("doc", file_5, {"path":"(effects)/chorus-t.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus-t.mdx"}),toRuntime("doc", file_6, {"path":"(effects)/dattorro.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/dattorro.mdx"}),toRuntime("doc", file_7, {"path":"(modifiers)/clip-amp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/clip-amp.mdx"}),toRuntime("doc", file_8, {"path":"(modifiers)/state-variable-filter.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/state-variable-filter.mdx"}),toRuntime("doc", file_9, {"path":"(modifiers)/vca.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/vca.mdx"}),toRuntime("doc", file_10, {"path":"(modulators)/ad.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/ad.mdx"}),toRuntime("doc", file_11, {"path":"(modulators)/adsr.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/adsr.mdx"}),toRuntime("doc", file_12, {"path":"(modulators)/lfo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/lfo.mdx"}),toRuntime("doc", file_13, {"path":"(modulators)/param.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/param.mdx"}),toRuntime("doc", file_14, {"path":"(sequencers)/arp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/arp.mdx"}),toRuntime("doc", file_15, {"path":"(sequencers)/clock.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/clock.mdx"}),toRuntime("doc", file_16, {"path":"(sequencers)/euclid.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/euclid.mdx"}),toRuntime("doc", file_17, {"path":"(sources)/impulse.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/impulse.mdx"}),toRuntime("doc", file_18, {"path":"(sources)/noise.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/noise.mdx"}),toRuntime("doc", file_19, {"path":"(sources)/polyblep.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/polyblep.mdx"}),toRuntime("doc", file_20, {"path":"(sources)/wavetable.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/wavetable.mdx"})] +export const meta = [toRuntime("meta", file_21, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_22, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_23, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_24, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_25, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_26, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file diff --git a/site/content/docs/(sequencers)/arp.mdx b/site/content/docs/(sequencers)/arp.mdx new file mode 100644 index 0000000..bc69b46 --- /dev/null +++ b/site/content/docs/(sequencers)/arp.mdx @@ -0,0 +1,41 @@ +--- +title: Arpeggiator +description: An arpeggiator module +--- + +import ArpExample from "../../../examples/ArpExample"; + +Generates a note sequence from a given chord. + + + This module is WIP. Currently it only generates a random note from a given + chord. + + +```ts +import { registerAllWorklets, ArpType, ArpChord } from "synthlet"; + +const audioContext = await registerAllWorklets(new AudioContext()); +const clock = Clock(audioContext, { bpm: 120 }); +const arp = Arp(audioContext, { + trigger: clock, + type: ArpType.Random, + chord: ArpChord.Major, + octaves: 2, +}); + +const mono = MonoSynth(audioContext, { + trigger: clock, + frequency: arp, +}); +``` + + + +## Parameters + +- `trigger`: The clock signal to advance the arpeggiator. It will output the next note. +- `type`: The arpeggiator type. Default is `ArpType.Random`. +- `baseNote`: The base midi note of the arpeggiator. Default is `60`. +- `chord`: The chord type. Default is `ArpChordType.Major`. +- `octaves`: The number of octaves to span. Default is 2. diff --git a/site/examples/ArpExample.tsx b/site/examples/ArpExample.tsx new file mode 100644 index 0000000..e05d2ac --- /dev/null +++ b/site/examples/ArpExample.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { useState } from "react"; +import { + Arp, + ArpChord, + ArpType, + Clock, + DattorroReverb, + Euclid, + MonoSynth, + NoiseType, +} from "synthlet"; +import { ExamplePane } from "./components/ExamplePane"; +import { Slider } from "./components/Slider"; +import { useSynth } from "./useSynth"; + +function ArpSynth(context: AudioContext) { + const clock = Clock(context, { bpm: 120 }); + + const euclid = Euclid(context, { + clock, + beats: 5, + steps: 16, + }); + const arp = Arp(context, { + trigger: euclid, + type: ArpType.Random, + chord: ArpChord.Major, + octaves: 2, + }); + + const synth1 = MonoSynth(context, { + gate: euclid, + frequency: arp, + }); + + const reverb = DattorroReverb(context, { + decay: 0.9, + dryWet: 0.5, + }); + synth1.connect(reverb); + + return Object.assign(reverb, { synth1, arp, clock, euclid }); +} + +function Example() { + const [currentNoise, setCurrentNoise] = useState(NoiseType.White); + const synth = useSynth(ArpSynth); + if (!synth) return null; + + return ( +

+ + +
+ ); +} + +export default () => ( + + + +); From df36940f35cdab3f1ee2a1648b02b003cf66b525 Mon Sep 17 00:00:00 2001 From: danigb Date: Sat, 7 Sep 2024 19:26:08 +0200 Subject: [PATCH 3/5] chore: rename to scale --- packages/arp/chords.js | 73 ++++++++++++++++++++++++++ packages/arp/scales.js | 73 ++++++++++++++++++++++++++ packages/arp/src/dsp.ts | 53 ++++++++++++++----- packages/arp/src/index.ts | 8 +-- packages/arp/src/processor.ts | 2 +- packages/arp/src/worklet.ts | 4 +- site/content/docs/(sequencers)/arp.mdx | 23 ++++++-- site/examples/ArpExample.tsx | 4 +- 8 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 packages/arp/chords.js create mode 100644 packages/arp/scales.js diff --git a/packages/arp/chords.js b/packages/arp/chords.js new file mode 100644 index 0000000..43cd2d1 --- /dev/null +++ b/packages/arp/chords.js @@ -0,0 +1,73 @@ +const noteToPitchClass = { + C: 0, + "C#": 1, + Db: 1, + D: 2, + "D#": 3, + Eb: 3, + E: 4, + F: 5, + "F#": 6, + Gb: 6, + G: 7, + "G#": 8, + Ab: 8, + A: 9, + "A#": 10, + Bb: 10, + B: 11, +}; + +const parse = (chord) => { + const notes = chord.split(" "); + const pitchClasses = notes.map((note) => noteToPitchClass[note]); + const binary = []; + for (let i = 0; i < 12; i++) { + binary.push(pitchClasses.includes(i) ? 1 : 0); + } + binary.reverse(); + const binaryString = binary.join(""); + return parseInt(binaryString, 2); +}; + +const SCALES = { + Major: "C D E F G A B", + Minor: "C D Eb F G Ab Bb", + Dorian: "C D Eb F G A Bb", + Phrygian: "C Db Eb F G Ab Bb", + Lydian: "C D E F# G A B", + Mixolydian: "C D E F G A Bb", + Locrian: "C Db Eb F Gb Ab Bb", + HarmonicMinor: "C D Eb F G Ab B", + MelodicMinor: "C D Eb F G A B", + WholeTone: "C D E F# G# A#", + WholeHalfDiminished: "C D Eb F Gb Ab A B", + HalfWholeDiminished: "C Db Eb E F# G A Bb", + PentatonicMajor: "C D E G A", + PentatonicMinor: "C Eb F G Bb", + Pentatonic: "C Eb F G Bb", + Blues: "C Eb F F# G Bb", + Chromatic: "C Db D Eb E F Gb G Ab A Bb B", + Augmented: "C D# E G Ab B", + Diminished: "C D Eb F Gb Ab A B", + Dominant7th: "C E G Bb", + Minor7th: "C Eb G Bb", + Major7th: "C E G B", + MinorMajor7th: "C Eb G B", + TriadMajor: "C E G", + TriadMinor: "C Eb G", + TriadAugmented: "C E G#", + TriadDiminished: "C Eb Gb", + Sus2: "C D G", + Sus4: "C F G", + Major6th: "C E G A", +}; + +const VALUES = Object.entries(SCALES).map(([key, value]) => { + return `${key}= ${parse(value)},`; +}); + +console.log("export enum ArpScale {"); +VALUES.sort(); +console.log(VALUES.join("\n")); +console.log("};"); diff --git a/packages/arp/scales.js b/packages/arp/scales.js new file mode 100644 index 0000000..eed9b16 --- /dev/null +++ b/packages/arp/scales.js @@ -0,0 +1,73 @@ +const noteToPitchClass = { + C: 0, + "C#": 1, + Db: 1, + D: 2, + "D#": 3, + Eb: 3, + E: 4, + F: 5, + "F#": 6, + Gb: 6, + G: 7, + "G#": 8, + Ab: 8, + A: 9, + "A#": 10, + Bb: 10, + B: 11, +}; + +const getChordNumber = (pitchClasses) => {}; + +const parse = (chord) => { + const notes = chord.split(" "); + const pitchClasses = notes.map((note) => noteToPitchClass[note]); + const binary = []; + for (let i = 0; i < 12; i++) { + binary.push(pitchClasses.includes(i) ? 1 : 0); + } + binary.reverse(); + const binaryString = binary.join(""); + console.log(notes, pitchClasses, binaryString); + return parseInt(binaryString, 2); +}; + +const SCALES = { + Major: "C D E F G A B", + Minor: "C D Eb F G Ab Bb", + Dorian: "C D Eb F G A Bb", + Phrygian: "C Db Eb F G Ab Bb", + Lydian: "C D E F# G A B", + Mixolydian: "C D E F G A Bb", + Locrian: "C Db Eb F Gb Ab Bb", + HarmonicMinor: "C D Eb F G Ab B", + MelodicMinor: "C D Eb F G A B", + WholeTone: "C D E F# G# A#", + WholeHalfDiminished: "C D Eb F Gb Ab A B", + HalfWholeDiminished: "C Db Eb E F# G A Bb", + MajorPentatonic: "C D E G A", + MinorPentatonic: "C Eb F G Bb", + Blues: "C Eb F F# G Bb", + Chromatic: "C Db D Eb E F Gb G Ab A Bb B", + Augmented: "C D# E G Ab B", + Diminished: "C D Eb F Gb Ab A B", + Dominant7th: "C E G Bb", + Minor7th: "C Eb G Bb", + Major7th: "C E G B", + MinorMajor7th: "C Eb G B", + MajorTriad: "C E G", + MinorTriad: "C Eb G", + AugmentedTriad: "C E G#", + DiminishedTriad: "C Eb Gb", + Sus2: "C D G", + Sus4: "C F G", + Major6th: "C E G A", +}; + +const CONVERTED = Object.entries(SCALES).reduce((acc, [key, value]) => { + acc[key] = parse(value); + return acc; +}); + +console.log(CONVERTED); diff --git a/packages/arp/src/dsp.ts b/packages/arp/src/dsp.ts index 061495d..ef5f63e 100644 --- a/packages/arp/src/dsp.ts +++ b/packages/arp/src/dsp.ts @@ -2,16 +2,45 @@ export enum ArpType { Random = 0, } -export enum ArpChord { - Major = 145, +export enum ArpScale { + Augmented = 2457, + Blues = 1257, + Chromatic = 4095, + Diminished = 2925, + Dominant7th = 1169, + Dorian = 1709, + HalfWholeDiminished = 1755, + HarmonicMinor = 2477, + Locrian = 1387, + Lydian = 2773, + Major6th = 657, + Major7th = 2193, + Major = 2741, + MelodicMinor = 2733, + Minor7th = 1161, + Minor = 1453, + MinorMajor7th = 2185, + Mixolydian = 1717, + Pentatonic = 1193, + PentatonicMajor = 661, + PentatonicMinor = 1193, + Phrygian = 1451, + Sus2 = 133, + Sus4 = 161, + TriadAugmented = 273, + TriadDiminished = 73, + TriadMajor = 145, + TriadMinor = 137, + WholeHalfDiminished = 2925, + WholeTone = 1365, } export function createArpeggiator() { let $note = 60; - let $chord = 0; + let $scale = 0; let $octaves = 1; - let chordNotes = [0]; + let scaleNotes = [0]; let position = 0; let len = 1; let active = false; @@ -21,16 +50,16 @@ export function createArpeggiator() { trigger: number, type: number, baseNote: number, - chord: number, + scale: number, octaves: number ): number { $note = baseNote; $octaves = octaves; - if ($chord !== chord) { - $chord = chord; - chordNotes = getPitchClasses(chord); - len = chordNotes.length; + if ($scale !== scale) { + $scale = scale; + scaleNotes = getPitchClasses(scale); + len = scaleNotes.length; position = position % len; } @@ -58,13 +87,13 @@ export function createArpeggiator() { function nextRandom() { const octave = Math.floor(Math.random() * ($octaves - 1)); - const randomFromChord = chordNotes[Math.floor(Math.random() * len)]; + const randomFromChord = scaleNotes[Math.floor(Math.random() * len)]; return $note + randomFromChord + octave * 12; } } -function getPitchClasses(chord: number) { - const binary = chord.toString(2); +function getPitchClasses(scale: number) { + const binary = scale.toString(2); const pitchClasses: number[] = []; for (let i = 0; i < binary.length; i++) { if (binary[i] === "1") { diff --git a/packages/arp/src/index.ts b/packages/arp/src/index.ts index d8a2967..2e7c10f 100644 --- a/packages/arp/src/index.ts +++ b/packages/arp/src/index.ts @@ -5,7 +5,7 @@ import { } from "./_worklet"; import { PROCESSOR } from "./processor"; -export { ArpChord, ArpType } from "./dsp"; +export { ArpScale, ArpType } from "./dsp"; export const registerArpWorklet = createRegistrar("ARP", PROCESSOR); @@ -13,7 +13,7 @@ export type ArpInputs = { trigger?: ParamInput; type?: ParamInput; baseNote?: ParamInput; - chord?: ParamInput; + scale?: ParamInput; octaves?: ParamInput; }; @@ -21,14 +21,14 @@ export type ArpWorkletNode = AudioWorkletNode & { trigger: AudioParam; type: AudioParam; baseNote: AudioParam; - chord: AudioParam; + scale: AudioParam; octaves: AudioParam; dispose(): void; }; export const Arp = createWorkletConstructor({ processorName: "ArpProcessor", - paramNames: ["trigger", "type", "baseNote", "chord", "octaves"], + paramNames: ["trigger", "type", "baseNote", "scale", "octaves"], workletOptions: () => ({ numberOfInputs: 0, numberOfOutputs: 1, diff --git a/packages/arp/src/processor.ts b/packages/arp/src/processor.ts index 3294b37..3ffc810 100644 --- a/packages/arp/src/processor.ts +++ b/packages/arp/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,a=1,i=!1,f=o;return function(c,p,b,u,d){return o=b,r=d,e!==u&&(e=u,t=y(u),a=t.length,n=n%a),c===1?i||(i=!0,f=h(p)):i=!1,440*Math.pow(2,(f-69)/12)};function h(s){switch(s){case 0:default:return g()}}function g(){let s=Math.floor(Math.random()*(r-1)),c=t[Math.floor(Math.random()*a)];return o+c+s*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.chord[0],t.octaves[0]),a=r[0][0];return a&&a.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["chord",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; +export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,s=1,i=!1,f=o;return function(u,p,b,c,d){return o=b,r=d,e!==c&&(e=c,t=y(c),s=t.length,n=n%s),u===1?i||(i=!0,f=h(p)):i=!1,440*Math.pow(2,(f-69)/12)};function h(a){switch(a){case 0:default:return g()}}function g(){let a=Math.floor(Math.random()*(r-1)),u=t[Math.floor(Math.random()*s)];return o+u+a*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.scale[0],t.octaves[0]),s=r[0][0];return s&&s.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["scale",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; diff --git a/packages/arp/src/worklet.ts b/packages/arp/src/worklet.ts index b01cdab..eba8f25 100644 --- a/packages/arp/src/worklet.ts +++ b/packages/arp/src/worklet.ts @@ -22,7 +22,7 @@ export class ArpProcessor extends AudioWorkletProcessor { params.trigger[0], params.type[0], params.baseNote[0], - params.chord[0], + params.scale[0], params.octaves[0] ); const output = outputs[0][0]; @@ -35,7 +35,7 @@ export class ArpProcessor extends AudioWorkletProcessor { ["trigger", 0, 0, 1], ["type", 0, 0, 1], ["baseNote", 60, 0, 200], - ["chord", 1, 1, 2047], + ["scale", 1, 1, 2047], ["octaves", 1, 1, 10], ].map(([name, defaultValue, minValue, maxValue]) => ({ name, diff --git a/site/content/docs/(sequencers)/arp.mdx b/site/content/docs/(sequencers)/arp.mdx index bc69b46..f990055 100644 --- a/site/content/docs/(sequencers)/arp.mdx +++ b/site/content/docs/(sequencers)/arp.mdx @@ -5,22 +5,22 @@ description: An arpeggiator module import ArpExample from "../../../examples/ArpExample"; -Generates a note sequence from a given chord. +Generates a note sequence from a given scale. This module is WIP. Currently it only generates a random note from a given - chord. + scale. ```ts -import { registerAllWorklets, ArpType, ArpChord } from "synthlet"; +import { registerAllWorklets, ArpType, ArpScale } from "synthlet"; const audioContext = await registerAllWorklets(new AudioContext()); const clock = Clock(audioContext, { bpm: 120 }); const arp = Arp(audioContext, { trigger: clock, type: ArpType.Random, - chord: ArpChord.Major, + scale: ArpScale.Major, octaves: 2, }); @@ -37,5 +37,18 @@ const mono = MonoSynth(audioContext, { - `trigger`: The clock signal to advance the arpeggiator. It will output the next note. - `type`: The arpeggiator type. Default is `ArpType.Random`. - `baseNote`: The base midi note of the arpeggiator. Default is `60`. -- `chord`: The chord type. Default is `ArpChordType.Major`. +- `scale`: The scale type. Default is `ArpScale.Major`. - `octaves`: The number of octaves to span. Default is 2. + +## Scales + +You can use `ArpScale` to specify the scale of the arpeggiator. Or just have fun with random numbers! + +- `ArpScale.Major` +- `ArpScale.Minor` +- `ArpScale.PentatonicMinor` +- `ArpScale.Blues` +- `ArpScale.WholeTone` +- `ArpScale.Chromatic` +- `ArpScale.Melodic` +- `ArpScale.Harmonic` diff --git a/site/examples/ArpExample.tsx b/site/examples/ArpExample.tsx index e05d2ac..6c75ae4 100644 --- a/site/examples/ArpExample.tsx +++ b/site/examples/ArpExample.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { Arp, - ArpChord, + ArpScale, ArpType, Clock, DattorroReverb, @@ -26,7 +26,7 @@ function ArpSynth(context: AudioContext) { const arp = Arp(context, { trigger: euclid, type: ArpType.Random, - chord: ArpChord.Major, + scale: ArpScale.Major, octaves: 2, }); From d51c412c0a92030e685149bcf5b24ac3d2950582 Mon Sep 17 00:00:00 2001 From: danigb Date: Sat, 7 Sep 2024 19:27:39 +0200 Subject: [PATCH 4/5] chore: version bump --- packages/arp/CHANGELOG.md | 5 +++++ packages/arp/package.json | 2 +- packages/synthlet/CHANGELOG.md | 4 ++++ packages/synthlet/package.json | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 packages/arp/CHANGELOG.md diff --git a/packages/arp/CHANGELOG.md b/packages/arp/CHANGELOG.md new file mode 100644 index 0000000..58908b3 --- /dev/null +++ b/packages/arp/CHANGELOG.md @@ -0,0 +1,5 @@ +# @synthlet/arp + +## 0.1.0 + +Initial version (random notes only) diff --git a/packages/arp/package.json b/packages/arp/package.json index eb928f8..0d07aca 100644 --- a/packages/arp/package.json +++ b/packages/arp/package.json @@ -1,6 +1,6 @@ { "name": "@synthlet/arp", - "version": "0.0.0", + "version": "0.1.0", "description": "Arpeggiator audio worklet", "keywords": [ "arpeggiator", diff --git a/packages/synthlet/CHANGELOG.md b/packages/synthlet/CHANGELOG.md index f1fe724..0e05b59 100644 --- a/packages/synthlet/CHANGELOG.md +++ b/packages/synthlet/CHANGELOG.md @@ -1,5 +1,9 @@ # synthlet +## 0.4.0 + +- New @synthlet/arp package + ## 0.3.0 - Function `registerSynthlet` renamed to `registerAllWorklets` diff --git a/packages/synthlet/package.json b/packages/synthlet/package.json index 4e76a79..ecb369d 100644 --- a/packages/synthlet/package.json +++ b/packages/synthlet/package.json @@ -1,6 +1,6 @@ { "name": "synthlet", - "version": "0.2.1", + "version": "0.4.0", "description": "Modular synthesis in the browser", "keywords": [ "modular", @@ -22,7 +22,7 @@ "dependencies": { "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", - "@synthlet/arp": "^0.0.0", + "@synthlet/arp": "^0.1.0", "@synthlet/chorus-t": "^0.1.0", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", From 91046eedfd524a7c3f6ff611dfc3f142c23395ff Mon Sep 17 00:00:00 2001 From: danigb Date: Sat, 7 Sep 2024 19:32:45 +0200 Subject: [PATCH 5/5] fix: add mono vibrato back --- CHANGELOG.md | 4 ++++ packages/arp/src/processor.ts | 2 +- packages/synthlet/src/synths/mono.ts | 10 +++++++++- site/.source/index.js | 6 +++--- site/examples/ArpExample.tsx | 1 - site/examples/MonoExample.tsx | 2 +- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1fe724..0e05b59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # synthlet +## 0.4.0 + +- New @synthlet/arp package + ## 0.3.0 - Function `registerSynthlet` renamed to `registerAllWorklets` diff --git a/packages/arp/src/processor.ts b/packages/arp/src/processor.ts index 3ffc810..de73a19 100644 --- a/packages/arp/src/processor.ts +++ b/packages/arp/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function m(){let o=60,e=0,r=1,t=[0],n=0,s=1,i=!1,f=o;return function(u,p,b,c,d){return o=b,r=d,e!==c&&(e=c,t=y(c),s=t.length,n=n%s),u===1?i||(i=!0,f=h(p)):i=!1,440*Math.pow(2,(f-69)/12)};function h(a){switch(a){case 0:default:return g()}}function g(){let a=Math.floor(Math.random()*(r-1)),u=t[Math.floor(Math.random()*s)];return o+u+a*12}}function y(o){let e=o.toString(2),r=[];for(let t=0;t{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(e,r,t){let n=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.scale[0],t.octaves[0]),s=r[0][0];return s&&s.fill(n),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["scale",1,1,2047],["octaves",1,1,10]].map(([e,r,t,n])=>({name:e,defaultValue:r,minValue:t,maxValue:n,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",l);})();`; +export const PROCESSOR = `"use strict";(()=>{function g(){let i=60,o=0,n=1,t=[0],e=0,s=1,u=!1,f=i;return function(h,a,y,m,x){return i=y,n=x,o!==m&&(o=m,t=D(m),s=t.length,e=e%s),h===1?u||(u=!0,f=M(a)):u=!1,440*Math.pow(2,(f-69)/12)};function M(r){switch(r){case 0:default:return b()}}function b(){let r=Math.floor(Math.random()*(n-1)),h=t[Math.floor(Math.random()*s)];return i+h+r*12}}function D(i){let o=i.toString(2),n=[];for(let t=0;t{switch(o.data.type){case"DISPOSE":this.r=!1;break}}}process(o,n,t){let e=this.a(t.trigger[0],t.type[0],t.baseNote[0],t.scale[0],t.octaves[0]),s=n[0][0];return s&&s.fill(e),this.r}static get parameterDescriptors(){return[["trigger",0,0,1],["type",0,0,1],["baseNote",60,0,200],["scale",1,1,2047],["octaves",1,1,10]].map(([o,n,t,e])=>({name:o,defaultValue:n,minValue:t,maxValue:e,automationRate:"k-rate"}))}};registerProcessor("ArpProcessor",d);})();`; diff --git a/packages/synthlet/src/synths/mono.ts b/packages/synthlet/src/synths/mono.ts index 076a1ef..e468cb1 100644 --- a/packages/synthlet/src/synths/mono.ts +++ b/packages/synthlet/src/synths/mono.ts @@ -1,4 +1,5 @@ import { AdsrInputs } from "@synthlet/adsr"; +import { LfoInputs, LfoType } from "@synthlet/lfo"; import { PolyblepOscillatorInputs } from "@synthlet/polyblep-oscillator"; import { StateVariableFilterInputs } from "@synthlet/state-variable-filter"; import { ParamInput } from "../_worklet"; @@ -8,6 +9,7 @@ export type MonoSynthInputs = { gate?: ParamInput; frequency?: ParamInput; volume?: ParamInput; + vibrato?: LfoInputs; osc?: PolyblepOscillatorInputs; filter?: StateVariableFilterInputs; amp?: AdsrInputs; @@ -21,6 +23,12 @@ export function MonoSynth(context: AudioContext, inputs: MonoSynthInputs = {}) { // Modules const osc = s.polyblep({ frequency: inputs.frequency, ...inputs.osc }); + const vibrato = s.lfo({ + type: LfoType.Sine, + gain: 0, + frequency: 10, + }); + vibrato.connect(osc.frequency); const filterEnv = s.env.adsr(gate, { gain: 3000, offset: 2000 }); const filter = s.svf({ frequency: filterEnv }); const amp = s.amp.adsr(gate, { ...inputs.amp }); @@ -28,6 +36,6 @@ export function MonoSynth(context: AudioContext, inputs: MonoSynthInputs = {}) { return s.synth({ out: s.conn.serial(osc, filter, amp, s.amp(volume)), params: { gate, volume }, - modules: { osc, filterEnv, filter, amp }, + modules: { osc, filterEnv, filter, amp, vibrato }, }); } diff --git a/site/.source/index.js b/site/.source/index.js index fcb8f30..499f217 100644 --- a/site/.source/index.js +++ b/site/.source/index.js @@ -21,10 +21,10 @@ import * as file_18 from "../content/docs/(sources)/noise.mdx?collection=docs&ha import * as file_19 from "../content/docs/(sources)/polyblep.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_20 from "../content/docs/(sources)/wavetable.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_21 from "../content/docs/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_22 from "../content/docs/(effects)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_23 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_22 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_23 from "../content/docs/(effects)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_24 from "../content/docs/(modulators)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_25 from "../content/docs/(sequencers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_26 from "../content/docs/(sources)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" export const docs = [toRuntime("doc", file_0, {"path":"dsl.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/dsl.mdx"}),toRuntime("doc", file_1, {"path":"guide.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/guide.mdx"}),toRuntime("doc", file_2, {"path":"quick-start.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/quick-start.mdx"}),toRuntime("doc", file_3, {"path":"synths.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/synths.mdx"}),toRuntime("doc", file_4, {"path":"troubleshoo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/troubleshoo.mdx"}),toRuntime("doc", file_5, {"path":"(effects)/chorus-t.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus-t.mdx"}),toRuntime("doc", file_6, {"path":"(effects)/dattorro.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/dattorro.mdx"}),toRuntime("doc", file_7, {"path":"(modifiers)/clip-amp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/clip-amp.mdx"}),toRuntime("doc", file_8, {"path":"(modifiers)/state-variable-filter.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/state-variable-filter.mdx"}),toRuntime("doc", file_9, {"path":"(modifiers)/vca.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/vca.mdx"}),toRuntime("doc", file_10, {"path":"(modulators)/ad.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/ad.mdx"}),toRuntime("doc", file_11, {"path":"(modulators)/adsr.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/adsr.mdx"}),toRuntime("doc", file_12, {"path":"(modulators)/lfo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/lfo.mdx"}),toRuntime("doc", file_13, {"path":"(modulators)/param.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/param.mdx"}),toRuntime("doc", file_14, {"path":"(sequencers)/arp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/arp.mdx"}),toRuntime("doc", file_15, {"path":"(sequencers)/clock.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/clock.mdx"}),toRuntime("doc", file_16, {"path":"(sequencers)/euclid.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/euclid.mdx"}),toRuntime("doc", file_17, {"path":"(sources)/impulse.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/impulse.mdx"}),toRuntime("doc", file_18, {"path":"(sources)/noise.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/noise.mdx"}),toRuntime("doc", file_19, {"path":"(sources)/polyblep.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/polyblep.mdx"}),toRuntime("doc", file_20, {"path":"(sources)/wavetable.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/wavetable.mdx"})] -export const meta = [toRuntime("meta", file_21, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_22, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_23, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_24, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_25, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_26, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file +export const meta = [toRuntime("meta", file_21, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_22, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_23, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_24, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_25, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_26, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file diff --git a/site/examples/ArpExample.tsx b/site/examples/ArpExample.tsx index 6c75ae4..7b93121 100644 --- a/site/examples/ArpExample.tsx +++ b/site/examples/ArpExample.tsx @@ -37,7 +37,6 @@ function ArpSynth(context: AudioContext) { const reverb = DattorroReverb(context, { decay: 0.9, - dryWet: 0.5, }); synth1.connect(reverb); diff --git a/site/examples/MonoExample.tsx b/site/examples/MonoExample.tsx index eca70d8..838216f 100644 --- a/site/examples/MonoExample.tsx +++ b/site/examples/MonoExample.tsx @@ -19,7 +19,7 @@ function Example() { inputClassName="col-span-2" min={20} max={3000} - param={synth.frequency} + param={synth.osc.frequency} />