From 7f1cdec78395797dd2710764031475a5727867ec Mon Sep 17 00:00:00 2001 From: Kevin Ngo Date: Mon, 12 Nov 2018 14:15:27 +0800 Subject: [PATCH] fix song preview time leaking into sync, don't use song current time until beat preload finish --- src/components/beat-loader.js | 71 ++++++++++++++++--------------- src/components/debug-song-time.js | 13 ++++++ src/components/song.js | 9 ++++ src/index.html | 1 + 4 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 src/components/debug-song-time.js diff --git a/src/components/beat-loader.js b/src/components/beat-loader.js index 200288a..2f9b173 100644 --- a/src/components/beat-loader.js +++ b/src/components/beat-loader.js @@ -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) { diff --git a/src/components/debug-song-time.js b/src/components/debug-song-time.js new file mode 100644 index 0000000..4ac726a --- /dev/null +++ b/src/components/debug-song-time.js @@ -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); + } +}); diff --git a/src/components/song.js b/src/components/song.js index 49e48e8..4c646e3 100644 --- a/src/components/song.js +++ b/src/components/song.js @@ -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 () { diff --git a/src/index.html b/src/index.html index 585db1a..82095b9 100644 --- a/src/index.html +++ b/src/index.html @@ -29,6 +29,7 @@ console-shortcuts debug-beat-loader debug-controller + debug-song-time debug-state effect-bloom="strength: 1" gpu-preloader