Merge pull request #174 from supermedium/sync

fix song preview time leaking into sync, don't use song current time until beat preload finish
This commit is contained in:
Kevin Ngo
2018-11-12 17:25:05 +08:00
committed by GitHub
4 changed files with 60 additions and 34 deletions

View File

@@ -67,9 +67,10 @@ AFRAME.registerComponent('beat-loader', {
this.beatDataProcessed = false;
this.beatContainer = document.getElementById('beatContainer');
this.beatsTime = undefined;
this.beatsTimeOffset = undefined;
this.beatsPreloadTime = 0;
this.beatsPreloadTimeTotal =
(this.data.beatAnticipationTime + this.data.beatWarmupTime) * 1000;
this.bpm = undefined;
this.songCurrentTime = undefined;
this.xhr = null;
this.stageColors = this.el.components['stage-colors'];
this.twister = document.getElementById('twister');
@@ -128,8 +129,8 @@ AFRAME.registerComponent('beat-loader', {
processBeats: function () {
// Reset variables used during playback.
// Beats spawn ahead of the song and get to the user in sync with the music.
this.beatsTimeOffset = (this.data.beatAnticipationTime + this.data.beatWarmupTime) * 1000;
this.beatsTime = 0;
this.beatsPreloadTime = 0;
this.beatData._events.sort(lessThan);
this.beatData._obstacles.sort(lessThan);
this.beatData._notes.sort(lessThan);
@@ -151,60 +152,62 @@ AFRAME.registerComponent('beat-loader', {
* Generate beats and stuff according to timestamp.
*/
tick: function (time, delta) {
var i;
var noteTime;
if (!this.data.isPlaying || !this.data.challengeId || !this.beatData) { return; }
// Re-sync song with beats playback.
const songComponent = this.el.components.song;
const currentTime = songComponent.getCurrentTime();
if (songComponent.songStartTime && this.beatsTimeOffset !== undefined &&
this.songCurrentTime !== currentTime) {
this.songCurrentTime = currentTime;
this.beatsTime = (this.songCurrentTime + this.data.beatAnticipationTime) * 1000;
const prevBeatsTime = this.beatsTime;
if (this.beatsPreloadTime === undefined) {
// Get current song time.
const song = this.el.components.song;
if (!song.isPlaying) { return; }
this.beatsTime = (song.getCurrentTime() + this.data.beatAnticipationTime) * 1000;
if (this.beatsTime <= this.beatsPreloadTimeTotal) { return; }
} else {
// Song is not playing and is preloading beats, use maintained beat time.
this.beatsTime = this.beatsPreloadTime;
}
// Load in stuff scheduled between the last timestamp and current timestamp.
// Beats.
const beatsTime = this.beatsTime;
const bpm = this.beatData._beatsPerMinute;
const msPerBeat = 1000 * 60 / this.beatData._beatsPerMinute;
const notes = this.beatData._notes;
for (i = 0; i < notes.length; ++i) {
noteTime = notes[i]._time * msPerBeat;
if (noteTime > beatsTime && noteTime <= (beatsTime + delta)) {
for (let i = 0; i < notes.length; ++i) {
let noteTime = notes[i]._time * msPerBeat;
if (noteTime > prevBeatsTime && noteTime <= beatsTime) {
notes[i].time = noteTime;
this.generateBeat(notes[i]);
}
}
// Walls.
const obstacles = this.beatData._obstacles;
for (i=0; i < obstacles.length; ++i) {
noteTime = obstacles[i]._time * msPerBeat;
if (noteTime > beatsTime && noteTime <= beatsTime + delta) {
for (let i = 0; i < obstacles.length; ++i) {
let noteTime = obstacles[i]._time * msPerBeat;
if (noteTime > prevBeatsTime && noteTime <= beatsTime) {
this.generateWall(obstacles[i]);
}
}
// Stage events.
const events = this.beatData._events;
for (i=0; i < events.length; ++i) {
noteTime = events[i]._time * msPerBeat;
if (noteTime > beatsTime && noteTime <= beatsTime + delta) {
for (let i = 0; i < events.length; ++i) {
let noteTime = events[i]._time * msPerBeat;
if (noteTime > prevBeatsTime && noteTime <= beatsTime) {
this.generateEvent(events[i]);
}
}
if (this.beatsTimeOffset !== undefined) {
if (this.beatsTimeOffset <= 0) {
this.el.sceneEl.emit('beatloaderpreloadfinish', null, false);
this.songCurrentTime = this.el.components.song.context.currentTime;
this.beatsTimeOffset = undefined;
} else {
this.beatsTimeOffset -= delta;
}
}
if (this.beatsPreloadTime === undefined) { return; }
this.beatsTime = beatsTime + delta;
if (this.beatsPreloadTime >= this.beatsPreloadTimeTotal) {
// Finished preload.
this.el.sceneEl.emit('beatloaderpreloadfinish', null, false);
this.beatsPreloadTime = undefined;
} else {
// Continue preload.
this.beatsPreloadTime += delta;
}
},
generateBeat: (function () {
@@ -321,8 +324,8 @@ AFRAME.registerComponent('beat-loader', {
* Restart by returning all beats to pool.
*/
clearBeats: function () {
this.beatsPreloadTime = 0;
this.beatsTime = 0;
this.beatsTimeOffset = (this.data.beatAnticipationTime + this.data.beatWarmupTime) * 1000;
for (let i = 0; i < this.beatContainer.children.length; i++) {
let child = this.beatContainer.children[i];
if (child.components.beat) {

View File

@@ -0,0 +1,13 @@
/**
* Emit events from query parameter to state to automatically set up state.
*/
AFRAME.registerComponent('debug-song-time', {
dependencies: ['song'],
init: function () {
if (!AFRAME.utils.getUrlParameter('debug-song-time')) { return; }
setInterval(() => {
console.log(this.el.components.song.getCurrentTime());
}, 250);
}
});

View File

@@ -31,6 +31,7 @@ AFRAME.registerComponent('song', {
this.analyserSetter = {buffer: true};
this.audioAnalyser = this.data.analyserEl.components.audioanalyser;
this.context = this.audioAnalyser.context;
this.isPlaying = false;
this.songLoadingIndicator = document.getElementById('songLoadingIndicator');
this.songStartTime = 0;
@@ -110,6 +111,8 @@ AFRAME.registerComponent('song', {
getAudio: function () {
const data = this.data;
this.isPlaying = false;
return new Promise(resolve => {
data.analyserEl.addEventListener('audioanalyserbuffersource', evt => {
// Finished decoding.
@@ -140,6 +143,7 @@ AFRAME.registerComponent('song', {
this.source.stop();
this.source.disconnect();
this.source = null;
this.isPlaying = false;
},
victory: function () {
@@ -156,6 +160,8 @@ AFRAME.registerComponent('song', {
},
onGameOver: function () {
this.isPlaying = false;
// Playback rate.
const playbackRate = this.source.playbackRate;
playbackRate.setValueAtTime(playbackRate.value, this.context.currentTime);
@@ -173,6 +179,8 @@ AFRAME.registerComponent('song', {
},
onRestart: function () {
this.isPlaying = false;
// Restart, get new buffer source node and play.
if (this.source) { this.source.disconnect(); }
@@ -203,6 +211,7 @@ AFRAME.registerComponent('song', {
this.songStartTime = this.context.currentTime;
this.source.onended = this.victory;
this.source.start();
this.isPlaying = true;
},
getCurrentTime: function () {

View File

@@ -29,6 +29,7 @@
console-shortcuts
debug-beat-loader
debug-controller
debug-song-time
debug-state
effect-bloom="strength: 1"
gpu-preloader