From 0feb31227af8a474e00ff81bd1b1b4f459ae7d42 Mon Sep 17 00:00:00 2001 From: LoveEevee Date: Tue, 11 Sep 2018 01:17:13 +0300 Subject: [PATCH] Buffer every sound asset --- public/index.html | 54 +++++----- public/src/css/main.css | 2 +- public/src/js/assets.js | 33 +++--- public/src/js/bufferedloop.js | 92 ---------------- public/src/js/controller.js | 39 +++---- public/src/js/game.js | 35 +++--- public/src/js/keyboard.js | 4 +- public/src/js/loader.js | 163 +++++++++++++++------------- public/src/js/loadsong.js | 104 ++++++++---------- public/src/js/main.js | 7 -- public/src/js/scoresheet.js | 61 +++++------ public/src/js/songselect.js | 160 +++++++++++++--------------- public/src/js/soundbuffer.js | 193 ++++++++++++++++++++++++++++++++++ public/src/js/soundsystem.js | 48 --------- public/src/js/titlescreen.js | 4 +- public/src/js/tutorial.js | 10 +- public/src/js/view.js | 7 +- 17 files changed, 510 insertions(+), 506 deletions(-) delete mode 100644 public/src/js/bufferedloop.js delete mode 100644 public/src/js/main.js create mode 100644 public/src/js/soundbuffer.js delete mode 100644 public/src/js/soundsystem.js diff --git a/public/index.html b/public/index.html index 44a11a4..f88b462 100644 --- a/public/index.html +++ b/public/index.html @@ -12,8 +12,9 @@ - 太鼓の達人ウェブ - Taiko no Tatsujin Web + + @@ -23,37 +24,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
+ \ No newline at end of file diff --git a/public/src/css/main.css b/public/src/css/main.css index 5a41678..064fc54 100644 --- a/public/src/css/main.css +++ b/public/src/css/main.css @@ -23,7 +23,7 @@ html, body{ height:100%; margin:0; padding:0; - background: url('/assets/img/bg-pattern-1.png'); + background: url('/assets/img/bg-pattern-1.png') top center; } #assets{ diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 55f1143..4d74296 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -1,6 +1,6 @@ var assets = { - img: new Array( + img: [ 'background.png', 'title-screen.png', 'logo-big.png', @@ -28,13 +28,13 @@ var assets = { 'muzu_hard.png', 'muzu_oni.png', 'don_anim_normal.png' - ), + ], - audio: new Array( + audioSfx: [ 'start.wav', 'don.wav', 'ka.wav', - + 'combo-50.wav', 'combo-100.wav', 'combo-200.wav', @@ -51,7 +51,7 @@ var assets = { 'combo-1300.wav', 'combo-1400.wav', 'fullcombo.wav', - + 'combo-50-meka.wav', 'combo-100-meka.wav', 'combo-200-meka.wav', @@ -68,35 +68,34 @@ var assets = { 'combo-1300-meka.wav', 'combo-1400-meka.wav', 'fullcombo-meka.wav', - + 'song-select.wav', 'title.ogg', 'pause.wav', 'cancel.wav', 'results.wav', 'diffsel.wav', - + 'gamefullcombo.wav', 'gameclear.wav', 'gamefail.wav', - + 'note_don.ogg', 'note_ka.ogg', - + ], + + audioMusic:[ 'bgm_songsel.ogg', - 'bgm_songsel_loop.ogg', 'bgm_result.ogg', - 'bgm_result_loop.ogg', - 'bgm_setsume.ogg', - 'bgm_setsume_loop.ogg' - ), + 'bgm_setsume.ogg' + ], - songs: new Array(), + songs: [], - fonts: new Array( + fonts: [ 'Kozuka', 'TnT' - ), + ], sounds: {}, image: {} diff --git a/public/src/js/bufferedloop.js b/public/src/js/bufferedloop.js deleted file mode 100644 index 4c8e405..0000000 --- a/public/src/js/bufferedloop.js +++ /dev/null @@ -1,92 +0,0 @@ -// thx to @LoveEevee for this - https://github.com/LoveEevee - -class BufferedLoop{ - constructor(bgm1,bgm2){ - this.context=new AudioContext() - this.buffers=[] - this.sources=new Set() - this.loadCallback=[] - this.bufferedTime=0 - this.bgm1=bgm1 - this.bgm2=bgm2 - this.loadSound(bgm1.url,0) - this.loadSound(bgm2.url,1) - } - loadSound(url,number){ - var self=this - var request=new XMLHttpRequest() - request.open("GET",url) - request.responseType="arraybuffer" - request.onload=function(){ - self.context.decodeAudioData(request.response,function(buffer){ - self.buffers[number]=buffer - self.setLoaded() - }) - } - request.send() - } - setLoaded(){ - if(this.buffers[0]&&this.buffers[1]){ - this.loaded=true - for(var i in this.loadCallback){ - this.loadCallback[i]() - } - } - } - onLoad(callback){ - this.loadCallback.push(callback) - } - playSound(buffer,time,duration){ - var self=this - var source=this.context.createBufferSource() - source.buffer=buffer - source.connect(this.context.destination) - source.start(time) - this.bufferedTime=time+duration - var sourceObject={ - source:source, - timeout:setTimeout(function(){ - self.sources.delete(sourceObject) - },duration*1000) - } - this.sources.add(sourceObject) - } - addLoop(){ - if(this.context.currentTime>this.bufferedTime-1){ - this.playSound( - this.buffers[1], - this.start+this.bgm1.duration+this.bgm2.duration*this.iteration, - this.bgm2.duration - ) - this.iteration++ - } - } - play(){ - var self=this - if(!this.loaded){ - return this.onLoad(function(){ - self.play() - }) - } - this.start=this.context.currentTime+0.1 - this.iteration=0 - this.playSound( - this.buffers[0], - this.start, - this.bgm1.duration - ) - self.addLoop() - self.interval=setInterval(function(){ - self.addLoop() - },100) - } - pause(){ - var self=this - clearInterval(this.interval) - this.sources.forEach(function(sourceObject){ - sourceObject.source.stop(0) - clearTimeout(sourceObject.timeout) - self.sources.delete(sourceObject) - }) - } -} diff --git a/public/src/js/controller.js b/public/src/js/controller.js index e90a53f..9350649 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -12,6 +12,12 @@ function Controller(selectedSong, songData, autoPlayEnabled){ var _keyboard = new Keyboard(this); var _mainLoop; var _pauseMenu = false; + var _mainAsset + assets.songs.forEach(song => { + if(song.id == selectedSong.folder){ + _mainAsset = song.sound + } + }) this.autoPlayEnabled = autoPlayEnabled @@ -25,11 +31,11 @@ function Controller(selectedSong, songData, autoPlayEnabled){ this.loadUIEvents = function(){ $("#song-selection-butt").click(function(){ - assets.sounds["don"].playAsset(); + assets.sounds["don"].play(); _this.songSelection(); }); $("#restart-butt").click(function(){ - assets.sounds["don"].playAsset(); + assets.sounds["don"].play(); _this.restartSong(); }); $("#continue-butt").click(function(){ @@ -48,10 +54,7 @@ function Controller(selectedSong, songData, autoPlayEnabled){ _view.refresh(); } else if(ms>=0 && !started){ //when music shall starts - setTimeout(function(){ - assets.sounds["main-music"].volume = 0.7; - assets.sounds["main-music"].playAsset(); - }, _songData.generalInfo.audioWait); + _mainAsset.play(_songData.generalInfo.audioWait) started=true; } @@ -85,20 +88,17 @@ function Controller(selectedSong, songData, autoPlayEnabled){ if (score.fail == 0) { vp = 'fullcombo'; - setTimeout(function(){ - _this.playSoundMeka('fullcombo'); - }, 1350); + _this.playSoundMeka('fullcombo', 1350); } else if (score.hp >= 50) { vp = 'clear'; } else { vp = 'fail'; } - assets.sounds['game' + vp].playAsset(); + assets.sounds['game' + vp].play(); setTimeout(function(){ - var scoresheet = new Scoresheet(_this, _this.getGlobalScore()); - scoresheet.run(); + new Scoresheet(_this, _this.getGlobalScore()); }, 7000); } @@ -123,25 +123,16 @@ function Controller(selectedSong, songData, autoPlayEnabled){ } this.restartSong = function(){ - assets.sounds["main-music"].pause(); - assets.sounds["main-music"].currentTime=0; + _mainAsset.stop() clearInterval(_mainLoop); $("#screen").load("/src/views/game.html", function(){ var taikoGame = new Controller(selectedSong, songData, autoPlayEnabled); taikoGame.run(); }); } - - this.playSound = function(soundID){ - _game.playSound(soundID); - } - this.playSoundMeka = function(soundID){ - _game.playSound(soundID + (autoPlayEnabled ? '-meka' : '')); - } - - this.pauseSound = function(soundID, stop){ - _game.pauseSound(soundID, stop); + this.playSoundMeka = function(soundID, time){ + assets.sounds[soundID + (autoPlayEnabled ? '-meka' : '')].play(time) } this.initTiming = function(){ diff --git a/public/src/js/game.js b/public/src/js/game.js index b5ec79d..5d1ee77 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -6,7 +6,6 @@ function Game(controller, selectedSong, songData){ var _offsetDate; //date when the chrono is started (before the game begins) var _startDate; //real start date (when the chrono will be 0) var _currentDate; // refreshed date - var _soundSystem = new soundSystem(controller); var _songData=songData; var _currentCircle=0; var _currentScore=0; @@ -24,6 +23,12 @@ function Game(controller, selectedSong, songData){ var _offsetTime=0; var _hitcircleSpeed=_songData.difficulty.sliderMultiplier*8; var _timeForDistanceCircle; + var _mainAsset + assets.songs.forEach(song => { + if(song.id == selectedSong.folder){ + _mainAsset = song.sound + } + }) this.run = function(){ _timeForDistanceCircle=((20*controller.getDistanceForCircle())/_hitcircleSpeed); @@ -185,20 +190,22 @@ function Game(controller, selectedSong, songData){ this.whenLastCirclePlayed = function(){ var circles = _songData.circles; var lastCircle = circles[_songData.circles.length-1]; - if(_ellapsedTime.ms>=lastCircle.getMS()+2000){ - _fadeOutStarted=true; + if(!_fadeOutStarted && _ellapsedTime.ms>=lastCircle.getMS()+2000){ + _fadeOutStarted=_ellapsedTime.ms } } this.whenFadeoutMusic = function(){ if(_fadeOutStarted){ - if(_musicFadeOut%8==0){ - _soundSystem.fadeOutMusic(); - _musicFadeOut++; + if(_musicFadeOut==0){ + snd.musicGain.fadeOut(1.6) + _mainAsset.stop(1.6) + snd.musicGain.fadeIn(0, 1.7) } - else{ - _musicFadeOut++; + if(_ellapsedTime.ms>=_fadeOutStarted+1600){ + controller.fadeOutOver() } + _musicFadeOut++; } } @@ -217,11 +224,11 @@ function Game(controller, selectedSong, songData){ this.toggleMainMusic = function(){ if(_mainMusicPlaying){ - assets.sounds["main-music"].pause(); + _mainAsset.pause(); _mainMusicPlaying=false; } else{ - assets.sounds["main-music"].playAsset(); + _mainAsset.play(0, false, _this.getEllapsedTime().ms / 1000); _mainMusicPlaying=true; } } @@ -230,10 +237,6 @@ function Game(controller, selectedSong, songData){ _fadeOutStarted=false; } - this.playSound = function(soundID){ - _soundSystem.playSound(soundID); - } - this.pauseSound = function(soundID, stop){ _soundSystem.pauseSound(soundID, stop); } @@ -244,14 +247,14 @@ function Game(controller, selectedSong, songData){ this.togglePause = function(){ if(!_paused){ - assets.sounds["pause"].playAsset(); + assets.sounds["pause"].play(); _paused=true; _latestDate = new Date(); _this.toggleMainMusic(); } else{ - assets.sounds["cancel"].playAsset(); + assets.sounds["cancel"].play(); _paused=false; var currentDate = new Date(); _ellapsedTimeSincePause = _ellapsedTimeSincePause + Math.abs(currentDate.getTime() - _latestDate.getTime()); diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index 5b31b16..e8b0842 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -67,7 +67,7 @@ function Keyboard(controller){ this.checkMenuKeys = function(){ _gamepad.play(1) _this.checkKey(_kbd["back"], "menu", function(){ - controller.pauseSound("main-music", true); + controller.togglePause(); controller.songSelection(); }) _this.checkKey(_kbd["pause"], "menu", function(){ @@ -84,7 +84,7 @@ function Keyboard(controller){ this.checkKeySound = function(keyCode, sound){ _this.checkKey(keyCode, "sound", function(){ - controller.playSound("note_"+sound); + assets.sounds["note_"+sound].play() _keyTime[sound] = controller.getEllapsedTime().ms }) } diff --git a/public/src/js/loader.js b/public/src/js/loader.js index 7f71645..f1c8e55 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -1,92 +1,103 @@ -function Loader(){ - - var _this=this; - var _loadedAssets=0; - var _percentage=0; - var _nbAssets=assets.audio.length+assets.img.length+assets.fonts.length+1; //+1 for song structures - var _assetsDiv=document.getElementById("assets") - var _loaderPercentage - var _errorCount=0 - - this.run = function(){ +class Loader{ + constructor(){ + this.loadedAssets = 0 + this.errorCount = 0 + this.assetsDiv = document.getElementById("assets") + this.promises = [] + $("#screen").load("/src/views/loader.html", () => { + this.run() + }) + } + run(){ + this.loaderPercentage = document.querySelector("#loader .percentage") - _loaderPercentage = document.querySelector("#loader .percentage") - - assets.fonts.forEach(function(name){ + assets.fonts.forEach(name => { var font = document.createElement("h1") font.style.fontFamily = name font.appendChild(document.createTextNode("I am a font")) - _assetsDiv.appendChild(font) - FontDetect.onFontLoaded (name, _this.assetLoaded, _this.errorMsg, {msTimeout: 90000}); - }); + this.assetsDiv.appendChild(font) + this.promises.push(new Promise((resolve, reject) => { + FontDetect.onFontLoaded(name, resolve, reject, {msTimeout: 90000}) + })) + }) - assets.img.forEach(function(name){ - var id = name.substr(0, name.length-4); + assets.img.forEach(name => { + var id = name.substr(0, name.length - 4) var image = document.createElement("img") - image.addEventListener("load", event=>{ - _this.assetLoaded(); - }) + this.promises.push(promiseLoad(image)) image.id = name image.src = "/assets/img/" + name - _assetsDiv.appendChild(image) + this.assetsDiv.appendChild(image) assets.image[id] = image - }); + }) - assets.audio.forEach(function(name){ - var id = name.substr(0, name.length-4); - assets.sounds[id] = new Audio(); - assets.sounds[id].muted = true; - assets.sounds[id].playAsset = function(){ - try{ - assets.sounds[id].muted = false; - assets.sounds[id].play() - }catch(e){ - console.warn(e) - } - } - assets.sounds[id].onloadeddata = function(){ - _this.assetLoaded(); - }; - assets.sounds[id].src = '/assets/audio/'+name; - assets.sounds[id].load(); - }); + snd.buffer = new SoundBuffer() + snd.musicGain = snd.buffer.createGain() + snd.sfxGain = snd.buffer.createGain() + snd.buffer.setCrossfade(snd.musicGain, snd.sfxGain, 0.5) + snd.previewGain = snd.buffer.createGain() + snd.previewGain.setVolume(0.5) - $.ajax({ - url: "/api/songs", - mimeType: "application/json", - success: function(songs){ - assets.songs = songs; - _this.assetLoaded(); - }, - error: _this.errorMsg - }); + assets.audioSfx.forEach(name => { + var id = name.substr(0, name.length-4) + this.promises.push(snd.sfxGain.load("/assets/audio/" + name).then(sound => { + assets.sounds[id] = sound + })) + }) + assets.audioMusic.forEach(name => { + var id = name.substr(0, name.length-4) + this.promises.push(snd.musicGain.load("/assets/audio/" + name).then(sound => { + assets.sounds[id] = sound + })) + }) + this.promises.push(ajax("/api/songs").then(songs => { + assets.songs = JSON.parse(songs) + })) + + this.promises.forEach(promise => { + promise.then(() => { + this.assetLoaded() + }, () => { + this.errorMsg() + }) + }) + + Promise.all(this.promises).then(() => { + new Titlescreen() + }) } - - this.errorMsg = function(){ - if(_errorCount == 0){ - _loaderPercentage.appendChild(document.createElement("br")) - _loaderPercentage.appendChild(document.createTextNode("An error occured, please refresh")) + errorMsg(){ + if(this.errorCount == 0){ + this.loaderPercentage.appendChild(document.createElement("br")) + this.loaderPercentage.appendChild(document.createTextNode("An error occurred, please refresh")) } - _errorCount++ + this.errorCount++ } - - this.assetLoaded = function(){ - _loadedAssets++; - _percentage=parseInt((_loadedAssets*100)/_nbAssets); - $("#loader .progress").css("width", _percentage+"%"); - _loaderPercentage.firstChild.data=_percentage+"%" - _this.checkIfEverythingLoaded(); + assetLoaded(){ + this.loadedAssets++ + var percentage = parseInt(this.loadedAssets * 100 / this.promises.length) + document.querySelector("#loader .progress").style.width = percentage + "%" + this.loaderPercentage.firstChild.data = percentage + "%" } - - this.checkIfEverythingLoaded = function(){ - if(_percentage==100){ - new Titlescreen(); - //var globalScore={points:1000, great:100, good:60, fail:10, maxCombo:50, hp:90}; - //new Scoresheet(null, globalScore); - } - } - - $("#screen").load("/src/views/loader.html", _this.run); - -} \ No newline at end of file +} +function ajax(url){ + return new Promise((resolve, reject) => { + var request = new XMLHttpRequest() + request.open("GET", url) + promiseLoad(request).then(() => { + resolve(request.response) + }, reject) + request.send() + }) +} +function promiseLoad(asset){ + return new Promise((resolve, reject) => { + asset.addEventListener("load", resolve) + asset.addEventListener("error", reject) + asset.addEventListener("abort", reject) + }) +} + +var snd = {} +new Loader() diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index a7b91a5..f0bbc94 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -1,66 +1,52 @@ -function loadSong(selectedSong, autoPlayEnabled){ - - var _this = this; - var _selectedSong=selectedSong; - var _bgLoaded=false; - var _musicLoaded=false; - var _songDataLoaded=false; - var _songFilePath = '/songs/'+_selectedSong.folder+'/'+_selectedSong.difficulty; - var _songData; - - this.run = function(){ +class loadSong{ + constructor(selectedSong, autoPlayEnabled){ + this.selectedSong = selectedSong + this.autoPlayEnabled = autoPlayEnabled + this.songFilePath = "/songs/" + this.selectedSong.folder + "/" + this.selectedSong.difficulty + $("#screen").load("/src/views/loadsong.html", () => { + this.run() + }) + } + run(){ + var id = this.selectedSong.folder + var promises = [] + assets.sounds["start"].play() - assets.sounds["bgm_songsel"].pause(); - assets.sounds["bgm_songsel"].currentTime = 0; - - assets.sounds["start"].playAsset(); - $("#assets").append(""); + var img = document.createElement("img") + promises.push(promiseLoad(img)) + img.id = "music-bg" + img.src = "/songs/" + id + "/bg.png" + document.getElementById("assets").appendChild(img) - var audio = new Audio(); - audio.muted = true; - audio.src = '/songs/'+_selectedSong.folder+'/main.mp3'; - audio.load(); - - $("#music-bg").load(function(){ - _bgLoaded=true; - _this.checkIfEverythingLoaded(); - }); - - audio.playAsset = function(){ - try{ - audio.muted = false; - audio.play() - }catch(e){ - console.warn(e) + promises.push(new Promise((resolve, reject) => { + var songObj + assets.songs.forEach(song => { + if(song.id == id){ + songObj = song + } + }) + if(songObj.sound){ + songObj.sound.gain = snd.musicGain + resolve() + }else{ + snd.musicGain.load("/songs/" + id + "/main.mp3").then(sound => { + songObj.sound = sound + resolve() + }, reject) } - } + })) - audio.onloadeddata = function(){ - _musicLoaded=true; - assets.sounds["main-music"]=audio; - _this.checkIfEverythingLoaded(); - }; - - $.ajax({ - url : _songFilePath, - dataType: "text", - success : function (data) { - _songData = data.split("\n"); - _songDataLoaded=true; - _this.checkIfEverythingLoaded(); - } - }); + promises.push(ajax(this.songFilePath).then(data => { + this.songData = data.replace(/\0/g, "").split("\n") + })) + Promise.all(promises).then(() => { + $("#screen").load("/src/views/game.html", () => { + var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled) + taikoGame.run() + }) + }, () => { + alert("An error occurred, please refresh") + }) } - - this.checkIfEverythingLoaded = function(){ - if(_musicLoaded && _songDataLoaded && _bgLoaded){ - $("#screen").load("/src/views/game.html", function(){ - var taikoGame = new Controller(_selectedSong, _songData, autoPlayEnabled); - taikoGame.run(); - }); - } - } - - $("#screen").load("/src/views/loadsong.html", _this.run); } \ No newline at end of file diff --git a/public/src/js/main.js b/public/src/js/main.js deleted file mode 100644 index c305166..0000000 --- a/public/src/js/main.js +++ /dev/null @@ -1,7 +0,0 @@ -$(document).ready(function(){ - - new Loader(); - -}); - - diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index d5a9eb7..7280ada 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -1,18 +1,18 @@ function Scoresheet(controller, score){ - - var _this = this; - var _score = score; - var _mark; + + var _this = this; + var _score = score; + var _mark; this.setResults = function(){ if (_score.fail == 0) { - _mark = 'gold'; - } else if (_score.hp >= 50) { - _mark = 'silver'; - }; - + _mark = 'gold'; + } else if (_score.hp >= 50) { + _mark = 'silver'; + }; + var imgW = (_score.hp*$("#score-hp-bar-colour").width())/100; $("#score-hp-bar-colour img").css("clip", "rect(0, "+imgW+"px, "+$("#score-hp-bar-colour").height()+"px, 0)"); @@ -23,13 +23,13 @@ function Scoresheet(controller, score){ } else { $("#score-mark").remove(); }; - + $("#score-points").html(_score.points+"点"); $("#nb-great").html(_score.great); $("#nb-good").html(_score.good); $("#nb-fail").html(_score.fail); $("#max-combo").html(_score.maxCombo); - + $('.result-song').attr('alt', _score.song).html(_score.song); } @@ -76,36 +76,29 @@ function Scoresheet(controller, score){ } - this.run = function(){ + this.run = function(){ _this.positionning(); _this.setResults(); - $("#song-select").click(function(){ - assets.sounds["don"].playAsset(); - bgm.pause(); - controller.songSelection(); - }); - - $("#replay").click(function(){ - assets.sounds["don"].playAsset(); - bgm.pause(); - controller.restartSong(); - }); + $("#song-select").click(function(){ + assets.sounds["don"].play(); + assets.sounds["bgm_result"].stop(); + controller.songSelection(); + }); + + $("#replay").click(function(){ + assets.sounds["don"].play(); + assets.sounds["bgm_result"].stop(); + controller.restartSong(); + }); $(window).resize(_this.positionning); - } - - assets.sounds["results"].playAsset(); + } + + assets.sounds["results"].play() + assets.sounds["bgm_result"].playLoop(0, false, 0, 0.846625) - bgm = new BufferedLoop( - {url: '/assets/audio/bgm_result.ogg', duration: 0.847}, - {url: '/assets/audio/bgm_result_loop.ogg', duration: 16.842} - ); - bgm.play(); - - - $("#screen").load("/src/views/scoresheet.html", _this.run); } \ No newline at end of file diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index b741f2b..7ab7bdd 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -1,82 +1,88 @@ function SongSelect(){ - - var _this=this; - var _songs; + var _this=this; + var _songs; var _selectedSong = {title:'', folder:'', difficulty:''}; var _preview; - var _preview_to; + var _preview_ended + var _preview_startLoad var _diffNames={ easy:"かんたん", normal:"ふつう", hard:"むずかしい", oni:"おに" } - + this.startPreview = function(id, prvtime, first_open=true) { - var start = Date.now(); - setTimeout(function(){ - bgm.pause(); - }, 400); - - _preview = new Audio('/songs/' + id + '/main.mp3'); - _preview.onloadeddata = function() { - var end = Date.now(); - var delay = end - start; - var no_delay = first_open ? 0 : 300; - - _preview.currentTime = prvtime/1000; - _preview.volume = 0.5; - - _preview.addEventListener('ended', function(){ - this.currentTime = prvtime/1000; - this.play(); - }, false); - - _preview_to = setTimeout(function(){ - _preview.play(); - }, delay <= 1000 && first_open ? 1000 : no_delay); + _this.endPreview(); + _preview_startLoad = +new Date + _preview_ended = false + if(first_open){ + snd.musicGain.fadeOut(0.4) + } + var songObj + assets.songs.forEach(song => { + if(song.id == id){ + songObj = song + } + }) + if(songObj.sound){ + _preview = songObj.sound + _preview.gain = snd.previewGain + this.previewLoaded(prvtime) + }else{ + snd.previewGain.load("/songs/" + id + "/main.mp3").then(sound => { + if(!_preview_ended){ + songObj.sound = sound + _preview = sound + this.previewLoaded(prvtime) + } + }) } }; - + + this.previewLoaded = function(prvtime){ + var endLoad = +new Date + var delay = Math.max(1000 - Math.min(1000, endLoad - _preview_startLoad), 300) + _preview.playLoop(delay / 1000, false, prvtime / 1000) + } + this.endPreview = function() { - clearTimeout(_preview_to); + _preview_ended = true if (_preview) { - _preview.pause(); + _preview.stop(); }; }; - - this.run = function(){ - + + this.run = function(){ _this.createCode(); - _this.display(); - $(window).resize(_this.display); - var menuLoop = setInterval(_this.refresh, 20); $("#song-container").show(); - + $('#songsel-help').click(function(){ - bgm.pause(); + assets.sounds["bgm_songsel"].stop() + assets.sounds["song-select"].stop() + assets.sounds["diffsel"].stop() + assets.sounds["don"].play() + snd.musicGain.fadeIn() _this.endPreview(); - assets.sounds['don'].playAsset(); - + new Tutorial(); }); $(".difficulty").click(function(e){ _this.endPreview(); - assets.sounds["diffsel"].pause(); - assets.sounds["diffsel"].currentTime = 0; - assets.sounds["don"].playAsset(); - - clearInterval(menuLoop); + assets.sounds["bgm_songsel"].stop() + assets.sounds["diffsel"].stop() + assets.sounds["don"].play() + var difficultyElement = (e.target.className=="stars" || e.target.className=="diffname") ? e.target.parentElement : e.target; _selectedSong.difficulty = difficultyElement.classList[1]+'.osu'; var parentID = $(this).parent().closest(".song").attr("id"); var songID = parseInt(parentID.substr(5, parentID.length-1)); _selectedSong.title = $(this).parent().closest('.song').data('title'); _selectedSong.folder = songID; - - bgm.pause(); + + snd.musicGain.fadeIn() new loadSong(_selectedSong, e.shiftKey); }); @@ -93,46 +99,38 @@ function SongSelect(){ if (!$(e.target).parents('.difficulties').length) { if ($(".opened").length && $(".opened").attr('id') == $(this).attr('id')) { _this.endPreview(); - bgm.play(); - assets.sounds["cancel"].playAsset(); + snd.musicGain.fadeIn(0.4) + assets.sounds["diffsel"].stop() + assets.sounds["cancel"].play() + assets.sounds["song-select"].play(0.3) + $(".difficulty").hide(); $(".opened").removeClass("opened", 300); - - assets.sounds["diffsel"].pause(); - assets.sounds["diffsel"].currentTime = 0; - setTimeout(function(){ - assets.sounds["song-select"].playAsset(); - }, 300); - + $('.songsel-title').fadeOut(200, function(){ $('.songsel-title').attr('alt', '曲をえらぶ').html('曲をえらぶ').css('left', -300); $('.songsel-title').animate({left:0, opacity:"show"}, 400); }); - + return; } - - + if(!$('.opened').length) { _this.startPreview($(this).data('song-id'), $(this).data('preview')); - assets.sounds["don"].playAsset(); - assets.sounds["song-select"].pause(); - assets.sounds["song-select"].currentTime = 0; - setTimeout(function(){ - assets.sounds["diffsel"].playAsset(); - }, 300); - + assets.sounds["don"].play() + assets.sounds["song-select"].stop() + assets.sounds["diffsel"].play(0.3) + $('.songsel-title').fadeOut(200, function(){ $('.songsel-title').attr('alt', 'むずかしさをえらぶ').html('むずかしさをえらぶ').css('left', -300); $('.songsel-title').animate({left:0, opacity:"show"}, 400); }); } else { - _preview.pause(); _this.startPreview($(this).data('song-id'), $(this).data('preview'), false); - assets.sounds["ka"].playAsset(); + assets.sounds["ka"].play(); } }; - + $(".difficulty").hide(); $(".opened").removeClass("opened", 300); $(this).addClass("opened", 300, "linear", function(){ @@ -140,19 +138,12 @@ function SongSelect(){ $(this).css("background", "rgba(255, 220, 47, 0.90)"); }); }); - + } - + this.createCode = function(){ - bgm = new BufferedLoop( - {url: '/assets/audio/bgm_songsel.ogg', duration: 1.442}, - {url: '/assets/audio/bgm_songsel_loop.ogg', duration: 2.064} - ); - bgm.play(); - - setTimeout(function(){ - assets.sounds["song-select"].playAsset(); - }, 200); + assets.sounds["bgm_songsel"].playLoop(0, false, 0, 1.4423958333333333) + assets.sounds["song-select"].play(0.2); var songElements = [0] @@ -243,14 +234,7 @@ function SongSelect(){ ) $('.difficulty').hide(); } - - this.display = function(){ - } - - this.refresh = function(){ - - } $("#screen").load("/src/views/songselect.html", _this.run); - + } \ No newline at end of file diff --git a/public/src/js/soundbuffer.js b/public/src/js/soundbuffer.js new file mode 100644 index 0000000..cd4279c --- /dev/null +++ b/public/src/js/soundbuffer.js @@ -0,0 +1,193 @@ +class SoundBuffer{ + constructor(){ + this.context = new AudioContext() + var resume = () => { + if(this.context.state == "suspended"){ + this.context.resume() + } + removeEventListener("click", resume) + } + addEventListener("click", resume) + } + load(url, gain){ + return new Promise((resolve, reject) => { + var request = new XMLHttpRequest() + request.open("GET", url) + request.responseType = "arraybuffer" + request.addEventListener("load", () => { + this.context.decodeAudioData(request.response, buffer => { + resolve(new Sound(gain || {soundBuffer: this}, buffer)) + }, reject) + }) + request.addEventListener("error", reject) + request.addEventListener("abort", reject) + request.send() + }) + } + createGain(){ + return new SoundGain(this) + } + setCrossfade(gain1, gain2, median){ + gain1.setCrossfade(1 - median) + gain2.setCrossfade(median) + } + getTime(){ + return this.context.currentTime + } + convertTime(time, absolute){ + time = (time || 0) + if(time < 0){ + time = 0 + } + return time + (absolute ? 0 : this.getTime()) + } + createSource(sound){ + var source = this.context.createBufferSource() + source.buffer = sound.buffer + source.connect(sound.gain.gainNode || this.context.destination) + return source + } +} +class SoundGain{ + constructor(soundBuffer){ + this.soundBuffer = soundBuffer + this.gainNode = soundBuffer.context.createGain() + this.gainNode.connect(soundBuffer.context.destination) + this.setVolume(1) + } + load(url){ + return this.soundBuffer.load(url, this) + } + convertTime(time, absolute){ + return this.soundBuffer.convertTime(time, absolute) + } + setVolume(amount){ + this.gainNode.gain.value = amount * amount + this.volume = amount + } + setCrossfade(amount){ + this.setVolume(Math.pow(Math.sin(Math.PI / 2 * amount), 1 / 4)) + } + fadeIn(duration, time, absolute){ + this.fadeVolume(0, this.volume * this.volume, duration, time, absolute) + } + fadeOut(duration, time, absolute){ + this.fadeVolume(this.volume * this.volume, 0, duration, time, absolute) + } + fadeVolume(vol1, vol2, duration, time, absolute){ + time = this.convertTime(time, absolute) + this.gainNode.gain.linearRampToValueAtTime(vol1, time) + this.gainNode.gain.linearRampToValueAtTime(vol2, time + (duration || 0)) + } + mute(){ + this.gainNode.gain.value = 0 + } + unmute(){ + this.setVolume(this.volume) + } +} +class Sound{ + constructor(gain, buffer){ + this.gain = gain + this.buffer = buffer + this.soundBuffer = gain.soundBuffer + this.duration = buffer.duration + this.timeouts = new Set() + this.sources = new Set() + } + getTime(){ + return this.soundBuffer.getTime() + } + convertTime(time, absolute){ + return this.soundBuffer.convertTime(time, absolute) + } + setTimeouts(time){ + return new Promise(resolve => { + var relTime = time - this.getTime() + if(relTime > 0){ + var timeout = setTimeout(() => { + this.timeouts.delete(timeout) + resolve() + }, relTime * 1000) + this.timeouts.add(timeout) + }else{ + resolve() + } + }) + } + clearTimeouts(){ + this.timeouts.forEach(timeout => { + clearTimeout(timeout) + this.timeouts.delete(timeout) + }) + } + playLoop(time, absolute, seek1, seek2, until){ + time = this.convertTime(time, absolute) + seek1 = seek1 || 0 + seek2 = seek2 || seek1 + until = until || this.duration + this.loop = { + started: time + until - seek1, + seek: seek2, + until: until + } + this.play(time, true, seek1, until) + this.addLoop() + this.loop.interval = setInterval(() => { + this.addLoop() + }, 100) + } + addLoop(){ + if(this.getTime() > this.loop.started - 1){ + this.play(this.loop.started, true, this.loop.seek, this.loop.until) + this.loop.started += this.loop.until - this.loop.seek + } + } + play(time, absolute, seek, until){ + time = this.convertTime(time, absolute) + var source = this.soundBuffer.createSource(this) + seek = seek || 0 + until = until || this.duration + this.setTimeouts(time).then(() => { + this.cfg = { + started: time, + seek: seek, + until: until + } + }) + source.start(time, Math.max(0, seek || 0), Math.max(0, until - seek)) + source.startTime = time + this.sources.add(source) + source.onended = () => { + this.sources.delete(source) + } + } + stop(time, absolute){ + time = this.convertTime(time, absolute) + this.sources.forEach(source => { + source.stop(Math.max(source.startTime, time)) + }) + this.setTimeouts(time).then(() => { + if(this.loop){ + clearInterval(this.loop.interval) + } + this.clearTimeouts() + }) + } + pause(time, absolute){ + if(this.cfg){ + time = this.convertTime(time, absolute) + this.stop(time, true) + this.cfg.pauseSeek = time - this.cfg.started + this.cfg.seek + } + } + resume(time, absolute){ + if(this.cfg){ + if(this.loop){ + this.playLoop(time, absolute, this.cfg.pauseSeek, this.loop.seek, this.loop.until) + }else{ + this.play(time, absolute, this.cfg.pauseSeek, this.cfg.until) + } + } + } +} diff --git a/public/src/js/soundsystem.js b/public/src/js/soundsystem.js deleted file mode 100644 index bf7e097..0000000 --- a/public/src/js/soundsystem.js +++ /dev/null @@ -1,48 +0,0 @@ -function soundSystem(controller){ - - var _this = this; - var _speed=0; - var _circles = []; - var _circleID = -1; - var _measures = []; - var _sounds = assets.sounds; - var _channels=[]; - var _channelMAX=20; - - for (var i=0;i<_channelMAX;i++) {// prepare the channels - _channels[i] = {}; - _channels[i]["end"] = -1; - _channels[i]["audio"] = new Audio(); - } - - this.playSound = function(soundID){ - - for(var i=0;i<_channelMAX;i++){ //play in different sounds in different channels - var now = new Date(); - if (_channels[i]["end"] < now.getTime()) {// is this channel finished? - _channels[i]["end"] = now.getTime() + _sounds[soundID].duration*1000; - _channels[i]["audio"].src = _sounds[soundID].src; - _channels[i]["audio"].load(); - _channels[i]["audio"].play(); - break; - } - } - - } - - this.pauseSound = function(){ - _sounds["main-music"].pause(); - } - - this.fadeOutMusic = function(){ - - if(_sounds["main-music"].volume.toFixed(1)!=0.0){ - _sounds["main-music"].volume-=0.1; - } - else{ - _sounds["main-music"].pause(); - controller.fadeOutOver(); - } - } - -} \ No newline at end of file diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index 64b3163..c1eb80e 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -32,7 +32,7 @@ function Titlescreen(){ $("#screen").find("#title-screen").show(); $(window).resize(_this.positionning); - assets.sounds["title"].playAsset(); + assets.sounds["title"].play(); } @@ -40,7 +40,7 @@ function Titlescreen(){ assets.sounds["title"].pause(); assets.sounds["title"].currentTime = 0; - assets.sounds["don"].playAsset(); + assets.sounds["don"].play(); if (localStorage.getItem('tutorial') !== 'true') { new Tutorial(); } else { diff --git a/public/src/js/tutorial.js b/public/src/js/tutorial.js index 7c8cce2..9b9ee2e 100644 --- a/public/src/js/tutorial.js +++ b/public/src/js/tutorial.js @@ -2,15 +2,11 @@ function Tutorial() { var _this = this; this.run = function() { - bgm = new BufferedLoop( - {url: '/assets/audio/bgm_setsume.ogg', duration: 1.054}, - {url: '/assets/audio/bgm_setsume_loop.ogg', duration: 15} - ); - bgm.play(); + assets.sounds["bgm_setsume"].playLoop(0, false, 0, 1.0540416666666668) $('#tutorial-end-button').click(function(){ - bgm.pause(); - assets.sounds['don'].playAsset(); + assets.sounds["bgm_setsume"].stop(); + assets.sounds["don"].play(); localStorage.setItem('tutorial', 'true'); new SongSelect(); diff --git a/public/src/js/view.js b/public/src/js/view.js index 5067744..e603d35 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -4,10 +4,9 @@ class View{ this.bg = bg this.diff = diff - this.winW = $(window).width() - this.winH = $(window).height() - - this.canvas = new ScalableCanvas("canvas", this.winW, this.winH) + this.canvas = new ScalableCanvas("canvas", $(window).width(), $(window).height()) + this.winW = this.canvas.scaledWidth + this.winH = this.canvas.scaledHeight this.ctx = this.canvas.ctx this.taikoSquareW = this.winW / 4