virtual-audio-graph
Version:
Library for declaratively manipulating the Web Audio API
92 lines (79 loc) • 2.95 kB
text/typescript
import { IVirtualAudioNodeParams, Output, VirtualAudioNode } from "../types";
import { equals } from "../utils";
import CustomVirtualAudioNode from "./CustomVirtualAudioNode";
import VirtualAudioNodeBase from "./VirtualAudioNodeBase";
export default class AudioWorkletVirtualAudioNode extends VirtualAudioNodeBase {
public audioNode!: AudioNode;
public connected: boolean = false;
private connections: AudioNode[] = [];
constructor(
public readonly node: string,
public output?: Output,
public params?: IVirtualAudioNodeParams,
public readonly input?: string,
) {
super();
}
public connect(...connectArgs: any[]): void {
const { audioNode } = this;
const filteredConnectArgs = connectArgs.filter(Boolean);
const [firstArg, ...rest] = filteredConnectArgs;
if (audioNode.connect) {
audioNode.connect(firstArg, ...rest);
}
this.connections = this.connections.concat(filteredConnectArgs);
this.connected = true;
}
public disconnect(node?: VirtualAudioNode): void {
const { audioNode } = this;
if (node) {
if (node instanceof CustomVirtualAudioNode) {
for (const childNode of Object.values(node.virtualNodes)) {
if (!this.connections.some((x) => x === childNode.audioNode))
continue;
this.connections = this.connections.filter(
(x) => x !== childNode.audioNode,
);
}
} else {
if (!this.connections.some((x) => x === node.audioNode)) return;
this.connections = this.connections.filter((x) => x !== node.audioNode);
}
}
if (audioNode.disconnect) audioNode.disconnect();
this.connected = false;
}
public disconnectAndDestroy(): void {
const { audioNode } = this;
if (audioNode.disconnect) audioNode.disconnect();
this.connected = false;
}
public initialize(audioContext: AudioContext | OfflineAudioContext): this {
const params = this.params || {};
this.audioNode = new window.AudioWorkletNode(audioContext, this.node);
this.params = undefined;
return this.update(params);
}
public update(params: IVirtualAudioNodeParams = {}): this {
const audioNode = this.audioNode;
for (const key of Object.keys(params)) {
const param = params[key];
if (this.params?.[key] === param) continue;
const paramInstance = (audioNode as any).parameters.get(key);
if (Array.isArray(param)) {
if (this.params && !equals(param, this.params[key])) {
(audioNode as any).parameters.get(key).cancelScheduledValues(0);
}
const callMethod = ([methodName, ...args]: [string, ...any[]]) =>
paramInstance[methodName](...args);
Array.isArray(param[0])
? param.forEach(callMethod)
: callMethod(param as [string, ...any[]]);
continue;
}
paramInstance.value = param;
}
this.params = params;
return this;
}
}