From 9a8c9afe20e17d82e5a5a6033980b027306fcbff Mon Sep 17 00:00:00 2001 From: Timothy Hale Date: Thu, 7 Mar 2024 12:49:34 +0100 Subject: [PATCH] audio-manager: Refactor stop() method and re-added emitter --- src/audio-manager.ts | 62 +++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/audio-manager.ts b/src/audio-manager.ts index 95a7294..989041e 100644 --- a/src/audio-manager.ts +++ b/src/audio-manager.ts @@ -1,5 +1,5 @@ import {_audioContext} from './audio-listener.js'; -import {Emitter, ListenerCallback} from '@wonderlandengine/api'; +import {Emitter} from '@wonderlandengine/api'; /* Ramp times of 0 cause a click, 5 ms should be sufficient */ const MIN_RAMP_TIME = 5 / 1000; @@ -7,8 +7,14 @@ const MIN_RAMP_TIME = 5 / 1000; const MIN_VOLUME = 0.001; export enum PlayState { + /* The source is ready to be played */ + READY, + /* The source has started playing */ PLAYING, + /* The source has been stopped */ STOPPED, + /* The source has reached the end of playback */ + ENDED, } /** @@ -129,7 +135,8 @@ class PlayableNode { private _audioNode: AudioBufferSourceNode = new AudioBufferSourceNode(_audioContext); private _destroy: boolean = false; private _rampTime: number = MIN_RAMP_TIME; - private _callback: ((state?: PlayState) => void) | undefined; + // @todo: Find out how costly this is to have per node + private _emitter: Emitter<[PlayState]> = new Emitter<[PlayState]>(); /** * Constructs a PlayableNode. @@ -145,8 +152,7 @@ class PlayableNode { this._audioManager = audioManager; this._source = src; this._gainNode.connect(_audioContext.destination); - // @todo: why is this necessary - this.stop = this.stop.bind(this); + this._emitter.notify(PlayState.READY); } /** @@ -227,40 +233,40 @@ class PlayableNode { throw 'playable-node: Invalid configuration for play()'; } } - this._audioNode.addEventListener('ended', this.stop); + this._audioNode.addEventListener('ended', () => { + this._handleEndedEvent(); + /* If node was stopped, isPlaying will be false already */ + if(this._isPlaying) { + this._isPlaying = false; + this._emitter.notify(PlayState.ENDED) + } + }); this._audioNode.start(); this._isPlaying = true; - if (this._callback) { - this._callback(PlayState.PLAYING); - } - } - - set listener(listener: (state?: PlayState) => void) { - this._callback = listener; + this._emitter.notify(PlayState.PLAYING); } - /** - * Stops the playback, and if set to destroy, removes associated audio file. - */ - stop() { - if (this.isPlaying) { - this._audioNode.removeEventListener('ended', this.stop); - this._audioNode.stop(); - } - if (this._audioNode !== undefined) { + private _handleEndedEvent() { + if (this._audioNode) { this._audioNode.disconnect(); } - if (this._pannerNode !== undefined) { + if (this._pannerNode) { this._pannerNode.disconnect(); } - this._isPlaying = false; if (this._destroy) { this._audioManager._remove(this._source); this._gainNode.disconnect(); } - if (this._callback) { - this._callback(PlayState.STOPPED); - } + } + + /** + * Stops the playback, and if set to destroy, removes associated audio file. + */ + stop() { + this._isPlaying = false + /* This triggers the 'ended' listener and frees the resources */ + this._audioNode.stop(); + this._emitter.notify(PlayState.STOPPED); } /** @@ -289,6 +295,10 @@ class PlayableNode { }, duration * 1000); } + get emitter(): Emitter<[PlayState]> { + return this._emitter; + } + /** * Checks if the audio node is currently playing. */