From 1b406592f9787e1e419cb430ba5c8375397b8e9c Mon Sep 17 00:00:00 2001 From: Christopher Toth Date: Tue, 28 Jan 2025 15:00:25 -0800 Subject: [PATCH] Revert "feat: add audio node interface to BasePlayback (#16)" (#17) This reverts commit b4ee6df87cc3b1c7f2539dcf1cb6581abd6eaea1. --- src/basePlayback.test.ts | 67 ------------------------------- src/basePlayback.ts | 34 +--------------- src/filters.ts | 8 ---- src/oscillatorMixin.ts | 14 ------- src/pannerMixin.ts | 14 ------- src/playback.test.ts | 87 ---------------------------------------- src/playback.ts | 14 ------- src/volumeMixin.ts | 16 +------- 8 files changed, 3 insertions(+), 251 deletions(-) delete mode 100644 src/basePlayback.test.ts diff --git a/src/basePlayback.test.ts b/src/basePlayback.test.ts deleted file mode 100644 index 157a8cf..0000000 --- a/src/basePlayback.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { vi, expect, describe, it, beforeEach } from "vitest"; -import { audioContextMock } from "./setupTests"; -import { BasePlayback } from "./basePlayback"; -import { Sound } from "./sound"; - -// Create a concrete implementation of BasePlayback for testing -class TestPlayback extends BasePlayback { - constructor( - public source?: AudioNode, - public gainNode?: GainNode, - public panner?: AudioNode - ) { - super(); - } - - play(): [this] { return [this]; } - pause(): void {} - stop(): void {} - cleanup(): void { - this.source = undefined; - this.gainNode = undefined; - this.panner = undefined; - } -} - -describe("BasePlayback Audio Node Interface", () => { - let playback: TestPlayback; - let destination: AudioNode; - let param: AudioParam; - - beforeEach(() => { - const source = audioContextMock.createOscillator(); - const gainNode = audioContextMock.createGain(); - const panner = audioContextMock.createStereoPanner(); - playback = new TestPlayback(source, gainNode, panner); - destination = audioContextMock.createGain(); - param = destination.gain; - }); - - describe("Connection Methods", () => { - it("can connect to other nodes", () => { - const connectSpy = vi.spyOn(playback.outputNode, 'connect'); - playback.connect(destination); - expect(connectSpy).toHaveBeenCalledWith(destination); - }); - - it("can connect to audio params", () => { - const connectSpy = vi.spyOn(playback.outputNode, 'connect'); - playback.connect(param); - expect(connectSpy).toHaveBeenCalledWith(param); - }); - - it("can disconnect all outputs", () => { - const disconnectSpy = vi.spyOn(playback.outputNode, 'disconnect'); - playback.disconnect(); - expect(disconnectSpy).toHaveBeenCalled(); - }); - }); - - describe("Error Handling", () => { - it("throws when accessing nodes after cleanup", () => { - playback.cleanup(); - expect(() => playback.connect(destination)).toThrow(); - expect(() => playback.disconnect()).toThrow(); - }); - }); -}); diff --git a/src/basePlayback.ts b/src/basePlayback.ts index 8c15a03..708438e 100644 --- a/src/basePlayback.ts +++ b/src/basePlayback.ts @@ -1,5 +1,5 @@ import { IPlaybackContainer } from "./container"; -import { AudioNode, AudioParam } from "./context"; +import { AudioNode } from "./context"; import { FilterManager } from "./filters"; import { PannerMixin } from "./pannerMixin"; import { VolumeMixin } from "./volumeMixin"; @@ -14,46 +14,16 @@ export abstract class BasePlayback extends PannerMixin( abstract play(): [this]; abstract pause(): void; abstract stop(): void; - abstract cleanup(): void; /** * Checks if the audio is currently playing. * @returns {boolean} True if the audio is playing, false otherwise. */ + get isPlaying(): boolean { if (!this.source) { return false; } return this._playing; } - - /** - * Gets the first node in the audio chain that can receive input - */ - abstract get inputNode(): AudioNode; - - /** - * Gets the final node in the audio chain - */ - abstract get outputNode(): AudioNode; - - /** - * Connects the output to an audio node or param - */ - connect(destination: AudioNode | AudioParam): void { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - this.outputNode.connect(destination); - } - - /** - * Disconnects all outputs - */ - disconnect(): void { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - this.outputNode.disconnect(); - } } diff --git a/src/filters.ts b/src/filters.ts index 56dae9e..d5b3140 100644 --- a/src/filters.ts +++ b/src/filters.ts @@ -5,14 +5,6 @@ export type FilterCloneOverrides = { export abstract class FilterManager { _filters: BiquadFilterNode[] = []; - get inputNode(): AudioNode { - return this._filters[0] || this.source!; - } - - get outputNode(): AudioNode { - return this._filters[this._filters.length - 1] || this.source!; - } - addFilter(filter: BiquadFilterNode) { this._filters.push(filter); } diff --git a/src/oscillatorMixin.ts b/src/oscillatorMixin.ts index e0da1e8..9f5cde1 100644 --- a/src/oscillatorMixin.ts +++ b/src/oscillatorMixin.ts @@ -12,20 +12,6 @@ export function OscillatorMixin(Base: TBase) { _oscillatorOptions: Partial = {}; declare public source?: OscillatorNode; - get inputNode(): AudioNode { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.source; - } - - get outputNode(): AudioNode { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.source; - } - get oscillatorOptions(): Partial { return this._oscillatorOptions; } diff --git a/src/pannerMixin.ts b/src/pannerMixin.ts index 91b1d8e..aa7adb2 100644 --- a/src/pannerMixin.ts +++ b/src/pannerMixin.ts @@ -16,20 +16,6 @@ export function PannerMixin(Base: TBase) { panner?: PannerNode | StereoPannerNode; _panType: PanType = 'stereo'; - get inputNode(): AudioNode { - if (!this.panner) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.panner; - } - - get outputNode(): AudioNode { - if (!this.panner) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.panner; - } - get panType(): PanType { return this._panType; } diff --git a/src/playback.test.ts b/src/playback.test.ts index 8a39877..1854e2f 100644 --- a/src/playback.test.ts +++ b/src/playback.test.ts @@ -258,16 +258,6 @@ describe("Playback filters chain", () => { gainNode = audioContextMock.createGain(); sound = new Sound("test-url", buffer, audioContextMock, gainNode); playback = new Playback(sound, source, gainNode); - - // Ensure filter nodes have proper connect methods - vi.spyOn(audioContextMock, 'createBiquadFilter').mockImplementation(() => ({ - connect: vi.fn(), - disconnect: vi.fn(), - type: 'lowpass', - frequency: { value: 350 }, - Q: { value: 1 }, - gain: { value: 0 } - })); }); it("connects multiple filters in order", () => { @@ -290,83 +280,6 @@ describe("Playback filters chain", () => { }); }); -describe("Audio Node Chain", () => { - let playback: Playback; - let buffer: AudioBuffer; - let source: AudioBufferSourceNode; - let gainNode: GainNode; - let sound: Sound; - - beforeEach(() => { - buffer = new AudioBuffer({ length: 100, sampleRate: 44100 }); - source = audioContextMock.createBufferSource(); - source.buffer = buffer; - gainNode = audioContextMock.createGain(); - sound = new Sound("test-url", buffer, audioContextMock, gainNode); - playback = new Playback(sound, source, gainNode); - }); - - it("maintains correct chain order with filters", () => { - const filter1 = audioContextMock.createBiquadFilter(); - const filter2 = audioContextMock.createBiquadFilter(); - - // Spy on the panner's connect method since that's the start of our chain - const pannerConnectSpy = vi.spyOn(playback.panner!, 'connect'); - - playback.addFilter(filter1 as unknown as BiquadFilterNode); - playback.addFilter(filter2 as unknown as BiquadFilterNode); - - // Verify the panner was connected - expect(pannerConnectSpy).toHaveBeenCalled(); - }); - - it("can connect to external Web Audio nodes", () => { - const externalNode = audioContextMock.createGain(); - const connectSpy = vi.spyOn(playback.outputNode, 'connect'); - - playback.connect(externalNode); - - expect(connectSpy).toHaveBeenCalledWith(externalNode); - }); - - it("maintains node access during play/pause/stop states", () => { - const getNodes = () => ({ - input: playback.inputNode, - output: playback.outputNode - }); - - // Test initial state - expect(getNodes().input).toBeDefined(); - expect(getNodes().output).toBeDefined(); - - // Test playing state - playback.play(); - expect(getNodes().input).toBeDefined(); - expect(getNodes().output).toBeDefined(); - - // Test paused state - playback.pause(); - expect(getNodes().input).toBeDefined(); - expect(getNodes().output).toBeDefined(); - - // Test stopped state - playback.stop(); - expect(getNodes().input).toBeDefined(); - expect(getNodes().output).toBeDefined(); - }); - - it("properly handles connections after cleanup", () => { - const externalNode = audioContextMock.createGain(); - - playback.cleanup(); - - expect(() => playback.connect(externalNode)).toThrow('Cannot access nodes of a cleaned up sound'); - expect(() => playback.disconnect()).toThrow('Cannot access nodes of a cleaned up sound'); - expect(() => playback.inputNode).toThrow('Cannot access nodes of a cleaned up sound'); - expect(() => playback.outputNode).toThrow('Cannot access nodes of a cleaned up sound'); - }); -}); - describe("Playback error cases", () => { let playback: Playback; let buffer: AudioBuffer; diff --git a/src/playback.ts b/src/playback.ts index 0a3db76..ed4cb56 100644 --- a/src/playback.ts +++ b/src/playback.ts @@ -421,20 +421,6 @@ export class Playback extends BasePlayback implements BaseSound { * @throws {Error} Throws an error if the sound has been cleaned up. */ - get inputNode(): AudioNode { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return super.inputNode; - } - - get outputNode(): AudioNode { - if (!this.source) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return super.outputNode; - } - private refreshFilters(): void { if (!this.panner || !this.gainNode) { throw new Error( diff --git a/src/volumeMixin.ts b/src/volumeMixin.ts index dbe24e6..3d2dffa 100644 --- a/src/volumeMixin.ts +++ b/src/volumeMixin.ts @@ -1,4 +1,4 @@ -import { AudioNode, GainNode } from "./context"; +import { GainNode } from "./context"; import { FilterManager } from "./filters"; export type VolumeCloneOverrides = { @@ -11,20 +11,6 @@ export function VolumeMixin(Base: TBase) { abstract class VolumeMixin extends Base { gainNode?: GainNode; - get inputNode(): AudioNode { - if (!this.gainNode) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.gainNode; - } - - get outputNode(): AudioNode { - if (!this.gainNode) { - throw new Error('Cannot access nodes of a cleaned up sound'); - } - return this.gainNode; - } - setGainNode(gainNode: GainNode) { this.gainNode = gainNode; }