Files
junisaber/src/components/song.js
2018-10-16 22:03:02 -07:00

157 lines
5.1 KiB
JavaScript

const utils = require('../utils');
var ONCE = {once: true};
var BASE_VOLUME = 0.75;
/**
* Active challenge song / audio.
*/
AFRAME.registerComponent('song', {
schema: {
analyserEl: {type: 'selector', default: '#audioAnalyser'},
challengeId: {default: ''},
isBeatsPreloaded: {default: false},
isGameOver: {default: false},
isPlaying: {default: false}
},
init: function () {
this.analyserSetter = {buffer: true};
this.audioAnalyser = this.data.analyserEl.components.audioanalyser;
this.context = this.audioAnalyser.context;
this.songLoadingIndicator = document.getElementById('songLoadingIndicator');
this.victory = this.victory.bind(this);
// Base volume.
this.audioAnalyser.gainNode.gain.value = BASE_VOLUME;
this.el.addEventListener('gamemenurestart', this.onRestart.bind(this));
this.el.addEventListener('wallhitstart', this.onWallHitStart.bind(this));
this.el.addEventListener('wallhitend', this.onWallHitEnd.bind(this));
},
update: function (oldData) {
var data = this.data;
// Game over, slow down audio, and then stop.
if (!oldData.isGameOver && data.isGameOver) {
this.onGameOver();
return;
}
if (oldData.isGameOver && !data.isGameOver) {
this.audioAnalyser.gainNode.value = BASE_VOLUME;
}
// New challenge, play if we have loaded and were waiting for beats to preload.
if (!oldData.isBeatsPreloaded && this.data.isBeatsPreloaded && this.source) {
this.startAudio();
}
if (oldData.challengeId && !data.challengeId) {
this.stopAudio();
return;
}
// New challenge, load audio and play when ready.
if (oldData.challengeId !== data.challengeId && data.challengeId) {
this.el.sceneEl.emit('songloadstart', null, false);
this.getAudio().then(source => {
this.el.sceneEl.emit('songloadfinish', null, false);
if (this.data.isBeatsPreloaded) { this.startAudio(); }
}).catch(console.error);
}
// Pause / stop.
if (oldData.isPlaying && !data.isPlaying) {
this.audioAnalyser.suspendContext();
}
// Resume.
if (!oldData.isPlaying && data.isPlaying && this.source) {
this.audioAnalyser.resumeContext();
}
},
getAudio: function () {
const data = this.data;
return new Promise(resolve => {
data.analyserEl.addEventListener('audioanalyserbuffersource', evt => {
// Finished decoding.
this.source = evt.detail;
this.source.onended = this.victory;
resolve(this.source);
}, ONCE);
this.analyserSetter.src = utils.getS3FileUrl(data.challengeId, 'song.ogg');
data.analyserEl.setAttribute('audioanalyser', this.analyserSetter);
this.audioAnalyser.xhr.addEventListener('progress', evt => {
// Finished fetching.
this.onFetchProgress(evt);
});
});
},
stopAudio: function () {
if (!this.source) {
console.warn('[song] Tried to stopAudio, but not playing.');
return;
}
this.source.stop();
this.source.disconnect();
this.source = null;
},
victory: function () {
if (!this.data.isPlaying) { return; }
this.el.sceneEl.emit('victory', null, false);
},
onFetchProgress: function (evt) {
const progress = evt.loaded / evt.total;
this.songLoadingIndicator.setAttribute('geometry', 'thetaLength', progress * 360);
if (progress >= 1) { this.el.sceneEl.emit('songfetchfinish', null, false); }
},
onGameOver: function () {
this.source.playbackRate.setValueAtTime(this.source.playbackRate.value,
this.context.currentTime);
this.source.playbackRate.linearRampToValueAtTime(0, this.context.currentTime + 3.5);
this.audioAnalyser.gainNode.gain.setValueAtTime(this.audioAnalyser.gainNode.gain.value,
this.context.currentTime);
this.audioAnalyser.gainNode.gain.linearRampToValueAtTime(0, this.context.currentTime + 3.5);
setTimeout(() => {
if (!this.data.isGameOver) { return; }
this.stopAudio();
}, 3500);
},
onRestart: function () {
// Restart, get new buffer source node and play.
if (this.source) { this.source.disconnect(); }
this.data.analyserEl.addEventListener('audioanalyserbuffersource', evt => {
this.source = evt.detail;
this.el.sceneEl.emit('songloadfinish', null, false);
if (this.data.isBeatsPreloaded) { this.startAudio(); }
}, ONCE);
this.audioAnalyser.refreshSource();
},
onWallHitStart: function () {
const gain = this.audioAnalyser.gainNode.gain;
gain.linearRampToValueAtTime(0.2, this.context.currentTime + 0.1);
this.source.detune.linearRampToValueAtTime(-1200, this.context.currentTime + 0.1);
},
onWallHitEnd: function () {
const gain = this.audioAnalyser.gainNode.gain;
gain.linearRampToValueAtTime(BASE_VOLUME, this.context.currentTime + 0.2);
this.source.detune.linearRampToValueAtTime(0, this.context.currentTime + 0.2);
},
startAudio: function () {
this.audioAnalyser.gainNode.gain.setValueAtTime(BASE_VOLUME, this.context.currentTime);
this.source.start();
}
});