diff --git a/app.py b/app.py index 9714b69..91f45b0 100644 --- a/app.py +++ b/app.py @@ -142,7 +142,8 @@ def route_api_songs(): 'category': category_out, 'type': song_type, 'offset': song[13], - 'song_skin': song_skin_out + 'song_skin': song_skin_out, + 'volume': song[16] }) return jsonify(songs_out) diff --git a/public/src/js/about.js b/public/src/js/about.js index 1c3586d..d01c66d 100644 --- a/public/src/js/about.js +++ b/public/src/js/about.js @@ -143,7 +143,7 @@ } } - var issueBody = strings.issueTemplate + "\n\n\n\n" + diag + var issueBody = strings.about.issueTemplate + "\n\n\n\n" + diag this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "
\r\n")) return diag diff --git a/public/src/js/controller.js b/public/src/js/controller.js index d0bc63b..6e2baae 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -21,6 +21,7 @@ class Controller{ assets.songs.forEach(song => { if(song.id == this.selectedSong.folder){ this.mainAsset = song.sound + this.volume = song.volume || 1 } }) @@ -35,6 +36,9 @@ class Controller{ if(syncWith){ this.syncWith = syncWith } + if(this.multiplayer !== 2){ + snd.musicGain.setVolumeMul(this.volume) + } this.game.run() this.view.run() if(this.multiplayer === 1){ @@ -161,8 +165,26 @@ class Controller{ if(this.multiplayer){ new LoadSong(this.selectedSong, false, true, this.touchEnabled) }else{ - var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) - taikoGame.run() + new Promise(resolve => { + var songObj = assets.songs.find(song => song.id === this.selectedSong.folder) + if(songObj.chart){ + var reader = new FileReader() + var promise = pageEvents.load(reader).then(event => { + this.songData = event.target.result.replace(/\0/g, "").split("\n") + resolve() + }) + if(this.selectedSong.type === "tja"){ + reader.readAsText(songObj.chart, "sjis") + }else{ + reader.readAsText(songObj.chart) + } + }else{ + resolve() + } + }).then(() => { + var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) + taikoGame.run() + }) } } playSound(id, time){ @@ -232,6 +254,7 @@ class Controller{ this.stopMainLoop() this.keyboard.clean() this.view.clean() + snd.buffer.loadSettings() if(!this.multiplayer){ debugObj.controller = null diff --git a/public/src/js/debug.js b/public/src/js/debug.js index 9f2836c..beeede3 100644 --- a/public/src/js/debug.js +++ b/public/src/js/debug.js @@ -16,6 +16,7 @@ class Debug{ this.branchSelectDiv = this.byClass("branch-select") this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0] this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0] + this.volumeDiv = this.byClass("music-volume") this.restartCheckbox = this.byClass("change-restart") this.autoplayLabel = this.byClass("autoplay-label") this.autoplayCheckbox = this.byClass("autoplay") @@ -40,6 +41,10 @@ class Debug{ this.measureNumSlider.onchange(this.measureNumChange.bind(this)) this.measureNumSlider.set(0) + this.volumeSlider = new InputSlider(this.volumeDiv, 0, 3, 2) + this.volumeSlider.onchange(this.volumeChange.bind(this)) + this.volumeSlider.set(1) + this.moveTo(100, 100) this.restore() this.updateStatus() @@ -110,10 +115,12 @@ class Debug{ if(this.songFolder === selectedSong.folder){ this.offsetChange(this.offsetSlider.get(), true) this.branchChange(null, true) + this.volumeChange(this.volumeSlider.get(), true) }else{ this.songFolder = selectedSong.folder this.offsetSlider.set(this.defaultOffset) this.branchReset(null, true) + this.volumeSlider.set(this.controller.volume) } var measures = this.controller.parsedSongData.measures.filter((measure, i, array) => { @@ -176,6 +183,14 @@ class Debug{ this.restartSong() } } + volumeChange(value, noRestart){ + if(this.controller){ + snd.musicGain.setVolumeMul(value) + } + if(this.restartCheckbox.checked && !noRestart){ + this.restartSong() + } + } restartSong(){ if(this.controller){ this.controller.restartSong() @@ -234,6 +249,7 @@ class Debug{ delete this.branchSelectDiv delete this.branchSelect delete this.branchResetBtn + delete this.volumeDiv delete this.restartCheckbox delete this.autoplayLabel delete this.autoplayCheckbox diff --git a/public/src/js/game.js b/public/src/js/game.js index ceabf3f..19609a4 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -30,6 +30,7 @@ class Game{ this.currentTimingPoint = 0 this.branchNames = ["normal", "advanced", "master"] this.resetSection() + this.gameLagSync = !this.controller.touchEnabled && !(/Firefox/.test(navigator.userAgent)) assets.songs.forEach(song => { if(song.id == selectedSong.folder){ @@ -364,7 +365,7 @@ class Game{ var value = { score: score, ms: circle.ms - currentTime, - dai: typeDai ? keyDai ? 2 : 1 : 0 + dai: typeDai ? (keyDai ? 2 : 1) : 0 } if((!keysDon || !typeDon) && (!keysKa || !typeKa)){ value.reverse = true @@ -536,7 +537,7 @@ class Game{ this.sndTime = this.startDate - snd.buffer.getTime() * 1000 }else if(ms < 0 || ms >= 0 && this.started){ var currentDate = Date.now() - if(!this.controller.touchEnabled){ + if(this.gameLagSync){ var sndTime = currentDate - snd.buffer.getTime() * 1000 var lag = sndTime - this.sndTime if(Math.abs(lag) >= 50){ diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index 5040c92..93bd9ec 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -183,7 +183,7 @@ var songObj = { id: index + 1, type: "tja", - chart: data, + chart: file, stars: [], music: "muted" } @@ -258,7 +258,7 @@ var songObj = { id: index + 1, type: "osu", - chart: data, + chart: file, subtitle: osu.metadata.ArtistUnicode || osu.metadata.Artist, subtitle_lang: osu.metadata.Artist || osu.metadata.ArtistUnicode, preview: osu.generalInfo.PreviewTime / 1000, diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index 5925658..cb9a2ac 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -196,18 +196,17 @@ class Keyboard{ this.checkKey(keyCode, "sound", () => { var circles = this.controller.getCircles() var circle = circles[this.controller.getCurrentCircle()] - if( - sound === "don" - && circle - && !circle.isPlayed - && circle.type === "balloon" - && circle.requiredHits - circle.timesHit <= 1 - ){ - this.controller.playSound("se_balloon") - }else{ - this.controller.playSound("neiro_1_" + sound) + var currentTime = this.keyTime[keyCode] + this.keyTime[sound] = currentTime + if(circle && !circle.isPlayed){ + if(circle.type === "balloon"){ + if(sound === "don" && circle.requiredHits - circle.timesHit <= 1){ + this.controller.playSound("se_balloon") + return + } + } } - this.keyTime[sound] = this.keyTime[keyCode] + this.controller.playSound("neiro_1_" + sound) }) } getKeys(){ @@ -242,6 +241,9 @@ class Keyboard{ } } waitForKeyup(keyCode, type){ + if(!this.keys[keyCode]){ + return + } if(type === "score"){ this.waitKeyupScore[keyCode] = true }else if(type === "sound"){ diff --git a/public/src/js/loader.js b/public/src/js/loader.js index a171642..47d1ecb 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -118,6 +118,7 @@ class Loader{ 0.5 ) snd.sfxLoudGain.setVolume(1.2) + snd.buffer.saveSettings() this.afterJSCount = 0 diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index 94012fb..0c0b02f 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -112,7 +112,15 @@ class LoadSong{ } })) if(songObj.chart){ - this.songData = songObj.chart + var reader = new FileReader() + promises.push(pageEvents.load(reader).then(event => { + this.songData = event.target.result.replace(/\0/g, "").split("\n") + })) + if(song.type === "tja"){ + reader.readAsText(songObj.chart, "sjis") + }else{ + reader.readAsText(songObj.chart) + } }else{ promises.push(loader.ajax(this.getSongPath(song)).then(data => { this.songData = data.replace(/\0/g, "").split("\n") diff --git a/public/src/js/mekadon.js b/public/src/js/mekadon.js index fe63d71..eff1537 100644 --- a/public/src/js/mekadon.js +++ b/public/src/js/mekadon.js @@ -69,25 +69,25 @@ class Mekadon{ type = "don" } } - if(type == "daiDon" && playDai){ + if(type === "daiDon" && playDai){ this.setKey(kbd["don_l"], ms) this.setKey(kbd["don_r"], ms) this.lr = false keyDai = true - }else if(type == "don" || type == "daiDon" || drumrollNotes && score !== 2){ + }else if(type === "don" || type === "daiDon" || drumrollNotes && score !== 2){ this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms) this.lr = !this.lr - }else if(type == "daiKa" && playDai){ + }else if(type === "daiKa" && playDai){ this.setKey(kbd["ka_l"], ms) this.setKey(kbd["ka_r"], ms) this.lr = false keyDai = true - }else if(type == "ka" || type == "daiKa" || drumrollNotes){ + }else if(type === "ka" || type === "daiKa" || drumrollNotes){ this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms) this.lr = !this.lr } if(type === "balloon"){ - if(circle.requiredHits == 1){ + if(circle.requiredHits === 1){ assets.sounds["se_balloon"].play() } this.game.checkBalloon(circle) diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js index 427bb43..2b10f9b 100644 --- a/public/src/js/parsetja.js +++ b/public/src/js/parsetja.js @@ -10,18 +10,20 @@ this.difficulty = difficulty this.offset = (offset || 0) * -1000 this.soundOffset = 0 - this.noteTypes = [ - {name: false, txt: false}, - {name: "don", txt: strings.note.don}, - {name: "ka", txt: strings.note.ka}, - {name: "daiDon", txt: strings.note.daiDon}, - {name: "daiKa", txt: strings.note.daiKa}, - {name: "drumroll", txt: strings.note.drumroll}, - {name: "daiDrumroll", txt: strings.note.daiDrumroll}, - {name: "balloon", txt: strings.note.balloon}, - {name: false, txt: false}, - {name: "balloon", txt: strings.note.balloon} - ] + this.noteTypes = { + "0": {name: false, txt: false}, + "1": {name: "don", txt: strings.note.don}, + "2": {name: "ka", txt: strings.note.ka}, + "3": {name: "daiDon", txt: strings.note.daiDon}, + "4": {name: "daiKa", txt: strings.note.daiKa}, + "5": {name: "drumroll", txt: strings.note.drumroll}, + "6": {name: "daiDrumroll", txt: strings.note.daiDrumroll}, + "7": {name: "balloon", txt: strings.note.balloon}, + "8": {name: false, txt: false}, + "9": {name: "balloon", txt: strings.note.balloon}, + "A": {name: "daiDon", txt: strings.note.daiDon}, + "B": {name: "daiKa", txt: strings.note.daiKa} + } this.courseTypes = { "0": "easy", "1": "normal", @@ -141,6 +143,7 @@ var firstNote = true var circles = [] var circleID = 0 + var regexAZ = /[A-Z]/ var pushMeasure = () => { var note = currentMeasure[0] @@ -321,7 +324,7 @@ }else{ - var string = line.split("") + var string = line.toUpperCase().split("") for(let symbol of string){ @@ -334,7 +337,7 @@ scroll: scroll }) break - case "1": case "2": case "3": case "4": + case "1": case "2": case "3": case "4": case "A": case "B": var type = this.noteTypes[symbol] var circleObj = { type: type.name, @@ -413,7 +416,14 @@ currentMeasure = [] break default: - error = true + if(regexAZ.test(symbol)){ + currentMeasure.push({ + bpm: bpm, + scroll: scroll + }) + }else{ + error = true + } break } diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 1402b86..037e7e4 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -856,7 +856,7 @@ class Scoresheet{ this.draw.clean() this.canvasCache.clean() assets.sounds["bgm_result"].stop() - snd.musicGain.fadeIn() + snd.buffer.loadSettings() this.redrawRunning = false pageEvents.keyRemove(this, "all") pageEvents.remove(this.canvas, ["mousedown", "touchstart"]) diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 09857f8..3af61af 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -107,7 +107,8 @@ class SongSelect{ type: song.type, offset: song.offset, songSkin: song.song_skin || {}, - music: song.music + music: song.music, + volume: song.volume }) } this.songs.sort((a, b) => { @@ -1741,7 +1742,7 @@ class SongSelect{ if(!loadOnly){ this.preview = songObj.preview_sound this.preview.gain = snd.previewGain - this.previewLoaded(startLoad, songObj.preview_time) + this.previewLoaded(startLoad, songObj.preview_time, currentSong.volume) } }else{ songObj = {id: id} @@ -1767,7 +1768,7 @@ class SongSelect{ if(currentId === this.previewId){ songObj.preview_sound = sound this.preview = sound - this.previewLoaded(startLoad, songObj.preview_time) + this.previewLoaded(startLoad, songObj.preview_time, currentSong.volume) var oldPreview = this.previewList.shift() if(oldPreview){ @@ -1781,11 +1782,12 @@ class SongSelect{ } } } - previewLoaded(startLoad, prvTime){ + previewLoaded(startLoad, prvTime, volume){ var endLoad = this.getMS() var difference = endLoad - startLoad var minDelay = 300 var delay = minDelay - Math.min(minDelay, difference) + snd.previewGain.setVolumeMul(volume || 1) this.preview.playLoop(delay / 1000, false, prvTime) } endPreview(){ @@ -1935,7 +1937,7 @@ class SongSelect{ if(!this.bgmEnabled){ snd.musicGain.fadeIn() setTimeout(() => { - snd.musicGain.fadeIn() + snd.buffer.loadSettings() }, 500) } this.redrawRunning = false diff --git a/public/src/js/soundbuffer.js b/public/src/js/soundbuffer.js index f220863..cd179f5 100644 --- a/public/src/js/soundbuffer.js +++ b/public/src/js/soundbuffer.js @@ -3,6 +3,7 @@ var AudioContext = window.AudioContext || window.webkitAudioContext this.context = new AudioContext() pageEvents.add(window, ["click", "touchend"], this.pageClicked.bind(this)) + this.gainList = [] } load(url, local, gain){ if(local){ @@ -27,7 +28,9 @@ }) } createGain(channel){ - return new SoundGain(this, channel) + var gain = new SoundGain(this, channel) + this.gainList.push(gain) + return gain } setCrossfade(gain1, gain2, median){ if(!Array.isArray(gain1)){ @@ -60,6 +63,18 @@ this.context.resume() } } + saveSettings(){ + for(var i = 0; i < this.gainList.length; i++){ + var gain = this.gainList[i] + gain.defaultVol = gain.volume + } + } + loadSettings(){ + for(var i = 0; i < this.gainList.length; i++){ + var gain = this.gainList[i] + gain.setVolume(gain.defaultVol) + } + } } class SoundGain{ constructor(soundBuffer, channel){ @@ -85,6 +100,9 @@ class SoundGain{ this.gainNode.gain.value = amount * amount this.volume = amount } + setVolumeMul(amount){ + this.setVolume(Math.sqrt(amount * amount * this.defaultVol)) + } setCrossfade(amount){ this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount))) } diff --git a/public/src/js/strings.js b/public/src/js/strings.js index 7d13389..16ddaa8 100644 --- a/public/src/js/strings.js +++ b/public/src/js/strings.js @@ -1,7 +1,7 @@ function StringsJa(){ this.id = "ja" this.name = "日本語" - this.regex = /^ja$/ + this.regex = /^ja$|^ja-/ this.font = "TnT, Meiryo, sans-serif" this.taikoWeb = "たいこウェブ" diff --git a/public/src/js/view.js b/public/src/js/view.js index b1733c8..f8fee1f 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -1438,7 +1438,7 @@ ctx.fill() ctx.globalAlpha = 1 } - if(!circle.animating){ + if(!circle.animating && circle.text){ // Text var text = circle.text var textX = circlePos.x diff --git a/public/src/views/debug.html b/public/src/views/debug.html index dd37b44..e57addc 100644 --- a/public/src/views/debug.html +++ b/public/src/views/debug.html @@ -20,6 +20,10 @@ +
Music volume:
+
+ x-+ +