UNPKG

@ng-web-apis/audio

Version:

This is a library for declarative use of Web Audio API with Angular

1,130 lines (1,100 loc) 84.7 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, forwardRef, Directive, Attribute, Output, Input, EventEmitter, ContentChildren, Injectable, Pipe, ElementRef } from '@angular/core'; import { interval, map, tap, distinctUntilChanged, skipWhile, debounceTime, filter, animationFrameScheduler, share, Subject, switchMap, of } from 'rxjs'; import { __decorate } from 'tslib'; const WA_AUDIO_CONTEXT = new InjectionToken('[WA_AUDIO_CONTEXT]', { providedIn: 'root', factory: () => new AudioContext(), }); /** * @deprecated: drop in v5.0, use {@link WA_AUDIO_CONTEXT} */ const AUDIO_CONTEXT = WA_AUDIO_CONTEXT; /** * This is mostly for internal use only */ const WA_CONSTRUCTOR_SUPPORT = new InjectionToken('[WA_CONSTRUCTOR_SUPPORT]', { providedIn: 'root', factory: () => { try { return !!new GainNode(inject(WA_AUDIO_CONTEXT)); } catch { return false; } }, }); /** * @deprecated: drop in v5.0, use {@link WA_CONSTRUCTOR_SUPPORT} */ const CONSTRUCTOR_SUPPORT = WA_CONSTRUCTOR_SUPPORT; /** * Just for unit tests */ const providers = [ { provide: CONSTRUCTOR_SUPPORT, useValue: false, }, ]; const POLLING_TIME = 100; function processAudioParam(param, value, currentTime = 0) { if (param.cancelAndHoldAtTime) { param.cancelAndHoldAtTime(currentTime); } else { param.cancelScheduledValues(currentTime); param.setValueAtTime(guard(param.value), currentTime); } if (typeof value === 'number') { param.setValueAtTime(guard(value), currentTime); return; } if (value instanceof Array) { processSchedule(param, value, currentTime); return; } if (!('mode' in value)) { param.setValueCurveAtTime(value.value, currentTime, value.duration); return; } param.setValueAtTime(guard(param.value), currentTime); processAutomation(param, value, currentTime); } function processSchedule(param, value, currentTime) { value.forEach((automation) => { if ('mode' in automation) { processAutomation(param, automation, currentTime); } else { param.setValueCurveAtTime(automation.value, currentTime, automation.duration); } currentTime += automation.duration; }); } function processAutomation(param, { value, mode = 'instant', duration }, currentTime) { switch (mode) { case 'exponential': if (value < 0 || value * param.value < 0) { param.linearRampToValueAtTime(guard(value), currentTime + duration); } else { param.exponentialRampToValueAtTime(guard(value), currentTime + duration); } param.setValueAtTime(guard(value), currentTime + duration); break; case 'instant': param.setValueAtTime(guard(value), currentTime); param.setValueAtTime(guard(value), currentTime + duration); break; case 'linear': param.linearRampToValueAtTime(guard(value), currentTime + duration); break; } } function guard(v) { return v || 0.00000001; } function audioParam(param = '') { const decorator = (target, propertyKey) => { Object.defineProperty(target, propertyKey, { configurable: true, set(value) { value = typeof value === 'string' ? Number.parseFloat(value) : value; const audioParam = this instanceof AudioWorkletNode ? this.parameters.get(propertyKey) : this[param]; if (audioParam instanceof AudioParam) { processAudioParam(audioParam, value, this.context.currentTime); } else { // Fallback for older browsers Object.defineProperty(target, propertyKey, { value, configurable: true, }); } }, }); }; return decorator; } function latencyHintFactory(latencyHint) { return latencyHint === null ? undefined : Number.parseFloat(latencyHint) || latencyHint; } class WebAudioContext extends AudioContext { constructor(latencyHint, sampleRate) { super({ latencyHint: latencyHintFactory(latencyHint), sampleRate: parseInt(sampleRate ?? '', 10) || undefined, }); } ngOnDestroy() { void this.close(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioContext, deps: [{ token: 'latencyHint', attribute: true }, { token: 'sampleRate', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioContext, isStandalone: true, selector: "[waAudioContext]", providers: [ { provide: AUDIO_CONTEXT, useExisting: forwardRef(() => WebAudioContext), }, ], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioContext, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waAudioContext]', providers: [ { provide: AUDIO_CONTEXT, useExisting: forwardRef(() => WebAudioContext), }, ], }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['latencyHint'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['sampleRate'] }] }]; } }); class WebAudioChannel extends GainNode { constructor() { const context = inject(AUDIO_CONTEXT); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); } else { const result = context.createGain(); Object.setPrototypeOf(result, WebAudioChannel.prototype); return result; } } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioChannel, isStandalone: true, selector: "[waChannel]", exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannel, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waChannel]', exportAs: 'AudioNode', }] }], ctorParameters: function () { return []; } }); const WA_AUDIO_NODE = new InjectionToken('[WA_AUDIO_NODE]', { factory: () => null, }); function asAudioNode(useExisting) { return { provide: WA_AUDIO_NODE, useExisting, }; } /** * @deprecated: drop in v5.0, use {@link WA_AUDIO_NODE} */ const AUDIO_NODE = WA_AUDIO_NODE; function connect(source, destination) { if (source && destination) { // @ts-ignore TS does not have a union override for connect method source.connect(destination); } } class WebAudioDestination extends AnalyserNode { quiet; constructor() { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); WebAudioDestination.init(this, node); } else { const result = context.createAnalyser(); Object.setPrototypeOf(result, WebAudioDestination.prototype); WebAudioDestination.init(result, node); return result; } } static init(that, node) { connect(node, that); that.fftSize = 256; that.connect(that.context.destination); that.quiet = interval(POLLING_TIME).pipe(map(() => new Float32Array(that.fftSize)), tap((array) => that.getFloatTimeDomainData(array)), map((array) => that.isSilent(array)), distinctUntilChanged(), skipWhile((isSilent) => isSilent), debounceTime(5000), filter((isSilent) => isSilent)); } ngOnDestroy() { this.disconnect(); } isSilent(array) { return Math.abs(array.reduce((acc, cur) => acc + cur, 0)) < 0.001; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDestination, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioDestination, isStandalone: true, selector: "[waAudioDestinationNode]", outputs: { quiet: "quiet" }, exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDestination, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waAudioDestinationNode]', exportAs: 'AudioNode', }] }], ctorParameters: function () { return []; }, propDecorators: { quiet: [{ type: Output }] } }); function fallbackAudioParam(value) { if (!value) { return 0; } if (typeof value === 'number') { return value; } if (value instanceof Array) { const last = value[value.length - 1]?.value; return typeof last === 'number' ? last : (last?.[last.length - 1] ?? 0); } if (value.value instanceof Array) { return value.value?.[value.value.length - 1] ?? 0; } return value.value; } class WebAudioListener extends GainNode { forwardXParam; forwardYParam; forwardZParam; positionXParam; positionYParam; positionZParam; upXParam; upYParam; upZParam; constructor() { const context = inject(AUDIO_CONTEXT, { self: true }); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); } else { const result = context.createGain(); Object.setPrototypeOf(result, WebAudioListener.prototype); return result; } } get forwardX() { return this.context.listener.forwardX; } get forwardY() { return this.context.listener.forwardY; } get forwardZ() { return this.context.listener.forwardZ; } get positionX() { return this.context.listener.positionX; } get positionY() { return this.context.listener.positionY; } get positionZ() { return this.context.listener.positionZ; } get upX() { return this.context.listener.upX; } get upY() { return this.context.listener.upY; } get upZ() { return this.context.listener.upZ; } ngOnChanges() { if (this.context.listener.positionX instanceof AudioParam) { return; } // Fallback for older browsers this.context.listener.setOrientation(fallbackAudioParam(this.forwardXParam), fallbackAudioParam(this.forwardYParam), fallbackAudioParam(this.forwardZParam), fallbackAudioParam(this.upXParam), fallbackAudioParam(this.upYParam), fallbackAudioParam(this.upZParam)); this.context.listener.setPosition(fallbackAudioParam(this.positionXParam), fallbackAudioParam(this.positionYParam), fallbackAudioParam(this.positionZParam)); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioListener, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioListener, isStandalone: true, selector: "[waAudioContext],[waOfflineAudioContext][length][sampleRate]", inputs: { forwardXParam: ["forwardX", "forwardXParam"], forwardYParam: ["forwardY", "forwardYParam"], forwardZParam: ["forwardZ", "forwardZParam"], positionXParam: ["positionX", "positionXParam"], positionYParam: ["positionY", "positionYParam"], positionZParam: ["positionZ", "positionZParam"], upXParam: ["upX", "upXParam"], upYParam: ["upY", "upYParam"], upZParam: ["upZ", "upZParam"] }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); } __decorate([ audioParam('forwardX') ], WebAudioListener.prototype, "forwardXParam", void 0); __decorate([ audioParam('forwardY') ], WebAudioListener.prototype, "forwardYParam", void 0); __decorate([ audioParam('forwardZ') ], WebAudioListener.prototype, "forwardZParam", void 0); __decorate([ audioParam('positionX') ], WebAudioListener.prototype, "positionXParam", void 0); __decorate([ audioParam('positionY') ], WebAudioListener.prototype, "positionYParam", void 0); __decorate([ audioParam('positionZ') ], WebAudioListener.prototype, "positionZParam", void 0); __decorate([ audioParam('upX') ], WebAudioListener.prototype, "upXParam", void 0); __decorate([ audioParam('upY') ], WebAudioListener.prototype, "upYParam", void 0); __decorate([ audioParam('upZ') ], WebAudioListener.prototype, "upZParam", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioListener, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waAudioContext],[waOfflineAudioContext][length][sampleRate]', }] }], ctorParameters: function () { return []; }, propDecorators: { forwardXParam: [{ type: Input, args: ['forwardX'] }], forwardYParam: [{ type: Input, args: ['forwardY'] }], forwardZParam: [{ type: Input, args: ['forwardZ'] }], positionXParam: [{ type: Input, args: ['positionX'] }], positionYParam: [{ type: Input, args: ['positionY'] }], positionZParam: [{ type: Input, args: ['positionZ'] }], upXParam: [{ type: Input, args: ['upX'] }], upYParam: [{ type: Input, args: ['upY'] }], upZParam: [{ type: Input, args: ['upZ'] }] } }); class WebAudioOfflineContext extends OfflineAudioContext { complete = new EventEmitter(); // eslint-disable-next-line @typescript-eslint/max-params constructor(length, sampleRate, numberOfChannels, autoplay) { super(parseInt(numberOfChannels ?? '', 10) || 1, parseInt(length, 10), parseInt(sampleRate, 10)); if (autoplay !== null) { void this.startRendering(); } } oncomplete = ({ renderedBuffer, }) => this.complete.emit(renderedBuffer); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioOfflineContext, deps: [{ token: 'length', attribute: true }, { token: 'sampleRate', attribute: true }, { token: 'numberOfChannels', attribute: true }, { token: 'autoplay', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioOfflineContext, isStandalone: true, selector: "[waOfflineAudioContext][length][sampleRate]", outputs: { complete: "complete" }, providers: [ { provide: AUDIO_CONTEXT, useExisting: forwardRef(() => WebAudioOfflineContext), }, ], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioOfflineContext, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waOfflineAudioContext][length][sampleRate]', providers: [ { provide: AUDIO_CONTEXT, useExisting: forwardRef(() => WebAudioOfflineContext), }, ], }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['length'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['sampleRate'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['numberOfChannels'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['autoplay'] }] }]; }, propDecorators: { complete: [{ type: Output }] } }); class WebAudioOutput extends GainNode { constructor() { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); connect(node, this); } else { const result = context.createGain(); Object.setPrototypeOf(result, WebAudioOutput.prototype); connect(node, result); return result; } } static init(that, node) { connect(node, that); } set waOutput(destination) { this.disconnect(); connect(this, destination); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioOutput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioOutput, isStandalone: true, selector: "[waOutput]", inputs: { waOutput: "waOutput" }, usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioOutput, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waOutput]', }] }], ctorParameters: function () { return []; }, propDecorators: { waOutput: [{ type: Input }] } }); class WebAudioMediaStreamDestination extends MediaStreamAudioDestinationNode { constructor() { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); connect(node, this); } else { const result = context.createMediaStreamDestination(); Object.setPrototypeOf(result, WebAudioMediaStreamDestination.prototype); connect(node, result); return result; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioMediaStreamDestination, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioMediaStreamDestination, isStandalone: true, selector: "[waMediaStreamAudioDestinationNode]", exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioMediaStreamDestination, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waMediaStreamAudioDestinationNode]', exportAs: 'AudioNode', }] }], ctorParameters: function () { return []; } }); function parse(value, fallback) { const parsed = parseFloat(value || ''); return Number.isNaN(parsed) ? fallback : parsed; } class WebAudioAnalyser extends AnalyserNode { // '!' because it is actually set in constructor frequencyByte$; frequencyFloat$; timeByte$; timeFloat$; // eslint-disable-next-line @typescript-eslint/max-params constructor(fftSizeArg, maxDecibelsArg, minDecibelsArg, smoothingTimeConstantArg) { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); const fftSize = parse(fftSizeArg, 2048); const maxDecibels = parse(maxDecibelsArg, -30); const minDecibels = parse(minDecibelsArg, -100); const smoothingTimeConstant = parse(smoothingTimeConstantArg, 0.8); if (modern) { super(context, { fftSize, maxDecibels, minDecibels, smoothingTimeConstant }); WebAudioAnalyser.init(this, node); } else { const result = context.createAnalyser(); Object.setPrototypeOf(result, WebAudioAnalyser.prototype); WebAudioAnalyser.init(result, node); result.fftSize = fftSize; result.maxDecibels = maxDecibels; result.minDecibels = minDecibels; result.smoothingTimeConstant = smoothingTimeConstant; return result; } } static init(that, node) { connect(node, that); let freqByte = new Uint8Array(that.frequencyBinCount); let freqFloat = new Float32Array(that.frequencyBinCount); let timeByte = new Uint8Array(that.fftSize); let timeFloat = new Float32Array(that.fftSize); that.frequencyByte$ = interval(0, animationFrameScheduler).pipe(map(() => { if (freqByte.length !== that.frequencyBinCount) { freqByte = new Uint8Array(that.frequencyBinCount); } that.getByteFrequencyData(freqByte); return freqByte; }), share()); that.frequencyFloat$ = interval(0, animationFrameScheduler).pipe(map(() => { if (freqFloat.length !== that.frequencyBinCount) { freqFloat = new Float32Array(that.frequencyBinCount); } that.getFloatFrequencyData(freqFloat); return freqFloat; }), share()); that.timeByte$ = interval(0, animationFrameScheduler).pipe(map(() => { if (timeByte.length !== that.fftSize) { timeByte = new Uint8Array(that.frequencyBinCount); } that.getByteTimeDomainData(timeByte); return timeByte; }), share()); that.timeFloat$ = interval(0, animationFrameScheduler).pipe(map(() => { if (timeFloat.length !== that.fftSize) { timeFloat = new Float32Array(that.frequencyBinCount); } that.getFloatTimeDomainData(timeFloat); return timeFloat; }), share()); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioAnalyser, deps: [{ token: 'fftSize', attribute: true }, { token: 'maxDecibels', attribute: true }, { token: 'minDecibels', attribute: true }, { token: 'smoothingTimeConstant', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioAnalyser, isStandalone: true, selector: "[waAnalyserNode]", inputs: { fftSize: "fftSize", minDecibels: "minDecibels", maxDecibels: "maxDecibels", smoothingTimeConstant: "smoothingTimeConstant", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, outputs: { frequencyByte$: "frequencyByte$", frequencyFloat$: "frequencyFloat$", timeByte$: "timeByte$", timeFloat$: "timeFloat$" }, providers: [asAudioNode(WebAudioAnalyser)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioAnalyser, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waAnalyserNode]', inputs: [ 'fftSize', 'minDecibels', 'maxDecibels', 'smoothingTimeConstant', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WebAudioAnalyser)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['fftSize'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['maxDecibels'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['minDecibels'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['smoothingTimeConstant'] }] }]; }, propDecorators: { frequencyByte$: [{ type: Output }], frequencyFloat$: [{ type: Output }], timeByte$: [{ type: Output }], timeFloat$: [{ type: Output }] } }); class WebAudioBiquadFilter extends BiquadFilterNode { detuneParam; frequencyParam; gainParam; qParam; // eslint-disable-next-line @typescript-eslint/max-params constructor(detuneArg, frequencyArg, gainArg, QArg) { const context = inject(AUDIO_CONTEXT); const modern = inject(CONSTRUCTOR_SUPPORT); const node = inject(AUDIO_NODE, { skipSelf: true }); const detune = parse(detuneArg, 0); const frequency = parse(frequencyArg, 350); const gain = parse(gainArg, 0); const Q = parse(QArg, 1); if (modern) { super(context, { detune, frequency, gain, Q }); connect(node, this); } else { const result = context.createBiquadFilter(); Object.setPrototypeOf(result, WebAudioBiquadFilter.prototype); result.detune.value = detune; result.frequency.value = frequency; result.gain.value = gain; result.Q.value = Q; connect(node, result); return result; } } static init(that, node) { connect(node, that); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioBiquadFilter, deps: [{ token: 'detune', attribute: true }, { token: 'frequency', attribute: true }, { token: 'gain', attribute: true }, { token: 'Q', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioBiquadFilter, isStandalone: true, selector: "[waBiquadFilterNode]", inputs: { type: "type", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation", detuneParam: ["detune", "detuneParam"], frequencyParam: ["frequency", "frequencyParam"], gainParam: ["gain", "gainParam"], qParam: ["Q", "qParam"] }, providers: [asAudioNode(WebAudioBiquadFilter)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } __decorate([ audioParam('detune') ], WebAudioBiquadFilter.prototype, "detuneParam", void 0); __decorate([ audioParam('frequency') ], WebAudioBiquadFilter.prototype, "frequencyParam", void 0); __decorate([ audioParam('gain') ], WebAudioBiquadFilter.prototype, "gainParam", void 0); __decorate([ audioParam('Q') ], WebAudioBiquadFilter.prototype, "qParam", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioBiquadFilter, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waBiquadFilterNode]', inputs: ['type', 'channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioBiquadFilter)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['detune'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['frequency'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['gain'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['Q'] }] }]; }, propDecorators: { detuneParam: [{ type: Input, args: ['detune'] }], frequencyParam: [{ type: Input, args: ['frequency'] }], gainParam: [{ type: Input, args: ['gain'] }], qParam: [{ type: Input, args: ['Q'] }] } }); class WebAudioChannelMerger extends ChannelMergerNode { constructor(inputs) { const context = inject(AUDIO_CONTEXT); const modern = inject(CONSTRUCTOR_SUPPORT); const numberOfInputs = parseInt(inputs ?? '', 10) || 6; if (modern) { super(context, { numberOfInputs }); } else { const result = context.createChannelMerger(numberOfInputs); Object.setPrototypeOf(result, WebAudioChannelMerger.prototype); return result; } } ngOnDestroy() { this.disconnect(); } set channels(channels) { channels.forEach((node, index) => { node.connect(this, 0, index); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannelMerger, deps: [{ token: 'numberOfInputs', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioChannelMerger, isStandalone: true, selector: "[waChannelMergerNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WebAudioChannelMerger)], queries: [{ propertyName: "channels", predicate: WebAudioChannel }], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannelMerger, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waChannelMergerNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioChannelMerger)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['numberOfInputs'] }] }]; }, propDecorators: { channels: [{ type: ContentChildren, args: [WebAudioChannel, { descendants: false }] }] } }); class WebAudioChannelSplitter extends ChannelSplitterNode { constructor(outputs) { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); const numberOfOutputs = parseInt(outputs ?? '', 10) || 6; if (modern) { super(context, { numberOfOutputs }); connect(node, this); } else { const result = context.createChannelSplitter(numberOfOutputs); Object.setPrototypeOf(result, WebAudioChannelSplitter.prototype); connect(node, result); return result; } } ngOnDestroy() { this.disconnect(); } set channels(channels) { this.disconnect(); channels .filter((node) => !!node) .forEach((node, index) => { this.connect(node, index); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannelSplitter, deps: [{ token: 'numberOfOutputs', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioChannelSplitter, isStandalone: true, selector: "[waChannelSplitterNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [ { provide: AUDIO_NODE, useValue: null, }, ], queries: [{ propertyName: "channels", predicate: AUDIO_NODE }], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioChannelSplitter, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waChannelSplitterNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [ { provide: AUDIO_NODE, useValue: null, }, ], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['numberOfOutputs'] }] }]; }, propDecorators: { channels: [{ type: ContentChildren, args: [AUDIO_NODE, { descendants: false }] }] } }); class AudioBufferService { context = inject(AUDIO_CONTEXT); cache = new Map(); async fetch(url) { return new Promise((resolve, reject) => { if (this.cache.has(url)) { resolve(this.cache.get(url)); return; } const request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onerror = reject; request.onabort = reject; request.onload = () => { void this.context.decodeAudioData(request.response, (buffer) => { this.cache.set(url, buffer); resolve(buffer); }, reject); }; request.send(); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AudioBufferService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AudioBufferService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AudioBufferService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class WebAudioConvolver extends ConvolverNode { buffer$; constructor() { const audioBufferService = inject(AudioBufferService); const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); if (modern) { super(context); WebAudioConvolver.init(this, node, audioBufferService); } else { const result = context.createConvolver(); Object.setPrototypeOf(result, WebAudioConvolver.prototype); WebAudioConvolver.init(result, node, audioBufferService); return result; } } static init(that, node, audioBufferService) { connect(node, that); that.buffer$ = new Subject(); that.buffer$ .pipe( // eslint-disable-next-line @typescript-eslint/promise-function-async switchMap((source) => typeof source === 'string' ? audioBufferService.fetch(source) : of(source))) .subscribe((buffer) => { that.buffer = buffer; }); } set bufferSetter(source) { this.buffer$.next(source); } ngOnDestroy() { this.buffer$.complete(); this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioConvolver, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioConvolver, isStandalone: true, selector: "[waConvolverNode]", inputs: { normalize: "normalize", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation", bufferSetter: ["buffer", "bufferSetter"] }, providers: [asAudioNode(WebAudioConvolver)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioConvolver, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waConvolverNode]', inputs: ['normalize', 'channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioConvolver)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return []; }, propDecorators: { bufferSetter: [{ type: Input, args: ['buffer'] }] } }); class WebAudioDelay extends DelayNode { delayTimeParam; constructor(delayTimeArg, maxDelayTimeArg) { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); const delayTime = parse(delayTimeArg, 0); const maxDelayTime = parse(maxDelayTimeArg, 1); if (modern) { super(context, { delayTime, maxDelayTime }); connect(node, this); } else { const result = context.createDelay(maxDelayTime); Object.setPrototypeOf(result, WebAudioDelay.prototype); connect(node, result); result.delayTime.value = delayTime; return result; } } static init(that, node) { connect(node, that); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDelay, deps: [{ token: 'delayTime', attribute: true }, { token: 'maxDelayTime', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioDelay, isStandalone: true, selector: "[waDelayNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation", delayTimeParam: ["delayTime", "delayTimeParam"] }, providers: [asAudioNode(WebAudioDelay)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } __decorate([ audioParam('delayTime') ], WebAudioDelay.prototype, "delayTimeParam", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDelay, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waDelayNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioDelay)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['delayTime'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['maxDelayTime'] }] }]; }, propDecorators: { delayTimeParam: [{ type: Input, args: ['delayTime'] }] } }); class WebAudioDynamicsCompressor extends DynamicsCompressorNode { attackParam; kneeParam; ratioParam; releaseParam; thresholdParam; // eslint-disable-next-line @typescript-eslint/max-params constructor(attackArg, kneeArg, ratioArg, releaseArg, thresholdArg) { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); const attack = parse(attackArg, 0.003); const knee = parse(kneeArg, 30); const ratio = parse(ratioArg, 12); const release = parse(releaseArg, 0.25); const threshold = parse(thresholdArg, -24); if (modern) { super(context, { attack, knee, ratio, release, threshold }); connect(node, this); } else { const result = context.createDynamicsCompressor(); Object.setPrototypeOf(result, WebAudioDynamicsCompressor.prototype); connect(node, result); result.attack.value = attack; result.knee.value = knee; result.ratio.value = ratio; result.release.value = release; result.threshold.value = threshold; return result; } } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDynamicsCompressor, deps: [{ token: 'attack', attribute: true }, { token: 'knee', attribute: true }, { token: 'ratio', attribute: true }, { token: 'release', attribute: true }, { token: 'threshold', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioDynamicsCompressor, isStandalone: true, selector: "[waDynamicsCompressorNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation", attackParam: ["attack", "attackParam"], kneeParam: ["knee", "kneeParam"], ratioParam: ["ratio", "ratioParam"], releaseParam: ["release", "releaseParam"], thresholdParam: ["threshold", "thresholdParam"] }, providers: [asAudioNode(WebAudioDynamicsCompressor)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } __decorate([ audioParam('attack') ], WebAudioDynamicsCompressor.prototype, "attackParam", void 0); __decorate([ audioParam('knee') ], WebAudioDynamicsCompressor.prototype, "kneeParam", void 0); __decorate([ audioParam('ratio') ], WebAudioDynamicsCompressor.prototype, "ratioParam", void 0); __decorate([ audioParam('release') ], WebAudioDynamicsCompressor.prototype, "releaseParam", void 0); __decorate([ audioParam('threshold') ], WebAudioDynamicsCompressor.prototype, "thresholdParam", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioDynamicsCompressor, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waDynamicsCompressorNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioDynamicsCompressor)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['attack'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['knee'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['ratio'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['release'] }] }, { type: undefined, decorators: [{ type: Attribute, args: ['threshold'] }] }]; }, propDecorators: { attackParam: [{ type: Input, args: ['attack'] }], kneeParam: [{ type: Input, args: ['knee'] }], ratioParam: [{ type: Input, args: ['ratio'] }], releaseParam: [{ type: Input, args: ['release'] }], thresholdParam: [{ type: Input, args: ['threshold'] }] } }); class WebAudioGain extends GainNode { gainParam; constructor(gainArg) { const context = inject(AUDIO_CONTEXT); const node = inject(AUDIO_NODE, { skipSelf: true }); const modern = inject(CONSTRUCTOR_SUPPORT); const gain = parse(gainArg, 1); if (modern) { super(context, { gain }); connect(node, this); } else { const result = context.createGain(); Object.setPrototypeOf(result, WebAudioGain.prototype); connect(node, result); result.gain.value = gain; return result; } } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioGain, deps: [{ token: 'gain', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioGain, isStandalone: true, selector: "[waGainNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation", gainParam: ["gain", "gainParam"] }, providers: [asAudioNode(WebAudioGain)], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } __decorate([ audioParam('gain') ], WebAudioGain.prototype, "gainParam", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioGain, decorators: [{ type: Directive, args: [{ standalone: true, selector: '[waGainNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WebAudioGain)], exportAs: 'AudioNode', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['gain'] }] }]; }, propDecorators: { gainParam: [{ type: Input, args: ['gain'] }] } }); const WA_FEEDBACK_COEFFICIENTS = new InjectionToken('[WA_FEEDBACK_COEFFICIENTS]'); /** * @deprecated: drop in v5.0, use {@link WA_FEEDBACK_COEFFICIENTS} */ const FEEDBACK_COEFFICIENTS = WA_FEEDBACK_COEFFICIENTS; const WA_FEEDFORWARD_COEFFICIENTS = new InjectionToken('[WA_FEEDFORWARD_COEFFICIENTS]'); /** * @deprecated: drop in v5.0, use {@link WA_FEEDFORWARD_COEFFICIENTS} */ const FEEDFORWARD_COEFFICIENTS = WA_FEEDFORWARD_COEFFICIENTS; class WebAudioIIRFilter extends IIRFilterNode { constructor() { const feedback = inject(FEEDBACK_COEFFICIENTS); const feedforward = inject(FEEDFORWARD_COEFFICIENTS); const context = inject(AUDIO_CONTEXT); const modern = inject(CONSTRUCTOR_SUPPORT); const node = inject(AUDIO_NODE, { skipSelf: true }); if (modern) { super(context, { feedback, feedforward }); connect(node, this); } else { const result = context.createIIRFilter(feedforward, feedback); Object.setPrototypeOf(result, WebAudioIIRFilter.prototype); connect(node, result); return result; } } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WebAudioIIRFilter, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WebAudioIIRFilter, isStandalone: true, selector: "[waIIRFilterNode]", inputs: { channelCount: "channelCount", c