From 06bc0fb62041e2993c58a37cb76c591513414426 Mon Sep 17 00:00:00 2001 From: Ben Hall Date: Thu, 15 Jul 2021 22:32:58 +0100 Subject: [PATCH] fix(CustomVirtualAudioNode): handle when startTime and stopTime change on child nodes See #316 https://github.com/benji6/virtual-audio-graph/issues/316 --- src/VirtualAudioGraph.ts | 10 +---- .../AudioWorkletVirtualAudioNode.ts | 7 +++- .../CustomVirtualAudioNode.ts | 38 ++++++++++++++++--- .../StandardVirtualAudioNode.ts | 14 ++++++- src/VirtualAudioNodes/VirtualAudioNodeBase.ts | 9 +++++ 5 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 src/VirtualAudioNodes/VirtualAudioNodeBase.ts diff --git a/src/VirtualAudioGraph.ts b/src/VirtualAudioGraph.ts index 90028dbf..aa007745 100644 --- a/src/VirtualAudioGraph.ts +++ b/src/VirtualAudioGraph.ts @@ -38,13 +38,7 @@ export default class VirtualAudioGraph { continue; } - if ( - (newVirtualAudioNode.params && newVirtualAudioNode.params.startTime) !== - (virtualAudioNode.params && virtualAudioNode.params.startTime) || - (newVirtualAudioNode.params && newVirtualAudioNode.params.stopTime) !== - (virtualAudioNode.params && virtualAudioNode.params.stopTime) || - newVirtualAudioNode.node !== virtualAudioNode.node - ) { + if (virtualAudioNode.cannotUpdateInPlace(newVirtualAudioNode)) { virtualAudioNode.disconnectAndDestroy(); this.disconnectParents(virtualAudioNode); this.virtualNodes[key] = newVirtualAudioNode.initialize( @@ -59,7 +53,7 @@ export default class VirtualAudioGraph { virtualAudioNode.output = newVirtualAudioNode.output; } - virtualAudioNode.update(newVirtualAudioNode.params); + virtualAudioNode.update(newVirtualAudioNode.params, this.audioContext); } connectAudioNodes(this.virtualNodes, (vNode: VirtualAudioNode) => diff --git a/src/VirtualAudioNodes/AudioWorkletVirtualAudioNode.ts b/src/VirtualAudioNodes/AudioWorkletVirtualAudioNode.ts index 0ccae822..59b8bdf9 100644 --- a/src/VirtualAudioNodes/AudioWorkletVirtualAudioNode.ts +++ b/src/VirtualAudioNodes/AudioWorkletVirtualAudioNode.ts @@ -1,12 +1,13 @@ import { IVirtualAudioNodeParams, Output, VirtualAudioNode } from "../types"; import { equals, values } from "../utils"; import CustomVirtualAudioNode from "./CustomVirtualAudioNode"; +import VirtualAudioNodeBase from "./VirtualAudioNodeBase"; interface IWindow { AudioWorkletNode?: any; } -export default class AudioWorkletVirtualAudioNode { +export default class AudioWorkletVirtualAudioNode extends VirtualAudioNodeBase { public audioNode: AudioNode; public connected: boolean = false; private connections: AudioNode[] = []; @@ -16,7 +17,9 @@ export default class AudioWorkletVirtualAudioNode { public output?: Output, public params?: IVirtualAudioNodeParams, public readonly input?: string - ) {} + ) { + super(); + } public connect(...connectArgs: any[]): void { const { audioNode } = this; diff --git a/src/VirtualAudioNodes/CustomVirtualAudioNode.ts b/src/VirtualAudioNodes/CustomVirtualAudioNode.ts index ceea070b..7b088d52 100644 --- a/src/VirtualAudioNodes/CustomVirtualAudioNode.ts +++ b/src/VirtualAudioNodes/CustomVirtualAudioNode.ts @@ -6,9 +6,10 @@ import { Output, VirtualAudioNode, } from "../types"; -import { mapObj, values } from "../utils"; +import { equals, mapObj, values } from "../utils"; +import VirtualAudioNodeBase from "./VirtualAudioNodeBase"; -export default class CustomVirtualAudioNode { +export default class CustomVirtualAudioNode extends VirtualAudioNodeBase { public readonly audioNode: undefined = undefined; public connected: boolean = false; public params: IVirtualAudioNodeParams; @@ -19,6 +20,7 @@ export default class CustomVirtualAudioNode { public output?: Output, params?: IVirtualAudioNodeParams ) { + super(); this.params = params || {}; } @@ -68,14 +70,40 @@ export default class CustomVirtualAudioNode { return this; } - public update(params: IVirtualAudioNodeParams = {}): this { + public update( + params: IVirtualAudioNodeParams = {}, + audioContext: AudioContext + ): this { const audioGraphParamsFactoryValues = values(this.node(params)); const keys = Object.keys(this.virtualNodes); + for (let i = 0; i < keys.length; i++) { - const p = audioGraphParamsFactoryValues[i]; - this.virtualNodes[keys[i]].update(p.params); + const key = keys[i]; + const virtualAudioNode = this.virtualNodes[key]; + const newVirtualAudioNode = audioGraphParamsFactoryValues[i]; + + if (virtualAudioNode.cannotUpdateInPlace(newVirtualAudioNode)) { + virtualAudioNode.disconnectAndDestroy(); + this.disconnectParents(virtualAudioNode); + this.virtualNodes[key] = newVirtualAudioNode.initialize(audioContext); + continue; + } + + virtualAudioNode.update(newVirtualAudioNode.params, audioContext); + + if (!equals(newVirtualAudioNode.output, virtualAudioNode.output)) { + virtualAudioNode.disconnect(); + this.disconnectParents(virtualAudioNode); + virtualAudioNode.output = newVirtualAudioNode.output; + } } + + connectAudioNodes(this.virtualNodes, () => {}); this.params = params; return this; } + + private disconnectParents(vNode: VirtualAudioNode): void { + for (const node of values(this.virtualNodes)) node.disconnect(vNode); + } } diff --git a/src/VirtualAudioNodes/StandardVirtualAudioNode.ts b/src/VirtualAudioNodes/StandardVirtualAudioNode.ts index 64d23630..9592f0a8 100644 --- a/src/VirtualAudioNodes/StandardVirtualAudioNode.ts +++ b/src/VirtualAudioNodes/StandardVirtualAudioNode.ts @@ -13,6 +13,7 @@ import { } from "../types"; import { capitalize, equals, find, values } from "../utils"; import CustomVirtualAudioNode from "./CustomVirtualAudioNode"; +import VirtualAudioNodeBase from "./VirtualAudioNodeBase"; interface IAudioContextFactoryLookup { [_: string]: any; @@ -41,7 +42,7 @@ const createAudioNode = ( return audioNode; }; -export default class StandardVirtualAudioNode { +export default class StandardVirtualAudioNode extends VirtualAudioNodeBase { public audioNode: AudioNode; public connected: boolean = false; private connections: AudioNode[] = []; @@ -53,10 +54,21 @@ export default class StandardVirtualAudioNode { public params?: IVirtualAudioNodeParams, public readonly input?: string ) { + super(); const stopTime = params && params.stopTime; this.stopCalled = stopTime !== undefined; } + public cannotUpdateInPlace(newVirtualAudioNode: VirtualAudioNode): boolean { + return ( + super.cannotUpdateInPlace(newVirtualAudioNode) || + (newVirtualAudioNode.params && newVirtualAudioNode.params.startTime) !== + (this.params && this.params.startTime) || + (newVirtualAudioNode.params && newVirtualAudioNode.params.stopTime) !== + (this.params && this.params.stopTime) + ); + } + public connect(...connectArgs: any[]): void { const { audioNode } = this; const filteredConnectArgs = connectArgs.filter(Boolean); diff --git a/src/VirtualAudioNodes/VirtualAudioNodeBase.ts b/src/VirtualAudioNodes/VirtualAudioNodeBase.ts new file mode 100644 index 00000000..54e0f054 --- /dev/null +++ b/src/VirtualAudioNodes/VirtualAudioNodeBase.ts @@ -0,0 +1,9 @@ +import { CustomVirtualAudioNodeFactory, VirtualAudioNode } from "../types"; + +export default abstract class VirtualAudioNodeBase { + public readonly node: string | CustomVirtualAudioNodeFactory; + + public cannotUpdateInPlace(newVirtualAudioNode: VirtualAudioNode): boolean { + return newVirtualAudioNode.node !== this.node; + } +}