UNPKG

sw-synth

Version:

Lightweight sound synthesizer designed for real-time user interaction using the Web Audio API

59 lines 2.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VoiceBase = void 0; // Exponential approach conversion, smaller value results in more eager envelopes const TIME_CONSTANT = 0.5; // Large but finite number to signify voices that are off const EXPIRED = 10000; // Tracking numbers for logging purposes let VOICE_ID = 1; /** * Oscillator with ADSR envelope. * Represents a single "channel" of polyphony. Should be reused for multiple notes. */ class VoiceBase { constructor(context, destination, log) { this.age = EXPIRED; this.context = context; this.envelope = new GainNode(context, { gain: 0 }); this.envelope.connect(destination); this.log = log; this.noteId = 0; this.voiceId = VOICE_ID++; } noteOn(frequency, velocity, noteId, params) { this.log(`Voice ${this.voiceId}: Age = ${this.age}, note = ${noteId}, frequency = ${frequency}`); this.age = 0; this.noteId = noteId; const now = this.context.currentTime + params.audioDelay; this.log(`Voice ${this.voiceId}: On time = ${now}, sustain time = ${now + params.attackTime}`); this.envelope.gain.setValueAtTime(0, now); this.envelope.gain.linearRampToValueAtTime(velocity, now + params.attackTime); this.envelope.gain.setTargetAtTime(velocity * params.sustainLevel, now + params.attackTime, params.decayTime * TIME_CONSTANT); // Construct a callback that turns this voice off. const noteOff = () => { // Do nothing if the voice has been stolen or already released. if (this.noteId !== noteId) { this.log(`Voice ${this.voiceId} had been stolen. Ignoring note off`); return; } this.age = EXPIRED; const then = this.context.currentTime; this.log(`Voice ${this.voiceId}: Off time = ${then}`); this.envelope.gain.cancelScheduledValues(then); // NOTE: Canceling scheduled values doesn't hold intermediate values of linear ramps if (then < now + params.attackTime) { // Calculate correct linear ramp hold value this.envelope.gain.setValueAtTime((velocity * (then - now)) / params.attackTime, then); } this.envelope.gain.setTargetAtTime(0, then, params.releaseTime * TIME_CONSTANT); // We're done here. this.noteId = -1; }; this.lastNoteOff = noteOff; return noteOff; } dispose() { } } exports.VoiceBase = VoiceBase; //# sourceMappingURL=base.js.map