UNPKG

@ng-web-apis/audio

Version:

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

833 lines (802 loc) 64.3 kB
import * as i0 from '@angular/core'; import { InjectionToken, input, inject, HostAttributeToken, Directive, booleanAttribute, output, contentChildren, effect, Injectable, Pipe, ElementRef } from '@angular/core'; import { outputFromObservable } from '@angular/core/rxjs-interop'; import { interval, map, tap, distinctUntilChanged, skipWhile, debounceTime, filter, animationFrameScheduler } from 'rxjs'; const WA_AUDIO_CONTEXT = new InjectionToken(ngDevMode ? '[WA_AUDIO_CONTEXT]' : '', { factory: () => new AudioContext() }); function latencyHintFactory(latencyHint) { return latencyHint === null ? undefined : Number.parseFloat(latencyHint) || latencyHint; } class WaAudioContext extends AudioContext { rate = input('', ...(ngDevMode ? [{ debugName: "rate", alias: 'sampleRate' }] : [{ alias: 'sampleRate' }])); latencyHint = input(...(ngDevMode ? [undefined, { debugName: "latencyHint" }] : [])); constructor() { const sampleRate = inject(new HostAttributeToken('sampleRate'), { optional: true }); const latencyHint = inject(new HostAttributeToken('latencyHint'), { optional: true, }); super({ latencyHint: latencyHintFactory(latencyHint), sampleRate: Number.parseInt(sampleRate ?? '', 10) || undefined, }); } ngOnDestroy() { void this.close(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAudioContext, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.19", type: WaAudioContext, isStandalone: true, selector: "[waAudioContext]", inputs: { rate: { classPropertyName: "rate", publicName: "sampleRate", isSignal: true, isRequired: false, transformFunction: null }, latencyHint: { classPropertyName: "latencyHint", publicName: "latencyHint", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: WA_AUDIO_CONTEXT, useExisting: WaAudioContext }], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAudioContext, decorators: [{ type: Directive, args: [{ selector: '[waAudioContext]', providers: [{ provide: WA_AUDIO_CONTEXT, useExisting: WaAudioContext }], }] }], ctorParameters: () => [], propDecorators: { rate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sampleRate", required: false }] }], latencyHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "latencyHint", required: false }] }] } }); class WaChannel extends GainNode { constructor() { super(inject(WA_AUDIO_CONTEXT)); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaChannel, isStandalone: true, selector: "[waChannel]", exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannel, decorators: [{ type: Directive, args: [{ selector: '[waChannel]', exportAs: 'AudioNode', }] }], ctorParameters: () => [] }); const WA_AUDIO_NODE = new InjectionToken(ngDevMode ? '[WA_AUDIO_NODE]' : '', { factory: () => null }); function asAudioNode(useExisting) { return { provide: WA_AUDIO_NODE, useExisting }; } function connect(source, destination) { if (source && destination) { // @ts-ignore TS does not have a union override for connect method source.connect(destination); } } class WaDestination extends AnalyserNode { quiet = outputFromObservable(interval(100).pipe(map(() => new Float32Array(this.fftSize)), tap((array) => this.getFloatTimeDomainData(array)), map((array) => this.isSilent(array)), distinctUntilChanged(), skipWhile((isSilent) => isSilent), debounceTime(5000), filter((isSilent) => isSilent))); constructor() { super(inject(WA_AUDIO_CONTEXT), { fftSize: 256 }); connect(inject(WA_AUDIO_NODE), this); this.connect(this.context.destination); } 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: "20.3.19", ngImport: i0, type: WaDestination, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaDestination, isStandalone: true, selector: "[waAudioDestinationNode]", outputs: { quiet: "quiet" }, exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaDestination, decorators: [{ type: Directive, args: [{ selector: '[waAudioDestinationNode]', exportAs: 'AudioNode', }] }], ctorParameters: () => [], propDecorators: { quiet: [{ type: i0.Output, args: ["quiet"] }] } }); function audioParam(param, value, currentTime = 0) { value = typeof value === 'string' ? Number.parseFloat(value) : value; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 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, 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; } class WaListener extends GainNode { constructor() { super(inject(WA_AUDIO_CONTEXT, { self: true })); } set forwardXSetter(value) { audioParam(this.context.listener.forwardX, value, this.context.currentTime); } set forwardYSetter(value) { audioParam(this.context.listener.forwardY, value, this.context.currentTime); } set forwardZSetter(value) { audioParam(this.context.listener.forwardZ, value, this.context.currentTime); } set positionXSetter(value) { audioParam(this.context.listener.positionX, value, this.context.currentTime); } set positionYSetter(value) { audioParam(this.context.listener.positionY, value, this.context.currentTime); } set positionZSetter(value) { audioParam(this.context.listener.positionZ, value, this.context.currentTime); } set upXSetter(value) { audioParam(this.context.listener.upX, value, this.context.currentTime); } set upYSetter(value) { audioParam(this.context.listener.upX, value, this.context.currentTime); } set upZSetter(value) { audioParam(this.context.listener.upX, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaListener, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaListener, isStandalone: true, selector: "[waAudioContext],[waOfflineAudioContext][length][sampleRate]", inputs: { forwardXSetter: ["forwardX", "forwardXSetter"], forwardYSetter: ["forwardY", "forwardYSetter"], forwardZSetter: ["forwardZ", "forwardZSetter"], positionXSetter: ["positionX", "positionXSetter"], positionYSetter: ["positionY", "positionYSetter"], positionZSetter: ["positionZ", "positionZSetter"], upXSetter: ["upX", "upXSetter"], upYSetter: ["upY", "upYSetter"], upZSetter: ["upZ", "upZSetter"] }, usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaListener, decorators: [{ type: Directive, args: [{ selector: '[waAudioContext],[waOfflineAudioContext][length][sampleRate]', inputs: [ 'forwardXSetter: forwardX', 'forwardYSetter: forwardY', 'forwardZSetter: forwardZ', 'positionXSetter: positionX', 'positionYSetter: positionY', 'positionZSetter: positionZ', 'upXSetter: upX', 'upYSetter: upY', 'upZSetter: upZ', ], }] }], ctorParameters: () => [] }); class WaNode { node = inject(WA_AUDIO_NODE, { self: true }); parent = inject(WA_AUDIO_NODE, { skipSelf: true }); ngOnInit() { connect(this.parent, this.node); } ngOnDestroy() { this.node?.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaNode, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaNode, isStandalone: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaNode, decorators: [{ type: Directive }] }); class WaOfflineContext extends OfflineAudioContext { lngth = input('', ...(ngDevMode ? [{ debugName: "lngth", alias: 'length' }] : [{ alias: 'length' }])); rate = input('', ...(ngDevMode ? [{ debugName: "rate", alias: 'sampleRate' }] : [{ alias: 'sampleRate' }])); channels = input('', ...(ngDevMode ? [{ debugName: "channels", alias: 'numberOfChannels' }] : [{ alias: 'numberOfChannels' }])); autoplay = input(false, ...(ngDevMode ? [{ debugName: "autoplay", transform: booleanAttribute }] : [{ transform: booleanAttribute }])); complete = output(); constructor() { const length = inject(new HostAttributeToken('length')); const sampleRate = inject(new HostAttributeToken('sampleRate')); const channels = inject(new HostAttributeToken('numberOfChannels'), { optional: true, }); super(Number.parseInt(channels ?? '', 10) || 1, Number.parseInt(length, 10), Number.parseInt(sampleRate, 10)); } oncomplete = (e) => this.complete.emit(e.renderedBuffer); ngOnInit() { if (this.autoplay()) { this.startRendering(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaOfflineContext, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.19", type: WaOfflineContext, isStandalone: true, selector: "[waOfflineAudioContext][length][sampleRate]", inputs: { lngth: { classPropertyName: "lngth", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, rate: { classPropertyName: "rate", publicName: "sampleRate", isSignal: true, isRequired: false, transformFunction: null }, channels: { classPropertyName: "channels", publicName: "numberOfChannels", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { complete: "complete" }, providers: [{ provide: WA_AUDIO_CONTEXT, useExisting: WaOfflineContext }], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaOfflineContext, decorators: [{ type: Directive, args: [{ selector: '[waOfflineAudioContext][length][sampleRate]', providers: [{ provide: WA_AUDIO_CONTEXT, useExisting: WaOfflineContext }], }] }], ctorParameters: () => [], propDecorators: { lngth: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], rate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sampleRate", required: false }] }], channels: [{ type: i0.Input, args: [{ isSignal: true, alias: "numberOfChannels", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], complete: [{ type: i0.Output, args: ["complete"] }] } }); class WaOutput extends GainNode { constructor() { super(inject(WA_AUDIO_CONTEXT)); connect(inject(WA_AUDIO_NODE), this); } set waOutput(destination) { this.disconnect(); connect(this, destination); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaOutput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaOutput, isStandalone: true, selector: "[waOutput]", inputs: { waOutput: "waOutput" }, usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaOutput, decorators: [{ type: Directive, args: [{ selector: '[waOutput]', inputs: ['waOutput'], }] }], ctorParameters: () => [] }); class WaScheduledSource { node = inject(WA_AUDIO_NODE, { self: true }); autoplay = input(false, ...(ngDevMode ? [{ debugName: "autoplay", transform: booleanAttribute }] : [{ transform: booleanAttribute }])); ended = output(); ngOnInit() { this.node.onended = () => this.ended.emit(); if (this.autoplay()) { this.node.start(); } } ngOnDestroy() { try { this.node.stop(); } catch { // noop } this.node.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaScheduledSource, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.19", type: WaScheduledSource, isStandalone: true, inputs: { autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { ended: "ended" }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaScheduledSource, decorators: [{ type: Directive }], propDecorators: { autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], ended: [{ type: i0.Output, args: ["ended"] }] } }); class WaSource { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaSource, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaSource, isStandalone: true, hostDirectives: [{ directive: WaScheduledSource, inputs: ["autoplay", "autoplay"], outputs: ["ended", "ended"] }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaSource, decorators: [{ type: Directive, args: [{ hostDirectives: [ { directive: WaScheduledSource, inputs: ['autoplay'], outputs: ['ended'], }, ], }] }] }); class WaMediaStreamDestination extends MediaStreamAudioDestinationNode { constructor() { super(inject(WA_AUDIO_CONTEXT)); connect(inject(WA_AUDIO_NODE), this); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaMediaStreamDestination, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaMediaStreamDestination, isStandalone: true, selector: "[waMediaStreamAudioDestinationNode]", exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaMediaStreamDestination, decorators: [{ type: Directive, args: [{ selector: '[waMediaStreamAudioDestinationNode]', exportAs: 'AudioNode', }] }], ctorParameters: () => [] }); function parse(value, fallback) { const parsed = Number.parseFloat(value || ''); return Number.isNaN(parsed) ? fallback : parsed; } class WaAnalyser extends AnalyserNode { fByte = new Uint8Array(this.frequencyBinCount); fFloat = new Float32Array(this.frequencyBinCount); tByte = new Uint8Array(this.fftSize); tFloat = new Float32Array(this.fftSize); frequencyByte = outputFromObservable(interval(0, animationFrameScheduler).pipe(map(() => { if (this.fByte.length !== this.frequencyBinCount) { this.fByte = new Uint8Array(this.frequencyBinCount); } this.getByteFrequencyData(this.fByte); return this.fByte; }))); frequencyFloat = outputFromObservable(interval(0, animationFrameScheduler).pipe(map(() => { if (this.fFloat.length !== this.frequencyBinCount) { this.fFloat = new Float32Array(this.frequencyBinCount); } this.getFloatFrequencyData(this.fFloat); return this.fFloat; }))); timeByte = outputFromObservable(interval(0, animationFrameScheduler).pipe(map(() => { if (this.tByte.length !== this.fftSize) { this.tByte = new Uint8Array(this.frequencyBinCount); } this.getByteTimeDomainData(this.tByte); return this.tByte; }))); timeFloat = outputFromObservable(interval(0, animationFrameScheduler).pipe(map(() => { if (this.tFloat.length !== this.fftSize) { this.tFloat = new Float32Array(this.frequencyBinCount); } this.getFloatTimeDomainData(this.tFloat); return this.tFloat; }))); constructor() { const O = { optional: true }; const fftSize = inject(new HostAttributeToken('fftSize'), O); const minDecibels = inject(new HostAttributeToken('minDecibels'), O); const maxDecibels = inject(new HostAttributeToken('maxDecibels'), O); const time = inject(new HostAttributeToken('smoothingTimeConstant'), O); super(inject(WA_AUDIO_CONTEXT), { fftSize: parse(fftSize, 2048), maxDecibels: parse(maxDecibels, -30), minDecibels: parse(minDecibels, -100), smoothingTimeConstant: parse(time, 0.8), }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAnalyser, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaAnalyser, 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(WaAnalyser)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAnalyser, decorators: [{ type: Directive, args: [{ selector: '[waAnalyserNode]', inputs: [ 'fftSize', 'minDecibels', 'maxDecibels', 'smoothingTimeConstant', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaAnalyser)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [], propDecorators: { frequencyByte: [{ type: i0.Output, args: ["frequencyByte"] }], frequencyFloat: [{ type: i0.Output, args: ["frequencyFloat"] }], timeByte: [{ type: i0.Output, args: ["timeByte"] }], timeFloat: [{ type: i0.Output, args: ["timeFloat"] }] } }); class WaBiquadFilter extends BiquadFilterNode { constructor() { const detune = inject(new HostAttributeToken('detune'), { optional: true }); const frequency = inject(new HostAttributeToken('frequency'), { optional: true }); const gain = inject(new HostAttributeToken('gain'), { optional: true }); const Q = inject(new HostAttributeToken('Q'), { optional: true }); super(inject(WA_AUDIO_CONTEXT), { detune: parse(detune, 0), frequency: parse(frequency, 350), gain: parse(gain, 0), Q: parse(Q, 1), }); } set detuneSetter(value) { audioParam(this.detune, value, this.context.currentTime); } set frequencySetter(value) { audioParam(this.frequency, value, this.context.currentTime); } set gainSetter(value) { audioParam(this.gain, value, this.context.currentTime); } set qSetter(value) { audioParam(this.Q, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaBiquadFilter, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaBiquadFilter, isStandalone: true, selector: "[waBiquadFilterNode]", inputs: { detuneSetter: ["detune", "detuneSetter"], frequencySetter: ["frequency", "frequencySetter"], gainSetter: ["gain", "gainSetter"], qSetter: ["Q", "qSetter"], type: "type", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaBiquadFilter)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaBiquadFilter, decorators: [{ type: Directive, args: [{ selector: '[waBiquadFilterNode]', inputs: [ 'detuneSetter: detune', 'frequencySetter: frequency', 'gainSetter: gain', 'qSetter: Q', 'type', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaBiquadFilter)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaChannelMerger extends ChannelMergerNode { channels = contentChildren(WaChannel, ...(ngDevMode ? [{ debugName: "channels", descendants: false }] : [{ descendants: false }])); inputs = input('', ...(ngDevMode ? [{ debugName: "inputs", alias: 'numberOfInputs' }] : [{ alias: 'numberOfInputs' }])); constructor() { const attr = inject(new HostAttributeToken('numberOfInputs'), { optional: true }); const numberOfInputs = Number.parseInt(attr || '', 10) || 6; super(inject(WA_AUDIO_CONTEXT), { numberOfInputs }); effect(() => { this.channels().forEach((node, index) => { node.connect(this, 0, index); }); }); } ngOnDestroy() { this.disconnect(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannelMerger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.19", type: WaChannelMerger, isStandalone: true, selector: "[waChannelMergerNode]", inputs: { channelCount: { classPropertyName: "channelCount", publicName: "channelCount", isSignal: false, isRequired: false, transformFunction: null }, channelCountMode: { classPropertyName: "channelCountMode", publicName: "channelCountMode", isSignal: false, isRequired: false, transformFunction: null }, channelInterpretation: { classPropertyName: "channelInterpretation", publicName: "channelInterpretation", isSignal: false, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "numberOfInputs", isSignal: true, isRequired: false, transformFunction: null } }, providers: [asAudioNode(WaChannelMerger)], queries: [{ propertyName: "channels", predicate: WaChannel, isSignal: true }], exportAs: ["AudioNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannelMerger, decorators: [{ type: Directive, args: [{ selector: '[waChannelMergerNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WaChannelMerger)], exportAs: 'AudioNode', }] }], ctorParameters: () => [], propDecorators: { channels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WaChannel), { ...{ descendants: false }, isSignal: true }] }], inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "numberOfInputs", required: false }] }] } }); class WaChannelSplitter extends ChannelSplitterNode { channels = contentChildren(WA_AUDIO_NODE, ...(ngDevMode ? [{ debugName: "channels", descendants: false }] : [{ descendants: false }])); outputs = input('', ...(ngDevMode ? [{ debugName: "outputs", alias: 'numberOfOutputs' }] : [{ alias: 'numberOfOutputs' }])); constructor() { const attr = inject(new HostAttributeToken('numberOfOutputs'), { optional: true }); const numberOfOutputs = Number.parseInt(attr || '', 10) || 6; super(inject(WA_AUDIO_CONTEXT), { numberOfOutputs }); effect(() => { this.disconnect(); this.channels() .filter((node) => !!node) .forEach((node, index) => { this.connect(node, index); }); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannelSplitter, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.19", type: WaChannelSplitter, isStandalone: true, selector: "[waChannelSplitterNode]", inputs: { channelCount: { classPropertyName: "channelCount", publicName: "channelCount", isSignal: false, isRequired: false, transformFunction: null }, channelCountMode: { classPropertyName: "channelCountMode", publicName: "channelCountMode", isSignal: false, isRequired: false, transformFunction: null }, channelInterpretation: { classPropertyName: "channelInterpretation", publicName: "channelInterpretation", isSignal: false, isRequired: false, transformFunction: null }, outputs: { classPropertyName: "outputs", publicName: "numberOfOutputs", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: WA_AUDIO_NODE, useValue: null }], queries: [{ propertyName: "channels", predicate: WA_AUDIO_NODE, isSignal: true }], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaChannelSplitter, decorators: [{ type: Directive, args: [{ selector: '[waChannelSplitterNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [{ provide: WA_AUDIO_NODE, useValue: null }], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [], propDecorators: { channels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WA_AUDIO_NODE), { ...{ descendants: false }, isSignal: true }] }], outputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "numberOfOutputs", required: false }] }] } }); class WaAudioBufferService { context = inject(WA_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: "20.3.19", ngImport: i0, type: WaAudioBufferService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAudioBufferService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaAudioBufferService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class WaConvolver extends ConvolverNode { service = inject(WaAudioBufferService); constructor() { super(inject(WA_AUDIO_CONTEXT)); } set bufferSetter(source) { if (typeof source === 'string') { this.service.fetch(source).then((result) => { this.buffer = result; }); } else { this.buffer = source; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaConvolver, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaConvolver, isStandalone: true, selector: "[waConvolverNode]", inputs: { bufferSetter: ["buffer", "bufferSetter"], normalize: "normalize", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaConvolver)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaConvolver, decorators: [{ type: Directive, args: [{ selector: '[waConvolverNode]', inputs: [ 'bufferSetter: buffer', 'normalize', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaConvolver)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaDelay extends DelayNode { maxDelayTime = input('', ...(ngDevMode ? [{ debugName: "maxDelayTime" }] : [])); constructor() { const delayTime = inject(new HostAttributeToken('delayTime'), { optional: true }); const maxDelayTime = inject(new HostAttributeToken('maxDelayTime'), { optional: true, }); super(inject(WA_AUDIO_CONTEXT), { delayTime: parse(delayTime, 0), maxDelayTime: parse(maxDelayTime, 1), }); } set delayTimeSetter(value) { audioParam(this.delayTime, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaDelay, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.19", type: WaDelay, isStandalone: true, selector: "[waDelayNode]", inputs: { delayTimeSetter: { classPropertyName: "delayTimeSetter", publicName: "delayTime", isSignal: false, isRequired: false, transformFunction: null }, channelCount: { classPropertyName: "channelCount", publicName: "channelCount", isSignal: false, isRequired: false, transformFunction: null }, channelCountMode: { classPropertyName: "channelCountMode", publicName: "channelCountMode", isSignal: false, isRequired: false, transformFunction: null }, channelInterpretation: { classPropertyName: "channelInterpretation", publicName: "channelInterpretation", isSignal: false, isRequired: false, transformFunction: null }, maxDelayTime: { classPropertyName: "maxDelayTime", publicName: "maxDelayTime", isSignal: true, isRequired: false, transformFunction: null } }, providers: [asAudioNode(WaDelay)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaDelay, decorators: [{ type: Directive, args: [{ selector: '[waDelayNode]', inputs: [ 'delayTimeSetter: delayTime', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaDelay)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [], propDecorators: { maxDelayTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDelayTime", required: false }] }] } }); class WaDynamicsCompressor extends DynamicsCompressorNode { constructor() { const attack = inject(new HostAttributeToken('attack'), { optional: true }); const knee = inject(new HostAttributeToken('knee'), { optional: true }); const ratio = inject(new HostAttributeToken('ratio'), { optional: true }); const release = inject(new HostAttributeToken('release'), { optional: true }); const threshold = inject(new HostAttributeToken('threshold'), { optional: true }); super(inject(WA_AUDIO_CONTEXT), { attack: parse(attack, 0.003), knee: parse(knee, 30), ratio: parse(ratio, 12), release: parse(release, 0.25), threshold: parse(threshold, -24), }); } set attackSetter(value) { audioParam(this.attack, value, this.context.currentTime); } set kneeSetter(value) { audioParam(this.knee, value, this.context.currentTime); } set ratioSetter(value) { audioParam(this.ratio, value, this.context.currentTime); } set releaseSetter(value) { audioParam(this.release, value, this.context.currentTime); } set thresholdSetter(value) { audioParam(this.threshold, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaDynamicsCompressor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaDynamicsCompressor, isStandalone: true, selector: "[waDynamicsCompressorNode]", inputs: { attackSetter: ["attack", "attackSetter"], kneeSetter: ["knee", "kneeSetter"], ratioSetter: ["ratio", "ratioSetter"], releaseSetter: ["release", "releaseSetter"], thresholdSetter: ["threshold", "thresholdSetter"], channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaDynamicsCompressor)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaDynamicsCompressor, decorators: [{ type: Directive, args: [{ selector: '[waDynamicsCompressorNode]', inputs: [ 'attackSetter: attack', 'kneeSetter: knee', 'ratioSetter: ratio', 'releaseSetter: release', 'thresholdSetter: threshold', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaDynamicsCompressor)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaGain extends GainNode { constructor() { const gain = inject(new HostAttributeToken('gain'), { optional: true }); super(inject(WA_AUDIO_CONTEXT), { gain: parse(gain, 1) }); } set gainSetter(value) { audioParam(this.gain, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaGain, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaGain, isStandalone: true, selector: "[waGainNode]", inputs: { gainSetter: ["gain", "gainSetter"], channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaGain)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaGain, decorators: [{ type: Directive, args: [{ selector: '[waGainNode]', inputs: [ 'gainSetter: gain', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaGain)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); const WA_FEEDBACK_COEFFICIENTS = new InjectionToken(ngDevMode ? '[WA_FEEDBACK_COEFFICIENTS]' : ''); const WA_FEEDFORWARD_COEFFICIENTS = new InjectionToken(ngDevMode ? '[WA_FEEDFORWARD_COEFFICIENTS]' : ''); class WaIIRFilter extends IIRFilterNode { constructor() { const feedback = inject(WA_FEEDBACK_COEFFICIENTS); const feedforward = inject(WA_FEEDFORWARD_COEFFICIENTS); super(inject(WA_AUDIO_CONTEXT), { feedback, feedforward }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaIIRFilter, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaIIRFilter, isStandalone: true, selector: "[waIIRFilterNode]", inputs: { channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaIIRFilter)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaIIRFilter, decorators: [{ type: Directive, args: [{ selector: '[waIIRFilterNode]', inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'], providers: [asAudioNode(WaIIRFilter)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaPanner extends PannerNode { constructor() { super(inject(WA_AUDIO_CONTEXT)); } set orientationXSetter(value) { audioParam(this.orientationX, value, this.context.currentTime); } set orientationYSetter(value) { audioParam(this.orientationY, value, this.context.currentTime); } set orientationZSetter(value) { audioParam(this.orientationZ, value, this.context.currentTime); } set positionXSetter(value) { audioParam(this.positionX, value, this.context.currentTime); } set positionYSetter(value) { audioParam(this.positionY, value, this.context.currentTime); } set positionZSetter(value) { audioParam(this.positionZ, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaPanner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaPanner, isStandalone: true, selector: "[waPannerNode]", inputs: { orientationXSetter: ["orientationX", "orientationXSetter"], orientationYSetter: ["orientationY", "orientationYSetter"], orientationZSetter: ["orientationZ", "orientationZSetter"], positionXSetter: ["positionX", "positionXSetter"], positionYSetter: ["positionY", "positionYSetter"], positionZSetter: ["positionZ", "positionZSetter"], coneInnerAngle: "coneInnerAngle", coneOuterAngle: "coneOuterAngle", coneOuterGain: "coneOuterGain", distanceModel: "distanceModel", maxDistance: "maxDistance", panningModel: "panningModel", refDistance: "refDistance", rolloffFactor: "rolloffFactor", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaPanner)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaPanner, decorators: [{ type: Directive, args: [{ selector: '[waPannerNode]', inputs: [ 'orientationXSetter: orientationX', 'orientationYSetter: orientationY', 'orientationZSetter: orientationZ', 'positionXSetter: positionX', 'positionYSetter: positionY', 'positionZSetter: positionZ', 'coneInnerAngle', 'coneOuterAngle', 'coneOuterGain', 'distanceModel', 'maxDistance', 'panningModel', 'refDistance', 'rolloffFactor', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaPanner)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaStereoPanner extends StereoPannerNode { constructor() { const pan = inject(new HostAttributeToken('pan'), { optional: true }); super(inject(WA_AUDIO_CONTEXT), { pan: parse(pan, 0) }); } set panSetter(value) { audioParam(this.pan, value, this.context.currentTime); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaStereoPanner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaStereoPanner, isStandalone: true, selector: "[waStereoPannerNode]", inputs: { panSetter: ["pan", "panSetter"], channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaStereoPanner)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaStereoPanner, decorators: [{ type: Directive, args: [{ selector: '[waStereoPannerNode]', inputs: [ 'panSetter: pan', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaStereoPanner)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); class WaWaveShaper extends WaveShaperNode { constructor() { super(inject(WA_AUDIO_CONTEXT)); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaWaveShaper, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: WaWaveShaper, isStandalone: true, selector: "[waWaveShaperNode]", inputs: { oversample: "oversample", curve: "curve", channelCount: "channelCount", channelCountMode: "channelCountMode", channelInterpretation: "channelInterpretation" }, providers: [asAudioNode(WaWaveShaper)], exportAs: ["AudioNode"], usesInheritance: true, hostDirectives: [{ directive: WaNode }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaWaveShaper, decorators: [{ type: Directive, args: [{ selector: '[waWaveShaperNode]', inputs: [ 'oversample', 'curve', 'channelCount', 'channelCountMode', 'channelInterpretation', ], providers: [asAudioNode(WaWaveShaper)], exportAs: 'AudioNode', hostDirectives: [WaNode], }] }], ctorParameters: () => [] }); // TODO: Add AudioWorkletNodeOptions class WaWorklet extends AudioWorkletNode { name = input('', ...(ngDevMode ? [{ debugName: "name" }] : [])); processorerror = output(); constructor() { super(inject(WA_AUDIO_CONTEXT), inject(new HostAttributeToken('name'))); } onprocessorerror = () => this.processorerror.emit(); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WaWorklet, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.19", type: WaWorklet, isStandalone: true, selector: "[waAudioWorkletNode][name]", inputs: { channelCount: { classPropertyName: "channelCount", publicName: "channelCount", isSignal: false, isRequired: false, transformFunction: null }, channelCountMode: { classPropertyName: "channelCountMode", publicName: "channelCountMode", isSignal: false, isRequired: false, transformFunction: null }, channelInterpretation: { classPropertyName: "channelInterpretation", publicName: "channelInterpretation", isSignal: false, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: