Assets: Convert to ogg

- Uses oggmented library to add ogg support to iOS
- Adds support for ogg songs on the server, configurable per song on the admin page
- Lets iOS users upload custom songs with gdrive when enabled
- Tweaked colour picker on account page to respond faster
- Fixed not being able to restart osu songs
This commit is contained in:
LoveEevee 2020-11-09 00:30:56 +03:00
parent 348322b7d8
commit dec0e55eb7
161 changed files with 91 additions and 49 deletions

11
app.py
View File

@ -243,6 +243,7 @@ def route_admin_songs_new_post():
output['category_id'] = int(request.form.get('category_id')) or None
output['type'] = request.form.get('type')
output['music_type'] = request.form.get('music_type')
output['offset'] = float(request.form.get('offset')) or None
output['skin_id'] = int(request.form.get('skin_id')) or None
output['preview'] = float(request.form.get('preview')) or None
@ -303,6 +304,7 @@ def route_admin_songs_id_post(id):
output['category_id'] = int(request.form.get('category_id')) or None
output['type'] = request.form.get('type')
output['music_type'] = request.form.get('music_type')
output['offset'] = float(request.form.get('offset')) or None
output['skin_id'] = int(request.form.get('skin_id')) or None
output['preview'] = float(request.form.get('preview')) or None
@ -351,9 +353,10 @@ def route_api_preview():
abort(400)
song_type = song['type']
prev_path = make_preview(song_id, song_type, song['preview'])
song_ext = song['music_type'] if song['music_type'] else "mp3"
prev_path = make_preview(song_id, song_type, song_ext, song['preview'])
if not prev_path:
return redirect(get_config()['songs_baseurl'] + '%s/main.mp3' % song_id)
return redirect(get_config()['songs_baseurl'] + '%s/main.%s' % (song_id, song_ext))
return redirect(get_config()['songs_baseurl'] + '%s/preview.mp3' % song_id)
@ -606,8 +609,8 @@ def route_api_scores_get():
return jsonify({'status': 'ok', 'scores': scores, 'username': user['username'], 'display_name': user['display_name'], 'don': don})
def make_preview(song_id, song_type, preview):
song_path = 'public/songs/%s/main.mp3' % song_id
def make_preview(song_id, song_type, song_ext, preview):
song_path = 'public/songs/%s/main.%s' % (song_id, song_ext)
prev_path = 'public/songs/%s/preview.mp3' % song_id
if os.path.isfile(song_path) and not os.path.isfile(prev_path):

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -59,12 +59,12 @@ class Account{
this.customdonBodyFill.value = account.don.body_fill
var parent = this.customdonBodyFill.parentNode
parent.insertBefore(document.createTextNode(strings.account.customdon.bodyFill), parent.firstChild)
pageEvents.add(this.customdonBodyFill, "change", this.customdonChange.bind(this))
pageEvents.add(this.customdonBodyFill, ["change", "input"], this.customdonChange.bind(this))
this.customdonFaceFill = this.getElement("customdon-facefill")
this.customdonFaceFill.value = account.don.face_fill
var parent = this.customdonFaceFill.parentNode
parent.insertBefore(document.createTextNode(strings.account.customdon.faceFill), parent.firstChild)
pageEvents.add(this.customdonFaceFill, "change", this.customdonChange.bind(this))
pageEvents.add(this.customdonFaceFill, ["change", "input"], this.customdonChange.bind(this))
this.customdonResetBtn = this.getElement("customdon-reset")
this.customdonResetBtn.value = strings.account.customdon.reset
pageEvents.add(this.customdonResetBtn, ["click", "touchstart"], this.customdonReset.bind(this))
@ -578,8 +578,8 @@ class Account{
}
this.redrawRunning = false
this.customdonCache.clean()
pageEvents.remove(this.customdonBodyFill, "change")
pageEvents.remove(this.customdonFaceFill, "change")
pageEvents.remove(this.customdonBodyFill, ["change", "input"])
pageEvents.remove(this.customdonFaceFill, ["change", "input"])
pageEvents.remove(this.customdonResetBtn, ["click", "touchstart"])
pageEvents.remove(this.accounPassButton, ["click", "touchstart"])
pageEvents.remove(this.accountDelButton, ["click", "touchstart"])

View File

@ -93,37 +93,37 @@ var assets = {
"settings_gamepad.png"
],
"audioSfx": [
"se_pause.wav",
"se_calibration.wav",
"v_results.wav",
"v_sanka.wav",
"v_songsel.wav",
"v_start.wav",
"v_title.wav"
"se_pause.ogg",
"se_calibration.ogg",
"v_results.ogg",
"v_sanka.ogg",
"v_songsel.ogg",
"v_start.ogg",
"v_title.ogg"
],
"audioSfxLR": [
"neiro_1_don.wav",
"neiro_1_ka.wav",
"se_cancel.wav",
"se_don.wav",
"se_ka.wav",
"se_jump.wav",
"se_balloon.wav",
"se_gameclear.wav",
"se_gamefail.wav",
"se_gamefullcombo.wav",
"se_results_countup.wav",
"se_results_crown.wav",
"v_fullcombo.wav",
"v_renda.wav",
"v_results_fullcombo.wav",
"v_results_fullcombo2.wav"
"neiro_1_don.ogg",
"neiro_1_ka.ogg",
"se_cancel.ogg",
"se_don.ogg",
"se_ka.ogg",
"se_jump.ogg",
"se_balloon.ogg",
"se_gameclear.ogg",
"se_gamefail.ogg",
"se_gamefullcombo.ogg",
"se_results_countup.ogg",
"se_results_crown.ogg",
"v_fullcombo.ogg",
"v_renda.ogg",
"v_results_fullcombo.ogg",
"v_results_fullcombo2.ogg"
],
"audioSfxLoud": [
"v_diffsel.wav"
"v_diffsel.ogg"
],
"audioMusic": [
"bgm_songsel.mp3",

View File

@ -48,7 +48,7 @@ class Controller{
comboVoices.forEach(name => {
if (!assets.sounds[name + "_p1"]) {
promises.push(loader.loadSound(name + ".wav", snd.sfxGain).then(sound => {
promises.push(loader.loadSound(name + ".ogg", snd.sfxGain).then(sound => {
assets.sounds[name + "_p1"] = assets.sounds[name].copy(snd.sfxGainL)
assets.sounds[name + "_p2"] = assets.sounds[name].copy(snd.sfxGainR)
}))
@ -246,7 +246,12 @@ class Controller{
var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
var promises = []
if(songObj.chart && songObj.chart !== "blank"){
promises.push(songObj.chart.read(this.selectedSong.type === "tja" ? "sjis" : undefined).then(data => {
var chart = songObj.chart
if(chart.separateDiff){
var chartDiff = this.selectedSong.difficulty
chart = chart[chartDiff]
}
promises.push(chart.read(this.selectedSong.type === "tja" ? "sjis" : undefined).then(data => {
this.songData = data.replace(/\0/g, "").split("\n")
return Promise.resolve()
}))

View File

@ -19,7 +19,7 @@ class CustomSongs{
this.items = []
this.linkLocalFolder = document.getElementById("link-localfolder")
this.hasLocal = "webkitdirectory" in HTMLInputElement.prototype && !(/Android/.test(navigator.userAgent))
this.hasLocal = "webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))
if(this.hasLocal){
this.browse = document.getElementById("browse")
pageEvents.add(this.browse, "change", this.browseChange.bind(this))

10
public/src/js/lib/oggmented.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -30,6 +30,10 @@ class Loader{
if(gameConfig.custom_js){
this.addPromise(this.loadScript(gameConfig.custom_js), gameConfig.custom_js)
}
var oggSupport = new Audio().canPlayType("audio/ogg;codecs=vorbis")
if(!oggSupport){
assets.js.push("lib/oggmented.min.js")
}
assets.js.forEach(name => {
this.addPromise(this.loadScript("/src/js/" + name), "/src/js/" + name)
})
@ -144,7 +148,8 @@ class Loader{
songs = JSON.parse(songs)
songs.forEach(song => {
var directory = gameConfig.songs_baseurl + song.id + "/"
song.music = new RemoteFile(directory + "main.mp3")
var songExt = song.music_type ? song.music_type : "mp3"
song.music = new RemoteFile(directory + "main." + songExt)
if(song.type === "tja"){
song.chart = new RemoteFile(directory + "main.tja")
}else{
@ -185,6 +190,10 @@ class Loader{
this.addPromise(Promise.all(categoryPromises))
snd.buffer = new SoundBuffer()
if(!oggSupport){
var oggmentedCtx = new oggmented.default()
snd.buffer.oggDecoder = oggmentedCtx.decodeAudioData.bind(oggmentedCtx)
}
snd.musicGain = snd.buffer.createGain()
snd.sfxGain = snd.buffer.createGain()
snd.previewGain = snd.buffer.createGain()

View File

@ -138,13 +138,9 @@ class SongSelect{
var showCustom = false
if(gameConfig.google_credentials.gdrive_enabled){
if(!(/iPhone|iPad/.test(navigator.userAgent))){
showCustom = true
}
}else{
if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){
showCustom = true
}
showCustom = true
}else if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){
showCustom = true
}
if(showCustom){
this.songs.push({

View File

@ -2,13 +2,16 @@
constructor(){
var AudioContext = window.AudioContext || window.webkitAudioContext
this.context = new AudioContext()
this.audioDecoder = this.context.decodeAudioData.bind(this.context)
this.oggDecoder = this.audioDecoder
pageEvents.add(window, ["click", "touchend", "keypress"], this.pageClicked.bind(this))
this.gainList = []
}
load(file, gain){
var decoder = file.name.endsWith(".ogg") ? this.oggDecoder : this.audioDecoder
return file.arrayBuffer().then(response => {
return new Promise((resolve, reject) => {
return this.context.decodeAudioData(response, resolve, reject)
return decoder(response, resolve, reject)
}).catch(error => Promise.reject([error, file.url]))
}).then(buffer => {
return new Sound(gain || {soundBuffer: this}, buffer)

View File

@ -78,13 +78,21 @@
</div>
<div class="form-field">
<p><label for="type">Type</label></p>
<p><label for="type">Chart type</label></p>
<select name="type" id="type">
<option value="tja"{% if song.type == 'tja' %} selected{% endif %}>TJA</option>
<option value="osu"{% if song.type == 'osu' %} selected{% endif %}>osu!taiko</option>
</select>
</div>
<div class="form-field">
<p><label for="music_type">Music type</label></p>
<select name="music_type" id="music_type">
<option value="ogg"{% if song.music_type == 'ogg' %} selected{% endif %}>OGG</option>
<option value="mp3"{% if song.music_type == 'mp3' or not song.music_type %} selected{% endif %}>MP3</option>
</select>
</div>
<div class="form-field">
<p><label for="offset">Offset</label></p>
<input type="text" id="offset" value="{{song.offset or '0'}}" name="offset" required>

View File

@ -74,13 +74,21 @@
</div>
<div class="form-field">
<p><label for="type">Type</label></p>
<p><label for="type">Chart type</label></p>
<select name="type" id="type">
<option value="tja">TJA</option>
<option value="osu">osu!taiko</option>
</select>
</div>
<div class="form-field">
<p><label for="music_type">Music type</label></p>
<select name="music_type" id="music_type">
<option value="ogg">OGG</option>
<option value="mp3">MP3</option>
</select>
</div>
<div class="form-field">
<p><label for="offset">Offset</label></p>
<input type="text" id="offset" value="0" name="offset" required>