From 06a9af4dc37c263298df79457f4a75e1aed87da5 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 11 Jun 2018 15:24:41 -0400 Subject: [PATCH] implement new SoundPlayer - Add GreenPlayer as SoundPlayer replacement - Standalone playback separate from an AudioPlayer - Each GreenPlayer has their own VolumeEffect for fading out - play() while already isPlaying fades out the last copy - stop() fades the sound out - stopImmediate() hard stops the sound --- src/GreenPlayer.js | 166 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 src/GreenPlayer.js diff --git a/src/GreenPlayer.js b/src/GreenPlayer.js new file mode 100644 index 0000000..76eda18 --- /dev/null +++ b/src/GreenPlayer.js @@ -0,0 +1,166 @@ +const {EventEmitter} = require('events'); + +const VolumeEffect = require('./effects/VolumeEffect'); + +class SoundPlayer extends EventEmitter { + constructor (audioEngine, {id, buffer}) { + super(); + + this.id = id; + + this.audioEngine = audioEngine; + this.buffer = buffer; + + this.outputNode = null; + this.target = null; + + this.initialized = false; + this.isPlaying = false; + this.playbackRate = 1; + + this.onEnd = this.onEnd.bind(this); + } + + onEnd () { + this.emit('stop'); + } + + initialize () { + this.outputNode = this.audioEngine.audioContext.createBufferSource(); + this.outputNode.playbackRate.value = this.playbackRate; + this.outputNode.buffer = this.buffer; + + this.outputNode.addEventListener('end', this.onEnd); + + this.volumeEffect = new VolumeEffect(this.audioEngine, this, null); + + this.initialized = true; + + if (this.target !== null) { + this.connect(this.target); + this.setPlaybackRate(this.playbackRate); + } + } + + connect (target) { + if (target === this.volumeEffect) { + this.outputNode.disconnect(); + this.outputNode.connect(this.volumeEffect.getInputNode()); + return; + } + + this.target = target; + + if (!this.initialized) { + return; + } + + this.volumeEffect.connect(target); + + return this; + } + + dispose () { + if (!this.initialized) { + return; + } + + this.stopImmediately(); + + this.volumeEffect.dispose(); + + this.outputNode.disconnect(); + this.outputNode = null; + + this.target = null; + + this.initialized = false; + } + + take () { + if (this.outputNode) { + this.outputNode.removeEventListener('end', this.onEnd); + } + + const taken = new SoundPlayer(this.audioEngine, this); + taken.outputNode = this.outputNode; + if (this.volumeEffect !== null) { + taken.volumeEffect.set(this.volumeEffect.value); + } + if (this.target !== null) { + taken.connect(this.target); + } + taken.initialized = this.initialized; + taken.isPlaying = this.isPlaying; + taken.playbackRate = this.playbackRate; + + if (this.isPlaying) { + this.emit('stop'); + taken.emit('play'); + } + + this.outputNode = null; + if (this.volumeEffect !== null) { + this.volumeEffect.dispose(); + } + this.volumeEffect = null; + this.target = null; + this.initialized = false; + this.isPlaying = false; + } + + play () { + if (this.isPlaying) { + // Spawn a Player with the current buffer source, and play for a + // short period until its volume is 0 and release it to be + // eventually garbage collected. + this.take().stop(); + } + + if (!this.initialized) { + this.initialize(); + } + + this.volumeEffect.set(this.volumeEffect.DEFAULT_VALUE); + this.outputNode.start(); + + this.isPlaying = true; + + this.emit('play'); + } + + stop () { + if (!this.isPlaying) { + return; + } + + this.volumeEffect.set(0); + this.outputNode.stop(this.audioEngine.audioEngineoContext.currentTime + this.audioEngine.DECAY_TIME); + + this.isPlaying = false; + + this.emit('stop'); + } + + stopImmediately () { + if (!this.isPlaying) { + return; + } + + this.outputNode.stop(); + + this.isPlaying = false; + + this.emit('stop'); + } + + setPlaybackRate (value) { + this.playbackRate = value; + + if (this.initialized) { + this.outputNode.playbackRate.value = value; + } + } +} + +module.exports = SoundPlayer;