@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
JavaScript
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