UNPKG

@ng-web-apis/audio

Version:

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

1 lines 90.3 kB
{"version":3,"file":"ng-web-apis-audio.mjs","sources":["../../../libs/audio/src/tokens/audio-context.ts","../../../libs/audio/src/utils/latency-hint-factory.ts","../../../libs/audio/src/directives/audio-context.ts","../../../libs/audio/src/directives/channel.ts","../../../libs/audio/src/tokens/audio-node.ts","../../../libs/audio/src/utils/connect.ts","../../../libs/audio/src/directives/destination.ts","../../../libs/audio/src/utils/audio-param.ts","../../../libs/audio/src/directives/listener.ts","../../../libs/audio/src/directives/node.ts","../../../libs/audio/src/directives/offline-audio-context.ts","../../../libs/audio/src/directives/output.ts","../../../libs/audio/src/directives/source.ts","../../../libs/audio/src/directives/stream-destination.ts","../../../libs/audio/src/utils/parse.ts","../../../libs/audio/src/nodes/analyser.ts","../../../libs/audio/src/nodes/biquad-filter.ts","../../../libs/audio/src/nodes/channel-merger.ts","../../../libs/audio/src/nodes/channel-splitter.ts","../../../libs/audio/src/services/audio-buffer.service.ts","../../../libs/audio/src/nodes/convolver.ts","../../../libs/audio/src/nodes/delay.ts","../../../libs/audio/src/nodes/dynamics-compressor.ts","../../../libs/audio/src/nodes/gain.ts","../../../libs/audio/src/tokens/feedback-coefficients.ts","../../../libs/audio/src/tokens/feedforward-coefficients.ts","../../../libs/audio/src/nodes/iir-filter.ts","../../../libs/audio/src/nodes/panner.ts","../../../libs/audio/src/nodes/stereo-panner.ts","../../../libs/audio/src/nodes/wave-shaper.ts","../../../libs/audio/src/nodes/worklet.ts","../../../libs/audio/src/pipes/audio-param.pipe.ts","../../../libs/audio/src/pipes/periodic-wave.pipe.ts","../../../libs/audio/src/sources/buffer-source.ts","../../../libs/audio/src/sources/constant-source.ts","../../../libs/audio/src/sources/media-source.ts","../../../libs/audio/src/tokens/media-stream.ts","../../../libs/audio/src/sources/media-stream-source.ts","../../../libs/audio/src/sources/oscillator.ts","../../../libs/audio/src/tokens/audio-worklet-processors.ts","../../../libs/audio/src/tokens/audio-worklet-processors-ready.ts","../../../libs/audio/src/tokens/support.ts","../../../libs/audio/src/web-audio.ts","../../../libs/audio/src/ng-web-apis-audio.ts"],"sourcesContent":["import {InjectionToken} from '@angular/core';\n\nexport const WA_AUDIO_CONTEXT = new InjectionToken<BaseAudioContext>(\n ngDevMode ? '[WA_AUDIO_CONTEXT]' : '',\n {factory: () => new AudioContext()},\n);\n","export function latencyHintFactory(\n latencyHint: AudioContextLatencyCategory | null,\n): AudioContextLatencyCategory | number | undefined {\n return latencyHint === null\n ? undefined\n : Number.parseFloat(latencyHint) || latencyHint;\n}\n","import {\n Directive,\n HostAttributeToken,\n inject,\n input,\n type OnDestroy,\n} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {latencyHintFactory} from '../utils/latency-hint-factory';\n\n@Directive({\n selector: '[waAudioContext]',\n providers: [{provide: WA_AUDIO_CONTEXT, useExisting: WaAudioContext}],\n})\nexport class WaAudioContext extends AudioContext implements OnDestroy {\n public readonly rate = input('', {alias: 'sampleRate'});\n public readonly latencyHint = input<AudioContextLatencyCategory>();\n\n constructor() {\n const sampleRate = inject(new HostAttributeToken('sampleRate'), {optional: true});\n\n const latencyHint: any = inject(new HostAttributeToken('latencyHint'), {\n optional: true,\n });\n\n super({\n latencyHint: latencyHintFactory(latencyHint),\n sampleRate: Number.parseInt(sampleRate ?? '', 10) || undefined,\n });\n }\n\n public ngOnDestroy(): void {\n void this.close();\n }\n}\n","import {Directive, inject, type OnDestroy} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\n\n@Directive({\n selector: '[waChannel]',\n exportAs: 'AudioNode',\n})\nexport class WaChannel extends GainNode implements OnDestroy {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT));\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n}\n","import {type ExistingProvider, InjectionToken, type Type} from '@angular/core';\n\nexport const WA_AUDIO_NODE = new InjectionToken<AudioNode | null>(\n ngDevMode ? '[WA_AUDIO_NODE]' : '',\n {factory: () => null},\n);\n\nexport function asAudioNode(useExisting: Type<AudioNode>): ExistingProvider {\n return {provide: WA_AUDIO_NODE, useExisting};\n}\n","export function connect(\n source?: AudioNode | null,\n destination?: AudioNode | AudioParam | null,\n): void {\n if (source && destination) {\n // @ts-ignore TS does not have a union override for connect method\n source.connect(destination);\n }\n}\n","import {Directive, inject, type OnDestroy} from '@angular/core';\nimport {outputFromObservable} from '@angular/core/rxjs-interop';\nimport {\n debounceTime,\n distinctUntilChanged,\n filter,\n interval,\n map,\n skipWhile,\n tap,\n} from 'rxjs';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\nimport {connect} from '../utils/connect';\n\n@Directive({\n selector: '[waAudioDestinationNode]',\n exportAs: 'AudioNode',\n})\nexport class WaDestination extends AnalyserNode implements OnDestroy {\n public readonly quiet = outputFromObservable<unknown>(\n interval(100).pipe(\n map(() => new Float32Array(this.fftSize)),\n tap((array) => this.getFloatTimeDomainData(array)),\n map((array) => this.isSilent(array)),\n distinctUntilChanged(),\n skipWhile((isSilent) => isSilent),\n debounceTime(5000),\n filter((isSilent) => isSilent),\n ),\n );\n\n constructor() {\n super(inject(WA_AUDIO_CONTEXT), {fftSize: 256});\n\n connect(inject(WA_AUDIO_NODE), this);\n this.connect(this.context.destination);\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n\n private isSilent(array: Float32Array): boolean {\n return Math.abs(array.reduce((acc, cur) => acc + cur, 0)) < 0.001;\n }\n}\n","import {type AudioParamAutomation} from '../types/audio-param-automation';\nimport {type AudioParamCurve} from '../types/audio-param-curve';\nimport {type AudioParamInput} from '../types/audio-param-input';\n\nexport function audioParam(\n param: AudioParam,\n value: AudioParamInput,\n currentTime = 0,\n): void {\n value = typeof value === 'string' ? Number.parseFloat(value) : value;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (param.cancelAndHoldAtTime) {\n param.cancelAndHoldAtTime(currentTime);\n } else {\n param.cancelScheduledValues(currentTime);\n param.setValueAtTime(guard(param.value), currentTime);\n }\n\n if (typeof value === 'number') {\n param.setValueAtTime(guard(value), currentTime);\n\n return;\n }\n\n if (value instanceof Array) {\n processSchedule(param, value, currentTime);\n\n return;\n }\n\n if (!('mode' in value)) {\n param.setValueCurveAtTime(value.value, currentTime, value.duration);\n\n return;\n }\n\n param.setValueAtTime(guard(param.value), currentTime);\n processAutomation(param, value, currentTime);\n}\n\nfunction processSchedule(\n param: AudioParam,\n value: Array<AudioParamAutomation | AudioParamCurve>,\n currentTime: number,\n): void {\n value.forEach((automation) => {\n if ('mode' in automation) {\n processAutomation(param, automation, currentTime);\n } else {\n param.setValueCurveAtTime(automation.value, currentTime, automation.duration);\n }\n\n currentTime += automation.duration;\n });\n}\n\nfunction processAutomation(\n param: AudioParam,\n {value, mode, duration}: AudioParamAutomation,\n currentTime: number,\n): void {\n switch (mode) {\n case 'exponential':\n if (value < 0 || value * param.value < 0) {\n param.linearRampToValueAtTime(guard(value), currentTime + duration);\n } else {\n param.exponentialRampToValueAtTime(guard(value), currentTime + duration);\n }\n\n param.setValueAtTime(guard(value), currentTime + duration);\n break;\n case 'instant':\n param.setValueAtTime(guard(value), currentTime);\n param.setValueAtTime(guard(value), currentTime + duration);\n break;\n case 'linear':\n param.linearRampToValueAtTime(guard(value), currentTime + duration);\n break;\n }\n}\n\nfunction guard(v: number): number {\n return v || 0.00000001;\n}\n","import {Directive, inject} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\n\n@Directive({\n selector: '[waAudioContext],[waOfflineAudioContext][length][sampleRate]',\n inputs: [\n 'forwardXSetter: forwardX',\n 'forwardYSetter: forwardY',\n 'forwardZSetter: forwardZ',\n 'positionXSetter: positionX',\n 'positionYSetter: positionY',\n 'positionZSetter: positionZ',\n 'upXSetter: upX',\n 'upYSetter: upY',\n 'upZSetter: upZ',\n ],\n})\nexport class WaListener extends GainNode {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT, {self: true}));\n }\n\n public set forwardXSetter(value: AudioParamInput) {\n audioParam(this.context.listener.forwardX, value, this.context.currentTime);\n }\n\n public set forwardYSetter(value: AudioParamInput) {\n audioParam(this.context.listener.forwardY, value, this.context.currentTime);\n }\n\n public set forwardZSetter(value: AudioParamInput) {\n audioParam(this.context.listener.forwardZ, value, this.context.currentTime);\n }\n\n public set positionXSetter(value: AudioParamInput) {\n audioParam(this.context.listener.positionX, value, this.context.currentTime);\n }\n\n public set positionYSetter(value: AudioParamInput) {\n audioParam(this.context.listener.positionY, value, this.context.currentTime);\n }\n\n public set positionZSetter(value: AudioParamInput) {\n audioParam(this.context.listener.positionZ, value, this.context.currentTime);\n }\n\n public set upXSetter(value: AudioParamInput) {\n audioParam(this.context.listener.upX, value, this.context.currentTime);\n }\n\n public set upYSetter(value: AudioParamInput) {\n audioParam(this.context.listener.upX, value, this.context.currentTime);\n }\n\n public set upZSetter(value: AudioParamInput) {\n audioParam(this.context.listener.upX, value, this.context.currentTime);\n }\n}\n","import {Directive, inject, type OnDestroy, type OnInit} from '@angular/core';\n\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\nimport {connect} from '../utils/connect';\n\n@Directive()\nexport class WaNode implements OnInit, OnDestroy {\n private readonly node = inject(WA_AUDIO_NODE, {self: true});\n private readonly parent = inject(WA_AUDIO_NODE, {skipSelf: true});\n\n public ngOnInit(): void {\n connect(this.parent, this.node);\n }\n\n public ngOnDestroy(): void {\n this.node?.disconnect();\n }\n}\n","import {\n booleanAttribute,\n Directive,\n HostAttributeToken,\n inject,\n input,\n type OnInit,\n output,\n} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\n\n@Directive({\n selector: '[waOfflineAudioContext][length][sampleRate]',\n providers: [{provide: WA_AUDIO_CONTEXT, useExisting: WaOfflineContext}],\n})\nexport class WaOfflineContext extends OfflineAudioContext implements OnInit {\n public readonly lngth = input('', {alias: 'length'});\n public readonly rate = input('', {alias: 'sampleRate'});\n public readonly channels = input('', {alias: 'numberOfChannels'});\n public readonly autoplay = input(false, {transform: booleanAttribute});\n public readonly complete = output<AudioBuffer>();\n\n constructor() {\n const length = inject(new HostAttributeToken('length'));\n const sampleRate = inject(new HostAttributeToken('sampleRate'));\n\n const channels = inject(new HostAttributeToken('numberOfChannels'), {\n optional: true,\n });\n\n super(\n Number.parseInt(channels ?? '', 10) || 1,\n Number.parseInt(length, 10),\n Number.parseInt(sampleRate, 10),\n );\n }\n\n public override readonly oncomplete = (e: OfflineAudioCompletionEvent): void =>\n this.complete.emit(e.renderedBuffer);\n\n public ngOnInit(): void {\n if (this.autoplay()) {\n this.startRendering();\n }\n }\n}\n","import {Directive, inject, type OnDestroy} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\nimport {connect} from '../utils/connect';\n\n@Directive({\n selector: '[waOutput]',\n inputs: ['waOutput'],\n})\nexport class WaOutput extends GainNode implements OnDestroy {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT));\n\n connect(inject(WA_AUDIO_NODE), this);\n }\n\n public set waOutput(destination: AudioNode | AudioParam | undefined) {\n this.disconnect();\n connect(this, destination);\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n}\n","import {\n booleanAttribute,\n Directive,\n inject,\n input,\n type OnDestroy,\n type OnInit,\n output,\n} from '@angular/core';\n\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\n\n@Directive()\nexport class WaScheduledSource implements OnInit, OnDestroy {\n private readonly node = inject<AudioScheduledSourceNode>(WA_AUDIO_NODE, {self: true});\n public readonly autoplay = input(false, {transform: booleanAttribute});\n public readonly ended = output();\n\n public ngOnInit(): void {\n this.node.onended = (): void => this.ended.emit();\n\n if (this.autoplay()) {\n this.node.start();\n }\n }\n\n public ngOnDestroy(): void {\n try {\n this.node.stop();\n } catch {\n // noop\n }\n\n this.node.disconnect();\n }\n}\n\n@Directive({\n hostDirectives: [\n {\n directive: WaScheduledSource,\n inputs: ['autoplay'],\n outputs: ['ended'],\n },\n ],\n})\nexport class WaSource {}\n","import {Directive, inject} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\nimport {connect} from '../utils/connect';\n\n@Directive({\n selector: '[waMediaStreamAudioDestinationNode]',\n exportAs: 'AudioNode',\n})\nexport class WaMediaStreamDestination extends MediaStreamAudioDestinationNode {\n constructor() {\n super(inject<AudioContext>(WA_AUDIO_CONTEXT));\n\n connect(inject(WA_AUDIO_NODE), this);\n }\n}\n","import {type AudioParamInput} from '../types/audio-param-input';\n\nexport function parse(value: AudioParamInput | string | null, fallback: number): number {\n const parsed = Number.parseFloat((value as string) || '');\n\n return Number.isNaN(parsed) ? fallback : parsed;\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\nimport {outputFromObservable} from '@angular/core/rxjs-interop';\nimport {animationFrameScheduler, interval, map} from 'rxjs';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waAnalyserNode]',\n inputs: [\n 'fftSize',\n 'minDecibels',\n 'maxDecibels',\n 'smoothingTimeConstant',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaAnalyser)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaAnalyser extends AnalyserNode {\n private fByte = new Uint8Array(this.frequencyBinCount);\n private fFloat = new Float32Array(this.frequencyBinCount);\n private tByte = new Uint8Array(this.fftSize);\n private tFloat = new Float32Array(this.fftSize);\n\n public readonly frequencyByte = outputFromObservable(\n interval(0, animationFrameScheduler).pipe(\n map(() => {\n if (this.fByte.length !== this.frequencyBinCount) {\n this.fByte = new Uint8Array(this.frequencyBinCount);\n }\n\n this.getByteFrequencyData(this.fByte);\n\n return this.fByte;\n }),\n ),\n );\n\n public readonly frequencyFloat = outputFromObservable(\n interval(0, animationFrameScheduler).pipe(\n map(() => {\n if (this.fFloat.length !== this.frequencyBinCount) {\n this.fFloat = new Float32Array(this.frequencyBinCount);\n }\n\n this.getFloatFrequencyData(this.fFloat);\n\n return this.fFloat;\n }),\n ),\n );\n\n public readonly timeByte = outputFromObservable(\n interval(0, animationFrameScheduler).pipe(\n map(() => {\n if (this.tByte.length !== this.fftSize) {\n this.tByte = new Uint8Array(this.frequencyBinCount);\n }\n\n this.getByteTimeDomainData(this.tByte);\n\n return this.tByte;\n }),\n ),\n );\n\n public readonly timeFloat = outputFromObservable(\n interval(0, animationFrameScheduler).pipe(\n map(() => {\n if (this.tFloat.length !== this.fftSize) {\n this.tFloat = new Float32Array(this.frequencyBinCount);\n }\n\n this.getFloatTimeDomainData(this.tFloat);\n\n return this.tFloat;\n }),\n ),\n );\n\n constructor() {\n const O = {optional: true} as const;\n const fftSize = inject(new HostAttributeToken('fftSize'), O);\n const minDecibels = inject(new HostAttributeToken('minDecibels'), O);\n const maxDecibels = inject(new HostAttributeToken('maxDecibels'), O);\n const time = inject(new HostAttributeToken('smoothingTimeConstant'), O);\n\n super(inject(WA_AUDIO_CONTEXT), {\n fftSize: parse(fftSize, 2048),\n maxDecibels: parse(maxDecibels, -30),\n minDecibels: parse(minDecibels, -100),\n smoothingTimeConstant: parse(time, 0.8),\n });\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waBiquadFilterNode]',\n inputs: [\n 'detuneSetter: detune',\n 'frequencySetter: frequency',\n 'gainSetter: gain',\n 'qSetter: Q',\n 'type',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaBiquadFilter)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaBiquadFilter extends BiquadFilterNode {\n constructor() {\n const detune = inject(new HostAttributeToken('detune'), {optional: true});\n const frequency = inject(new HostAttributeToken('frequency'), {optional: true});\n const gain = inject(new HostAttributeToken('gain'), {optional: true});\n const Q = inject(new HostAttributeToken('Q'), {optional: true});\n\n super(inject(WA_AUDIO_CONTEXT), {\n detune: parse(detune, 0),\n frequency: parse(frequency, 350),\n gain: parse(gain, 0),\n Q: parse(Q, 1),\n });\n }\n\n public set detuneSetter(value: AudioParamInput) {\n audioParam(this.detune, value, this.context.currentTime);\n }\n\n public set frequencySetter(value: AudioParamInput) {\n audioParam(this.frequency, value, this.context.currentTime);\n }\n\n public set gainSetter(value: AudioParamInput) {\n audioParam(this.gain, value, this.context.currentTime);\n }\n\n public set qSetter(value: AudioParamInput) {\n audioParam(this.Q, value, this.context.currentTime);\n }\n}\n","import {\n contentChildren,\n Directive,\n effect,\n HostAttributeToken,\n inject,\n input,\n type OnDestroy,\n} from '@angular/core';\n\nimport {WaChannel} from '../directives/channel';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\n\n@Directive({\n selector: '[waChannelMergerNode]',\n inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'],\n providers: [asAudioNode(WaChannelMerger)],\n exportAs: 'AudioNode',\n})\nexport class WaChannelMerger extends ChannelMergerNode implements OnDestroy {\n private readonly channels = contentChildren(WaChannel, {descendants: false});\n public readonly inputs = input('', {alias: 'numberOfInputs'});\n\n constructor() {\n const attr = inject(new HostAttributeToken('numberOfInputs'), {optional: true});\n const numberOfInputs = Number.parseInt(attr || '', 10) || 6;\n\n super(inject(WA_AUDIO_CONTEXT), {numberOfInputs});\n\n effect(() => {\n this.channels().forEach((node, index) => {\n node.connect(this, 0, index);\n });\n });\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n}\n","import {\n contentChildren,\n Directive,\n effect,\n HostAttributeToken,\n inject,\n input,\n} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {WA_AUDIO_NODE} from '../tokens/audio-node';\n\n@Directive({\n selector: '[waChannelSplitterNode]',\n inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'],\n providers: [{provide: WA_AUDIO_NODE, useValue: null}],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaChannelSplitter extends ChannelSplitterNode {\n private readonly channels = contentChildren(WA_AUDIO_NODE, {descendants: false});\n public readonly outputs = input('', {alias: 'numberOfOutputs'});\n\n constructor() {\n const attr = inject(new HostAttributeToken('numberOfOutputs'), {optional: true});\n const numberOfOutputs = Number.parseInt(attr || '', 10) || 6;\n\n super(inject(WA_AUDIO_CONTEXT), {numberOfOutputs});\n\n effect(() => {\n this.disconnect();\n this.channels()\n .filter((node) => !!node)\n .forEach((node, index) => {\n this.connect(node, index);\n });\n });\n }\n}\n","import {inject, Injectable} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\n\n@Injectable({providedIn: 'root'})\nexport class WaAudioBufferService {\n private readonly context = inject(WA_AUDIO_CONTEXT);\n private readonly cache = new Map<string, AudioBuffer>();\n\n public async fetch(url: string): Promise<AudioBuffer> {\n return new Promise<AudioBuffer>((resolve, reject) => {\n if (this.cache.has(url)) {\n resolve(this.cache.get(url)!);\n\n return;\n }\n\n const request = new XMLHttpRequest();\n\n request.open('GET', url, true);\n request.responseType = 'arraybuffer';\n request.onerror = reject;\n request.onabort = reject;\n\n request.onload = () => {\n void this.context.decodeAudioData(\n request.response,\n (buffer) => {\n this.cache.set(url, buffer);\n resolve(buffer);\n },\n reject,\n );\n };\n\n request.send();\n });\n }\n}\n","import {Directive, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WaAudioBufferService} from '../services/audio-buffer.service';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\n\n@Directive({\n selector: '[waConvolverNode]',\n inputs: [\n 'bufferSetter: buffer',\n 'normalize',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaConvolver)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaConvolver extends ConvolverNode {\n private readonly service = inject(WaAudioBufferService);\n\n constructor() {\n super(inject(WA_AUDIO_CONTEXT));\n }\n\n public set bufferSetter(source: AudioBuffer | string | null) {\n if (typeof source === 'string') {\n this.service.fetch(source).then((result) => {\n this.buffer = result;\n });\n } else {\n this.buffer = source;\n }\n }\n}\n","import {Directive, HostAttributeToken, inject, input} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waDelayNode]',\n inputs: [\n 'delayTimeSetter: delayTime',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaDelay)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaDelay extends DelayNode {\n public readonly maxDelayTime = input('');\n\n constructor() {\n const delayTime = inject(new HostAttributeToken('delayTime'), {optional: true});\n\n const maxDelayTime = inject(new HostAttributeToken('maxDelayTime'), {\n optional: true,\n });\n\n super(inject(WA_AUDIO_CONTEXT), {\n delayTime: parse(delayTime, 0),\n maxDelayTime: parse(maxDelayTime, 1),\n });\n }\n\n public set delayTimeSetter(value: AudioParamInput) {\n audioParam(this.delayTime, value, this.context.currentTime);\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waDynamicsCompressorNode]',\n inputs: [\n 'attackSetter: attack',\n 'kneeSetter: knee',\n 'ratioSetter: ratio',\n 'releaseSetter: release',\n 'thresholdSetter: threshold',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaDynamicsCompressor)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaDynamicsCompressor extends DynamicsCompressorNode {\n constructor() {\n const attack = inject(new HostAttributeToken('attack'), {optional: true});\n const knee = inject(new HostAttributeToken('knee'), {optional: true});\n const ratio = inject(new HostAttributeToken('ratio'), {optional: true});\n const release = inject(new HostAttributeToken('release'), {optional: true});\n const threshold = inject(new HostAttributeToken('threshold'), {optional: true});\n\n super(inject(WA_AUDIO_CONTEXT), {\n attack: parse(attack, 0.003),\n knee: parse(knee, 30),\n ratio: parse(ratio, 12),\n release: parse(release, 0.25),\n threshold: parse(threshold, -24),\n });\n }\n\n public set attackSetter(value: AudioParamInput) {\n audioParam(this.attack, value, this.context.currentTime);\n }\n\n public set kneeSetter(value: AudioParamInput) {\n audioParam(this.knee, value, this.context.currentTime);\n }\n\n public set ratioSetter(value: AudioParamInput) {\n audioParam(this.ratio, value, this.context.currentTime);\n }\n\n public set releaseSetter(value: AudioParamInput) {\n audioParam(this.release, value, this.context.currentTime);\n }\n\n public set thresholdSetter(value: AudioParamInput) {\n audioParam(this.threshold, value, this.context.currentTime);\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waGainNode]',\n inputs: [\n 'gainSetter: gain',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaGain)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaGain extends GainNode {\n constructor() {\n const gain = inject(new HostAttributeToken('gain'), {optional: true});\n\n super(inject(WA_AUDIO_CONTEXT), {gain: parse(gain, 1)});\n }\n\n public set gainSetter(value: AudioParamInput) {\n audioParam(this.gain, value, this.context.currentTime);\n }\n}\n","import {InjectionToken} from '@angular/core';\n\nexport const WA_FEEDBACK_COEFFICIENTS = new InjectionToken<number[]>(\n ngDevMode ? '[WA_FEEDBACK_COEFFICIENTS]' : '',\n);\n","import {InjectionToken} from '@angular/core';\n\nexport const WA_FEEDFORWARD_COEFFICIENTS = new InjectionToken<number[]>(\n ngDevMode ? '[WA_FEEDFORWARD_COEFFICIENTS]' : '',\n);\n","import {Directive, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {WA_FEEDBACK_COEFFICIENTS} from '../tokens/feedback-coefficients';\nimport {WA_FEEDFORWARD_COEFFICIENTS} from '../tokens/feedforward-coefficients';\n\n@Directive({\n selector: '[waIIRFilterNode]',\n inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'],\n providers: [asAudioNode(WaIIRFilter)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaIIRFilter extends IIRFilterNode {\n constructor() {\n const feedback = inject(WA_FEEDBACK_COEFFICIENTS);\n const feedforward = inject(WA_FEEDFORWARD_COEFFICIENTS);\n\n super(inject(WA_AUDIO_CONTEXT), {feedback, feedforward});\n }\n}\n","import {Directive, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\n\n@Directive({\n selector: '[waPannerNode]',\n inputs: [\n 'orientationXSetter: orientationX',\n 'orientationYSetter: orientationY',\n 'orientationZSetter: orientationZ',\n 'positionXSetter: positionX',\n 'positionYSetter: positionY',\n 'positionZSetter: positionZ',\n 'coneInnerAngle',\n 'coneOuterAngle',\n 'coneOuterGain',\n 'distanceModel',\n 'maxDistance',\n 'panningModel',\n 'refDistance',\n 'rolloffFactor',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaPanner)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaPanner extends PannerNode {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT));\n }\n\n public set orientationXSetter(value: AudioParamInput) {\n audioParam(this.orientationX, value, this.context.currentTime);\n }\n\n public set orientationYSetter(value: AudioParamInput) {\n audioParam(this.orientationY, value, this.context.currentTime);\n }\n\n public set orientationZSetter(value: AudioParamInput) {\n audioParam(this.orientationZ, value, this.context.currentTime);\n }\n\n public set positionXSetter(value: AudioParamInput) {\n audioParam(this.positionX, value, this.context.currentTime);\n }\n\n public set positionYSetter(value: AudioParamInput) {\n audioParam(this.positionY, value, this.context.currentTime);\n }\n\n public set positionZSetter(value: AudioParamInput) {\n audioParam(this.positionZ, value, this.context.currentTime);\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waStereoPannerNode]',\n inputs: [\n 'panSetter: pan',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaStereoPanner)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaStereoPanner extends StereoPannerNode {\n constructor() {\n const pan = inject(new HostAttributeToken('pan'), {optional: true});\n\n super(inject(WA_AUDIO_CONTEXT), {pan: parse(pan, 0)});\n }\n\n public set panSetter(value: AudioParamInput) {\n audioParam(this.pan, value, this.context.currentTime);\n }\n}\n","import {Directive, inject} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\n\n@Directive({\n selector: '[waWaveShaperNode]',\n inputs: [\n 'oversample',\n 'curve',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaWaveShaper)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaWaveShaper extends WaveShaperNode {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT));\n }\n}\n","import {Directive, HostAttributeToken, inject, input, output} from '@angular/core';\n\nimport {WaNode} from '../directives/node';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\n\n// TODO: Add AudioWorkletNodeOptions\n@Directive({\n selector: '[waAudioWorkletNode][name]',\n inputs: ['channelCount', 'channelCountMode', 'channelInterpretation'],\n providers: [asAudioNode(WaWorklet)],\n exportAs: 'AudioNode',\n hostDirectives: [WaNode],\n})\nexport class WaWorklet extends AudioWorkletNode {\n public readonly name = input('');\n public readonly processorerror = output();\n\n constructor() {\n super(inject(WA_AUDIO_CONTEXT), inject(new HostAttributeToken('name')));\n }\n\n public override readonly onprocessorerror = (): void => this.processorerror.emit();\n}\n","import {Pipe, type PipeTransform} from '@angular/core';\n\nimport {type AudioParamAutomation} from '../types/audio-param-automation';\nimport {type AudioParamAutomationMode} from '../types/audio-param-automation-mode';\nimport {type AudioParamCurve} from '../types/audio-param-curve';\n\n@Pipe({name: 'waAudioParam'})\nexport class WaAudioParamPipe implements PipeTransform {\n /**\n * Creates {@link AudioParamAutomation} to use with {@link AudioParam} inputs\n *\n * @param value target value\n * @param duration duration of the automation\n * @param mode either instant for given duration or linear/exponential ramp\n */\n public transform(\n value: number[] | number,\n duration: number,\n mode: AudioParamAutomationMode = 'exponential',\n ): AudioParamAutomation | AudioParamCurve {\n return value instanceof Array ? {value, duration} : {value, duration, mode};\n }\n}\n","import {inject, Pipe, type PipeTransform} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\n\n@Pipe({name: 'waPeriodicWave'})\nexport class WaPeriodicWavePipe implements PipeTransform {\n private readonly context = inject(WA_AUDIO_CONTEXT);\n\n /**\n * Creates {@link PeriodicWave} to use with {@link OscillatorNode}\n *\n * @param real cosine terms (traditionally the A terms)\n * @param imag sine terms (traditionally the B terms)\n * @param disableNormalization see {@lin PeriodicWaveConstraints}\n */\n public transform(\n real: number[],\n imag?: number[],\n disableNormalization?: boolean,\n ): PeriodicWave {\n return this.context.createPeriodicWave(\n new Float32Array(real),\n imag ? new Float32Array(imag) : new Float32Array(real.length),\n {disableNormalization: !!disableNormalization},\n );\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaSource} from '../directives/source';\nimport {WaAudioBufferService} from '../services/audio-buffer.service';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waAudioBufferSourceNode]',\n inputs: [\n 'bufferSetter: buffer',\n 'detuneSetter: detune',\n 'playbackRateSetter: playbackRate',\n 'loop',\n 'loopStart',\n 'loopEnd',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaBufferSource)],\n exportAs: 'AudioNode',\n hostDirectives: [WaSource],\n})\nexport class WaBufferSource extends AudioBufferSourceNode {\n private readonly service = inject(WaAudioBufferService);\n\n constructor() {\n const detune = inject(new HostAttributeToken('detune'), {optional: true});\n\n const playbackRate = inject(new HostAttributeToken('playbackRate'), {\n optional: true,\n });\n\n super(inject(WA_AUDIO_CONTEXT), {\n detune: parse(detune, 0),\n playbackRate: parse(playbackRate, 1),\n });\n }\n\n public set detuneSetter(value: AudioParamInput) {\n audioParam(this.detune, value, this.context.currentTime);\n }\n\n public set playbackRateSetter(value: AudioParamInput) {\n audioParam(this.playbackRate, value, this.context.currentTime);\n }\n\n public set bufferSetter(source: AudioBuffer | string | null) {\n if (typeof source === 'string') {\n this.service.fetch(source).then((result) => {\n this.buffer = result;\n });\n } else {\n this.buffer = source;\n }\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaSource} from '../directives/source';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waConstantSourceNode]',\n inputs: [\n 'offsetSetter: offset',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n ],\n providers: [asAudioNode(WaConstantSource)],\n exportAs: 'AudioNode',\n hostDirectives: [WaSource],\n})\nexport class WaConstantSource extends ConstantSourceNode {\n constructor() {\n super(inject(WA_AUDIO_CONTEXT), {\n offset: parse(inject(new HostAttributeToken('offset'), {optional: true}), 0),\n });\n }\n\n public set offsetSetter(value: AudioParamInput) {\n audioParam(this.offset, value, this.context.currentTime);\n }\n}\n","import {Directive, ElementRef, inject, type OnDestroy} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\n\n@Directive({\n selector:\n 'audio[waMediaElementAudioSourceNode], video[waMediaElementAudioSourceNode]',\n providers: [asAudioNode(WaMediaSource)],\n exportAs: 'AudioNode',\n})\nexport class WaMediaSource extends MediaElementAudioSourceNode implements OnDestroy {\n constructor() {\n super(inject<AudioContext>(WA_AUDIO_CONTEXT), {\n mediaElement: inject(ElementRef).nativeElement,\n });\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n}\n","import {InjectionToken} from '@angular/core';\n\nexport const WA_MEDIA_STREAM = new InjectionToken<MediaStream>(\n ngDevMode ? '[WA_MEDIA_STREAM]' : '',\n);\n","import {Directive, inject, type OnDestroy} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {WA_MEDIA_STREAM} from '../tokens/media-stream';\n\n@Directive({\n selector: '[waMediaStreamAudioSourceNode]',\n providers: [asAudioNode(WaMediaStreamSource)],\n exportAs: 'AudioNode',\n})\nexport class WaMediaStreamSource extends MediaStreamAudioSourceNode implements OnDestroy {\n constructor() {\n super(inject<AudioContext>(WA_AUDIO_CONTEXT), {\n mediaStream: inject(WA_MEDIA_STREAM),\n });\n }\n\n public ngOnDestroy(): void {\n this.disconnect();\n }\n}\n","import {Directive, HostAttributeToken, inject} from '@angular/core';\n\nimport {WaSource} from '../directives/source';\nimport {WA_AUDIO_CONTEXT} from '../tokens/audio-context';\nimport {asAudioNode} from '../tokens/audio-node';\nimport {type AudioParamInput} from '../types/audio-param-input';\nimport {audioParam} from '../utils/audio-param';\nimport {parse} from '../utils/parse';\n\n@Directive({\n selector: '[waOscillatorNode]',\n inputs: [\n 'detuneSetter: detune',\n 'frequencySetter: frequency',\n 'type',\n 'channelCount',\n 'channelCountMode',\n 'channelInterpretation',\n 'periodicWave',\n ],\n providers: [asAudioNode(WaOscillator)],\n exportAs: 'AudioNode',\n hostDirectives: [WaSource],\n})\nexport class WaOscillator extends OscillatorNode {\n constructor() {\n const detune = inject(new HostAttributeToken('detune'), {optional: true});\n const frequency = inject(new HostAttributeToken('frequency'), {optional: true});\n\n super(inject(WA_AUDIO_CONTEXT), {\n detune: parse(detune, 0),\n frequency: parse(frequency, 440),\n });\n }\n\n public set periodicWave(periodicWave: PeriodicWave) {\n this.setPeriodicWave(periodicWave);\n }\n\n public set detuneSetter(value: AudioParamInput) {\n audioParam(this.detune, value, this.context.currentTime);\n }\n\n public set frequencySetter(value: AudioParamInput) {\n audioParam(this.frequency, value, this.context.currentTime);\n }\n}\n","import {InjectionToken} from '@angular/core';\n\nexport const WA_AUDIO_WORKLET_PROCESSORS = new InjectionToken<readonly string[]>(\n ngDevMode ? '[WA_AUDIO_WORKLET_PROCESSORS]' : '',\n {factory: () => []},\n);\n","import {inject, InjectionToken} from '@angular/core';\n\nimport {WA_AUDIO_CONTEXT} from './audio-context';\nimport {WA_AUDIO_WORKLET_PROCESSORS} from './audio-worklet-processors';\n\nexport const WA_AUDIO_WORKLET_PROCESSORS_READY = new InjectionToken<Promise<boolean>>(\n ngDevMode ? '[WA_AUDIO_WORKLET_PROCESSORS_READY]' : '',\n {\n factory: async () => {\n const context = inject(WA_AUDIO_CONTEXT);\n const processors = inject(WA_AUDIO_WORKLET_PROCESSORS);\n\n return Promise.all(\n processors.map(async (processor) =>\n context.audioWorklet.addModule(processor),\n ),\n ).then(() => true);\n },\n },\n);\n","import {InjectionToken} from '@angular/core';\n\nexport const WA_WEB_AUDIO_SUPPORT = new InjectionToken<boolean>(\n ngDevMode ? '[WA_WEB_AUDIO_SUPPORT]' : '',\n {factory: () => typeof AudioContext !== 'undefined'},\n);\n","import {WaAudioContext} from './directives/audio-context';\nimport {WaChannel} from './directives/channel';\nimport {WaDestination} from './directives/destination';\nimport {WaListener} from './directives/listener';\nimport {WaOfflineContext} from './directives/offline-audio-context';\nimport {WaOutput} from './directives/output';\nimport {WaMediaStreamDestination} from './directives/stream-destination';\nimport {WaAnalyser} from './nodes/analyser';\nimport {WaBiquadFilter} from './nodes/biquad-filter';\nimport {WaChannelMerger} from './nodes/channel-merger';\nimport {WaChannelSplitter} from './nodes/channel-splitter';\nimport {WaConvolver} from './nodes/convolver';\nimport {WaDelay} from './nodes/delay';\nimport {WaDynamicsCompressor} from './nodes/dynamics-compressor';\nimport {WaGain} from './nodes/gain';\nimport {WaIIRFilter} from './nodes/iir-filter';\nimport {WaPanner} from './nodes/panner';\nimport {WaStereoPanner} from './nodes/stereo-panner';\nimport {WaWaveShaper} from './nodes/wave-shaper';\nimport {WaWorklet} from './nodes/worklet';\nimport {WaAudioParamPipe} from './pipes/audio-param.pipe';\nimport {WaPeriodicWavePipe} from './pipes/periodic-wave.pipe';\nimport {WaBufferSource} from './sources/buffer-source';\nimport {WaConstantSource} from './sources/constant-source';\nimport {WaMediaSource} from './sources/media-source';\nimport {WaMediaStreamSource} from './sources/media-stream-source';\nimport {WaOscillator} from './sources/oscillator';\n\nexport const WaWebAudio = [\n WaAudioContext,\n WaChannel,\n WaDestination,\n WaListener,\n WaOfflineContext,\n WaOutput,\n WaMediaStreamDestination,\n WaBufferSource,\n WaConstantSource,\n WaMediaSource,\n WaMediaStreamSource,\n WaOscillator,\n WaAnalyser,\n WaBiquadFilter,\n WaChannelSplitter,\n WaChannelMerger,\n WaConvolver,\n WaDelay,\n WaDynamicsCompressor,\n WaGain,\n WaIIRFilter,\n WaPanner,\n WaStereoPanner,\n WaWaveShaper,\n WaWorklet,\n WaAudioParamPipe,\n WaPeriodicWavePipe,\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1.WaNode","i1.WaSource"],"mappings":"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