this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add ADSR node

+99 -1
+1
index.html
··· 9 9 <body> 10 10 <div id="app"> 11 11 <button id="start">Click to start</button> 12 + <button id="trigger">Trigger</button> 12 13 </div> 13 14 <script type="module" src="/src/main.ts"></script> 14 15 </body>
src/Oscillator.ts

This is a binary file and will not be displayed.

+52 -1
src/main.ts
··· 1 + import { AdsrNode } from "./nodes/AdsrNode"; 1 2 import "./style.css"; 3 + 4 + let audioCtx: AudioContext; 5 + let osc1: OscillatorNode; 2 6 3 7 function start() { 4 8 const startButton = document.getElementById("start"); 5 9 if (!startButton) { 6 10 return; 7 11 } 12 + const triggerButton = document.getElementById("trigger"); 13 + if (!triggerButton) { 14 + return; 15 + } 16 + 17 + audioCtx = new AudioContext(); 18 + const freq = 261.625565; // Middle C 19 + 20 + osc1 = new OscillatorNode(audioCtx, { 21 + frequency: freq, 22 + type: "sawtooth", 23 + }); 24 + 25 + const adsr1 = new AdsrNode(audioCtx, { 26 + attack: 0.01, 27 + decay: 0.11, 28 + sustain: 0.0, 29 + release: 0.1, 30 + }); 31 + adsr1.start(); 32 + 33 + const vca1 = new GainNode(audioCtx); 34 + vca1.gain.value = 0.0; 35 + 36 + const vol = new GainNode(audioCtx); 37 + vol.gain.value = 1.0; 38 + 39 + adsr1.connect(vca1.gain); 40 + osc1.connect(vca1).connect(vol).connect(audioCtx.destination); 41 + osc1.start(); 8 42 9 43 const play = () => { 10 - console.log("Starting"); 44 + if (audioCtx.state === "suspended") { 45 + console.log("Starting"); 46 + audioCtx.resume(); 47 + 48 + startButton.innerText = "Click to pause"; 49 + } else if (audioCtx.state == "running") { 50 + console.log("Pausing"); 51 + startButton.innerText = "Click to start"; 52 + 53 + audioCtx.suspend(); 54 + } 11 55 }; 12 56 startButton.addEventListener("click", play); 57 + 58 + const trigger = () => { 59 + const noteTime = 0.5; // Quarter note at 120bpm 60 + const gateOnTime = noteTime / 2; 61 + adsr1.trigger(gateOnTime); 62 + }; 63 + triggerButton.addEventListener("click", trigger); 13 64 } 14 65 15 66 start();
+46
src/nodes/AdsrNode.ts
··· 1 + export type AdsrNodeOptions = { 2 + attack?: number; 3 + decay?: number; 4 + sustain?: number; 5 + release?: number; 6 + }; 7 + 8 + export class AdsrNode extends ConstantSourceNode { 9 + attack = 0; 10 + decay = 0; 11 + sustain = 0; 12 + release = 0; 13 + 14 + constructor(ctx: AudioContext, options?: AdsrNodeOptions) { 15 + super(ctx, { offset: 0 }); 16 + const { attack = 0, decay = 0, sustain = 0, release = 0 } = options ?? {}; 17 + this.attack = attack; 18 + this.decay = decay; 19 + this.sustain = sustain; 20 + this.release = release; 21 + } 22 + 23 + trigger(length: number) { 24 + this.offset.cancelScheduledValues(this.context.currentTime); 25 + this.offset.setValueAtTime(0.0, this.context.currentTime); 26 + // Attack 27 + this.offset.linearRampToValueAtTime( 28 + 1.0, 29 + this.context.currentTime + this.attack, 30 + ); 31 + 32 + // Sustain 33 + this.offset.linearRampToValueAtTime( 34 + this.sustain, 35 + this.context.currentTime + this.attack + this.decay, 36 + ); 37 + this.offset.cancelAndHoldAtTime(this.context.currentTime + length); 38 + this.offset.setValueAtTime(this.sustain, this.context.currentTime + length); 39 + 40 + // Release 41 + this.offset.linearRampToValueAtTime( 42 + 0.0, 43 + this.context.currentTime + length + this.release, 44 + ); 45 + } 46 + }