UNPKG

sw-synth

Version:

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

130 lines 4.53 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BufferSynth = exports.AperiodicSynth = exports.UnisonSynth = exports.Synth = void 0; const oscillator_1 = require("./voice/oscillator"); const buffer_1 = require("./voice/buffer"); __exportStar(require("./voice"), exports); // Tracking numbers for voice stealing // Technically we could run out of note identifiers, // but who is going to play 9007199254740991 notes in one session? let NOTE_ID = 1; /** * Simple web audio synth of finite polyphony. */ class Synth { constructor(audioContext, destination, log) { this.audioContext = audioContext; this.destination = destination; if (log === undefined) { // eslint-disable-next-line @typescript-eslint/no-unused-vars this.log = (msg) => { }; } else { this.log = log; } this.voices = []; } _newVoice() { return new oscillator_1.OscillatorVoice(this.audioContext, this.destination, this.log); } setPolyphony(maxPolyphony) { if (maxPolyphony < 0 || maxPolyphony === Infinity || isNaN(maxPolyphony)) { throw new Error('Invalid max polyphony'); } while (this.voices.length > maxPolyphony) { this.voices.pop().dispose(); } while (this.voices.length < maxPolyphony) { this.voices.push(this._newVoice()); } } get maxPolyphony() { return this.voices.length; } set maxPolyphony(value) { this.setPolyphony(value); } _allocateVoice() { // Allocate voices based on age. // Boils down to: // a) Pick the oldest released voice. // b) If there are no released voices, replace the oldest currently playing voice. let oldestVoice; for (const voice of this.voices) { voice.age++; if (oldestVoice === undefined || voice.age > oldestVoice.age) { oldestVoice = voice; } } return oldestVoice; } /** * Start playing a note at the specified frequency and velocity. * @param frequency Frequency in Hertz. * @param velocity Voice amplitude. Recomended range from 0 to 1. * @returns A callback that stops playing the note. */ noteOn(frequency, velocity) { const oldestVoice = this._allocateVoice(); if (oldestVoice === undefined) { return () => { }; } if (this.voiceParams === undefined) { throw new Error('Synth.voiceParams must be set before calling Synth.noteOn'); } return oldestVoice.noteOn(frequency, velocity, NOTE_ID++, this.voiceParams); } /** * Trigger panic and release all notes. */ allNotesOff() { for (const voice of this.voices) { if (voice.lastNoteOff !== undefined) { voice.lastNoteOff(); } } } } exports.Synth = Synth; /** * Web audio synth of finite polyphony where the voices are stacked in unison. */ class UnisonSynth extends Synth { _newVoice() { return new oscillator_1.UnisonVoice(this.audioContext, this.destination, this.log); } } exports.UnisonSynth = UnisonSynth; /** * Web audio synth of finite polyphony that supports inharmonic timbres. */ class AperiodicSynth extends Synth { _newVoice() { return new oscillator_1.AperiodicVoice(this.audioContext, this.destination, this.log); } } exports.AperiodicSynth = AperiodicSynth; /** * Web audio synth of finite polyphony with user-provided audio buffers. */ class BufferSynth extends Synth { _newVoice() { return new buffer_1.BufferVoice(this.audioContext, this.destination, this.log); } } exports.BufferSynth = BufferSynth; //# sourceMappingURL=index.js.map