Implement audio fallbacks

This commit is contained in:
toasted-nutbread 2019-10-10 19:58:06 -04:00
parent 8ae1da4277
commit 1d516b3b24
6 changed files with 86 additions and 29 deletions

View File

@ -27,6 +27,7 @@
<script src="/bg/js/templates.js"></script>
<script src="/bg/js/translator.js"></script>
<script src="/bg/js/util.js"></script>
<script src="/mixed/js/audio.js"></script>
<script src="/mixed/js/japanese.js"></script>
<script src="/bg/js/backend.js"></script>

View File

@ -136,6 +136,7 @@ function audioBuildFilename(definition) {
return filename += '.mp3';
}
return null;
}
async function audioInject(definition, fields, sources, optionsContext) {
@ -157,11 +158,12 @@ async function audioInject(definition, fields, sources, optionsContext) {
audioSourceDefinition = definition.expressions[0];
}
const url = await audioBuildUrl(audioSourceDefinition, sources[0], optionsContext);
const filename = audioBuildFilename(audioSourceDefinition);
if (url && filename) {
definition.audio = {url, filename};
const {url} = await audioGetFromSources(audioSourceDefinition, sources, optionsContext, false);
if (url !== null) {
const filename = audioBuildFilename(audioSourceDefinition);
if (filename !== null) {
definition.audio = {url, filename};
}
}
return true;

View File

@ -36,7 +36,6 @@
<script src="/mixed/js/extension.js"></script>
<script src="/bg/js/audio.js"></script>
<script src="/bg/js/dictionary.js"></script>
<script src="/bg/js/handlebars.js"></script>
<script src="/bg/js/templates.js"></script>
@ -44,6 +43,7 @@
<script src="/fg/js/document.js"></script>
<script src="/fg/js/source.js"></script>
<script src="/fg/js/util.js"></script>
<script src="/mixed/js/audio.js"></script>
<script src="/mixed/js/display.js"></script>
<script src="/mixed/js/japanese.js"></script>
<script src="/mixed/js/scroll.js"></script>

View File

@ -39,6 +39,7 @@
<script src="/fg/js/util.js"></script>
<script src="/fg/js/document.js"></script>
<script src="/fg/js/source.js"></script>
<script src="/mixed/js/audio.js"></script>
<script src="/mixed/js/display.js"></script>
<script src="/mixed/js/scroll.js"></script>

60
ext/mixed/js/audio.js Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2019 Alex Yatskov <alex@foosoft.net>
* Author: Alex Yatskov <alex@foosoft.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
function audioGetFromUrl(url) {
return new Promise((resolve, reject) => {
const audio = new Audio(url);
audio.addEventListener('loadeddata', () => {
if (audio.duration === 5.694694 || audio.duration === 5.720718) {
// Hardcoded values for invalid audio
reject(new Error('Could not retrieve audio'));
} else {
resolve(audio);
}
});
audio.addEventListener('error', () => reject(audio.error));
});
}
async function audioGetFromSources(expression, sources, optionsContext, createAudioObject, cache=null) {
const key = `${expression.expression}:${expression.reading}`;
if (cache !== null && cache.hasOwnProperty(expression)) {
return cache[key];
}
for (let i = 0, ii = sources.length; i < ii; ++i) {
const source = sources[i];
const url = await apiAudioGetUrl(expression, source, optionsContext);
if (url === null) {
continue;
}
try {
const audio = createAudioObject ? await audioGetFromUrl(url) : null;
const result = {audio, url, source};
if (cache !== null) {
cache[key] = result;
}
return result;
} catch (e) {
// NOP
}
}
return {audio: null, source: null};
}

View File

@ -26,6 +26,8 @@ class Display {
this.context = null;
this.sequence = 0;
this.index = 0;
this.audioPlaying = null;
this.audioFallback = null;
this.audioCache = {};
this.optionsContext = {};
this.eventListeners = [];
@ -404,33 +406,24 @@ class Display {
this.setSpinnerVisible(true);
const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex];
let url = await apiAudioGetUrl(expression, this.options.audio.sources[0], this.optionsContext);
if (!url) {
url = '/mixed/mp3/button.mp3';
if (this.audioPlaying !== null) {
this.audioPlaying.pause();
this.audioPlaying = null;
}
for (const key in this.audioCache) {
this.audioCache[key].pause();
let {audio} = await audioGetFromSources(expression, this.options.audio.sources, this.optionsContext, true, this.audioCache);
if (audio === null) {
if (this.audioFallback === null) {
this.audioFallback = new Audio('/mixed/mp3/button.mp3');
}
audio = this.audioFallback;
}
const volume = this.options.audio.volume / 100.0;
let audio = this.audioCache[url];
if (audio) {
audio.currentTime = 0;
audio.volume = volume;
audio.play();
} else {
audio = new Audio(url);
audio.onloadeddata = () => {
if (audio.duration === 5.694694 || audio.duration === 5.720718) {
audio = new Audio('/mixed/mp3/button.mp3');
}
this.audioCache[url] = audio;
audio.volume = volume;
audio.play();
};
}
this.audioPlaying = audio;
audio.currentTime = 0;
audio.volume = this.options.audio.volume / 100.0;
audio.play();
} catch (e) {
this.onError(e);
} finally {