summaryrefslogtreecommitdiff
path: root/ext/mixed/js/audio.js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-03-07 14:16:19 -0500
committertoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-03-07 21:43:51 -0500
commitdceaa853098a465b2eaa1b90900b5c1832131f26 (patch)
tree40659359b076fed2ad57980f88e26d2ff95a12c5 /ext/mixed/js/audio.js
parent6adf2cf63f3f67020d3d06adfdfa0f136a7e09e0 (diff)
Rename audio.js to audio-system.js
Diffstat (limited to 'ext/mixed/js/audio.js')
-rw-r--r--ext/mixed/js/audio.js185
1 files changed, 0 insertions, 185 deletions
diff --git a/ext/mixed/js/audio.js b/ext/mixed/js/audio.js
deleted file mode 100644
index 31c476b1..00000000
--- a/ext/mixed/js/audio.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
- */
-
-class TextToSpeechAudio {
- constructor(text, voice) {
- this.text = text;
- this.voice = voice;
- this._utterance = null;
- this._volume = 1;
- }
-
- get currentTime() {
- return 0;
- }
- set currentTime(value) {
- // NOP
- }
-
- get volume() {
- return this._volume;
- }
- set volume(value) {
- this._volume = value;
- if (this._utterance !== null) {
- this._utterance.volume = value;
- }
- }
-
- play() {
- try {
- if (this._utterance === null) {
- this._utterance = new SpeechSynthesisUtterance(this.text || '');
- this._utterance.lang = 'ja-JP';
- this._utterance.volume = this._volume;
- this._utterance.voice = this.voice;
- }
-
- speechSynthesis.cancel();
- speechSynthesis.speak(this._utterance);
- } catch (e) {
- // NOP
- }
- }
-
- pause() {
- try {
- speechSynthesis.cancel();
- } catch (e) {
- // NOP
- }
- }
-}
-
-class AudioSystem {
- constructor({getAudioUri}) {
- this._cache = new Map();
- this._cacheSizeMaximum = 32;
- this._getAudioUri = getAudioUri;
-
- if (typeof speechSynthesis !== 'undefined') {
- // speechSynthesis.getVoices() will not be populated unless some API call is made.
- speechSynthesis.addEventListener('voiceschanged', this._onVoicesChanged.bind(this));
- }
- }
-
- async getDefinitionAudio(definition, sources, details) {
- const key = `${definition.expression}:${definition.reading}`;
- const cacheValue = this._cache.get(definition);
- if (typeof cacheValue !== 'undefined') {
- const {audio, uri, source} = cacheValue;
- return {audio, uri, source};
- }
-
- for (const source of sources) {
- const uri = await this._getAudioUri(definition, source, details);
- if (uri === null) { continue; }
-
- try {
- const audio = await this._createAudio(uri, details);
- this._cacheCheck();
- this._cache.set(key, {audio, uri, source});
- return {audio, uri, source};
- } catch (e) {
- // NOP
- }
- }
-
- throw new Error('Could not create audio');
- }
-
- createTextToSpeechAudio({text, voiceUri}) {
- const voice = this._getTextToSpeechVoiceFromVoiceUri(voiceUri);
- if (voice === null) {
- throw new Error('Invalid text-to-speech voice');
- }
- return new TextToSpeechAudio(text, voice);
- }
-
- _onVoicesChanged() {
- // NOP
- }
-
- async _createAudio(uri, details) {
- const ttsParameters = this._getTextToSpeechParameters(uri);
- if (ttsParameters !== null) {
- if (typeof details === 'object' && details !== null) {
- if (details.tts === false) {
- throw new Error('Text-to-speech not permitted');
- }
- }
- return this.createTextToSpeechAudio(ttsParameters);
- }
-
- return await this._createAudioFromUrl(uri);
- }
-
- _createAudioFromUrl(url) {
- return new Promise((resolve, reject) => {
- const audio = new Audio(url);
- audio.addEventListener('loadeddata', () => {
- const duration = audio.duration;
- if (duration === 5.694694 || duration === 5.720718) {
- // Hardcoded values for invalid audio
- reject(new Error('Could not retrieve audio'));
- } else {
- resolve(audio);
- }
- });
- audio.addEventListener('error', () => reject(audio.error));
- });
- }
-
- _getTextToSpeechVoiceFromVoiceUri(voiceUri) {
- try {
- for (const voice of speechSynthesis.getVoices()) {
- if (voice.voiceURI === voiceUri) {
- return voice;
- }
- }
- } catch (e) {
- // NOP
- }
- return null;
- }
-
- _getTextToSpeechParameters(uri) {
- const m = /^tts:[^#?]*\?([^#]*)/.exec(uri);
- if (m === null) { return null; }
-
- const searchParameters = new URLSearchParams(m[1]);
- const text = searchParameters.get('text');
- const voiceUri = searchParameters.get('voice');
- return (text !== null && voiceUri !== null ? {text, voiceUri} : null);
- }
-
- _cacheCheck() {
- const removeCount = this._cache.size - this._cacheSizeMaximum;
- if (removeCount <= 0) { return; }
-
- const removeKeys = [];
- for (const key of this._cache.keys()) {
- removeKeys.push(key);
- if (removeKeys.length >= removeCount) { break; }
- }
-
- for (const key of removeKeys) {
- this._cache.delete(key);
- }
- }
-}