2018-06-11 15:24:41 -04:00
|
|
|
const {EventEmitter} = require('events');
|
|
|
|
|
|
|
|
const VolumeEffect = require('./effects/VolumeEffect');
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
const ON_ENDED = 'ended';
|
|
|
|
|
2018-06-11 15:24:41 -04:00
|
|
|
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;
|
2018-06-12 09:18:48 -04:00
|
|
|
}
|
2018-06-11 15:24:41 -04:00
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
/**
|
|
|
|
* Handle any event we have told the output node to listen for.
|
|
|
|
*/
|
|
|
|
handleEvent (event) {
|
|
|
|
if (event.type === ON_ENDED) {
|
|
|
|
this.onEnded();
|
|
|
|
}
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
onEnded () {
|
2018-06-11 15:24:41 -04:00
|
|
|
this.emit('stop');
|
2018-06-12 09:18:48 -04:00
|
|
|
|
|
|
|
this.isPlaying = false;
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
_createSource () {
|
|
|
|
if (this.outputNode !== null) {
|
|
|
|
this.outputNode.removeEventListener(ON_ENDED, this);
|
|
|
|
this.outputNode.disconnect();
|
|
|
|
}
|
|
|
|
|
2018-06-11 15:24:41 -04:00
|
|
|
this.outputNode = this.audioEngine.audioContext.createBufferSource();
|
|
|
|
this.outputNode.playbackRate.value = this.playbackRate;
|
|
|
|
this.outputNode.buffer = this.buffer;
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
this.outputNode.addEventListener(ON_ENDED, this);
|
2018-06-11 15:24:41 -04:00
|
|
|
|
|
|
|
if (this.target !== null) {
|
|
|
|
this.connect(this.target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
initialize () {
|
|
|
|
this.initialized = true;
|
|
|
|
|
|
|
|
this.volumeEffect = new VolumeEffect(this.audioEngine, this, null);
|
|
|
|
|
|
|
|
this._createSource();
|
|
|
|
}
|
|
|
|
|
2018-06-11 15:24:41 -04:00
|
|
|
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) {
|
2018-06-12 09:18:48 -04:00
|
|
|
this.outputNode.removeEventListener(ON_ENDED, this);
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const taken = new SoundPlayer(this.audioEngine, this);
|
2018-06-12 09:20:21 -04:00
|
|
|
taken.playbackRate = this.playbackRate;
|
|
|
|
if (this.isPlaying) {
|
|
|
|
taken.isPlaying = this.isPlaying;
|
|
|
|
taken.initialize();
|
|
|
|
taken.outputNode.disconnect();
|
|
|
|
taken.outputNode = this.outputNode;
|
|
|
|
taken.outputNode.addEventListener(ON_ENDED, taken);
|
2018-06-11 15:24:41 -04:00
|
|
|
taken.volumeEffect.set(this.volumeEffect.value);
|
2018-06-12 09:20:21 -04:00
|
|
|
if (this.target !== null) {
|
|
|
|
taken.connect(this.target);
|
|
|
|
}
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isPlaying) {
|
|
|
|
this.emit('stop');
|
|
|
|
taken.emit('play');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.outputNode = null;
|
|
|
|
if (this.volumeEffect !== null) {
|
|
|
|
this.volumeEffect.dispose();
|
|
|
|
}
|
|
|
|
this.volumeEffect = null;
|
|
|
|
this.initialized = false;
|
|
|
|
this.isPlaying = false;
|
2018-06-12 09:20:21 -04:00
|
|
|
|
|
|
|
return taken;
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2018-06-12 09:18:48 -04:00
|
|
|
} else {
|
|
|
|
this._createSource();
|
2018-06-11 15:24:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2018-06-12 09:18:48 -04:00
|
|
|
this.outputNode.stop(this.audioEngine.audioContext.currentTime + this.audioEngine.DECAY_TIME);
|
2018-06-11 15:24:41 -04:00
|
|
|
|
|
|
|
this.isPlaying = false;
|
|
|
|
|
|
|
|
this.emit('stop');
|
|
|
|
}
|
|
|
|
|
|
|
|
stopImmediately () {
|
|
|
|
if (!this.isPlaying) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.outputNode.stop();
|
|
|
|
|
|
|
|
this.isPlaying = false;
|
|
|
|
|
|
|
|
this.emit('stop');
|
|
|
|
}
|
|
|
|
|
2018-06-12 09:18:48 -04:00
|
|
|
finished () {
|
|
|
|
return new Promise(resolve => {
|
|
|
|
this.once('stop', resolve);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-06-11 15:24:41 -04:00
|
|
|
setPlaybackRate (value) {
|
|
|
|
this.playbackRate = value;
|
|
|
|
|
|
|
|
if (this.initialized) {
|
|
|
|
this.outputNode.playbackRate.value = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = SoundPlayer;
|