Skip to content

Commit

Permalink
Revert "feat: add audio node interface to BasePlayback (#16)" (#17)
Browse files Browse the repository at this point in the history
This reverts commit b4ee6df.
  • Loading branch information
ctoth authored Jan 28, 2025
1 parent b4ee6df commit 1b40659
Show file tree
Hide file tree
Showing 8 changed files with 3 additions and 251 deletions.
67 changes: 0 additions & 67 deletions src/basePlayback.test.ts

This file was deleted.

34 changes: 2 additions & 32 deletions src/basePlayback.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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();
}
}
8 changes: 0 additions & 8 deletions src/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
14 changes: 0 additions & 14 deletions src/oscillatorMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,6 @@ export function OscillatorMixin<TBase extends Constructor>(Base: TBase) {
_oscillatorOptions: Partial<OscillatorOptions> = {};
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<OscillatorOptions> {
return this._oscillatorOptions;
}
Expand Down
14 changes: 0 additions & 14 deletions src/pannerMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,6 @@ export function PannerMixin<TBase extends Constructor>(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;
}
Expand Down
87 changes: 0 additions & 87 deletions src/playback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand All @@ -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;
Expand Down
14 changes: 0 additions & 14 deletions src/playback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
16 changes: 1 addition & 15 deletions src/volumeMixin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioNode, GainNode } from "./context";
import { GainNode } from "./context";
import { FilterManager } from "./filters";

export type VolumeCloneOverrides = {
Expand All @@ -11,20 +11,6 @@ export function VolumeMixin<TBase extends Constructor>(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;
}
Expand Down

0 comments on commit 1b40659

Please sign in to comment.