Update audio controller (#904)

* Remove redundant assignment of select value

* Update TTS preparation

* Don't hide options

* Improve voice sorting

* Update event handler

* Apply options to all selects targeting audio.textToSpeechVoice

* Update selector

* Use IDs

* Move test text to a hidden text input
This commit is contained in:
toasted-nutbread 2020-10-10 12:11:33 -04:00 committed by GitHub
parent 6799b87cc6
commit 3174f3c657
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 59 deletions

View File

@ -28,18 +28,25 @@ class AudioController {
this._audioSourceContainer = null; this._audioSourceContainer = null;
this._audioSourceAddButton = null; this._audioSourceAddButton = null;
this._audioSourceEntries = []; this._audioSourceEntries = [];
this._ttsVoiceTestTextInput = null;
} }
async prepare() { async prepare() {
this._audioSystem.prepare(); this._audioSystem.prepare();
this._audioSourceContainer = document.querySelector('.audio-source-list'); this._ttsVoiceTestTextInput = document.querySelector('#text-to-speech-voice-test-text');
this._audioSourceAddButton = document.querySelector('.audio-source-add'); this._audioSourceContainer = document.querySelector('#audio-source-list');
this._audioSourceAddButton = document.querySelector('#audio-source-add');
this._audioSourceContainer.textContent = ''; this._audioSourceContainer.textContent = '';
this._audioSourceAddButton.addEventListener('click', this._onAddAudioSource.bind(this), false); this._audioSourceAddButton.addEventListener('click', this._onAddAudioSource.bind(this), false);
this._prepareTextToSpeech(); if (typeof speechSynthesis !== 'undefined') {
speechSynthesis.addEventListener('voiceschanged', this._updateTextToSpeechVoices.bind(this), false);
}
this._updateTextToSpeechVoices();
document.querySelector('#text-to-speech-voice-test').addEventListener('click', this._onTestTextToSpeech.bind(this), false);
this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this));
@ -59,57 +66,62 @@ class AudioController {
} }
} }
_prepareTextToSpeech() { _onTestTextToSpeech() {
if (typeof speechSynthesis === 'undefined') { return; } try {
const text = this._ttsVoiceTestTextInput.value || '';
const voiceUri = document.querySelector('[data-setting="audio.textToSpeechVoice"]').value;
speechSynthesis.addEventListener('voiceschanged', this._updateTextToSpeechVoices.bind(this), false); const audio = this._audioSystem.createTextToSpeechAudio(text, voiceUri);
this._updateTextToSpeechVoices(); audio.volume = 1.0;
audio.play();
document.querySelector('#text-to-speech-voice').addEventListener('change', this._onTextToSpeechVoiceChange.bind(this), false); } catch (e) {
document.querySelector('#text-to-speech-voice-test').addEventListener('click', this._testTextToSpeech.bind(this), false); // NOP
}
} }
_updateTextToSpeechVoices() { _updateTextToSpeechVoices() {
const voices = Array.prototype.map.call(speechSynthesis.getVoices(), (voice, index) => ({voice, index})); const voices = (
typeof speechSynthesis !== 'undefined' ?
[...speechSynthesis.getVoices()].map((voice, index) => ({
voice,
isJapanese: this._languageTagIsJapanese(voice.lang),
index
})) :
[]
);
voices.sort(this._textToSpeechVoiceCompare.bind(this)); voices.sort(this._textToSpeechVoiceCompare.bind(this));
document.querySelector('#text-to-speech-voice-container').hidden = (voices.length === 0); for (const select of document.querySelectorAll('[data-setting="audio.textToSpeechVoice"]')) {
const fragment = document.createDocumentFragment();
const fragment = document.createDocumentFragment(); let option = document.createElement('option');
option.value = '';
let option = document.createElement('option'); option.textContent = 'None';
option.value = '';
option.textContent = 'None';
fragment.appendChild(option);
for (const {voice} of voices) {
option = document.createElement('option');
option.value = voice.voiceURI;
option.textContent = `${voice.name} (${voice.lang})`;
fragment.appendChild(option); fragment.appendChild(option);
}
const select = document.querySelector('#text-to-speech-voice'); for (const {voice} of voices) {
select.textContent = ''; option = document.createElement('option');
select.appendChild(fragment); option.value = voice.voiceURI;
select.value = select.dataset.value; option.textContent = `${voice.name} (${voice.lang})`;
fragment.appendChild(option);
}
select.textContent = '';
select.appendChild(fragment);
}
} }
_textToSpeechVoiceCompare(a, b) { _textToSpeechVoiceCompare(a, b) {
const aIsJapanese = this._languageTagIsJapanese(a.voice.lang); if (a.isJapanese) {
const bIsJapanese = this._languageTagIsJapanese(b.voice.lang); if (!b.isJapanese) { return -1; }
if (aIsJapanese) {
if (!bIsJapanese) { return -1; }
} else { } else {
if (bIsJapanese) { return 1; } if (b.isJapanese) { return 1; }
} }
const aIsDefault = a.voice.default; if (a.voice.default) {
const bIsDefault = b.voice.default; if (!b.voice.default) { return -1; }
if (aIsDefault) {
if (!bIsDefault) { return -1; }
} else { } else {
if (bIsDefault) { return 1; } if (b.voice.default) { return 1; }
} }
return a.index - b.index; return a.index - b.index;
@ -123,19 +135,6 @@ class AudioController {
); );
} }
_testTextToSpeech() {
try {
const text = document.querySelector('#text-to-speech-voice-test').dataset.speechText || '';
const voiceUri = document.querySelector('#text-to-speech-voice').value;
const audio = this._audioSystem.createTextToSpeechAudio(text, voiceUri);
audio.volume = 1.0;
audio.play();
} catch (e) {
// NOP
}
}
_getUnusedAudioSource() { _getUnusedAudioSource() {
const audioSourcesAvailable = [ const audioSourcesAvailable = [
'jpod101', 'jpod101',
@ -195,10 +194,6 @@ class AudioController {
this._audioSourceEntries.splice(index, 1); this._audioSourceEntries.splice(index, 1);
} }
_onTextToSpeechVoiceChange(e) {
e.currentTarget.dataset.value = e.currentTarget.value;
}
async _onAddAudioSource() { async _onAddAudioSource() {
const audioSource = this._getUnusedAudioSource(); const audioSource = this._getUnusedAudioSource();
const index = this._audioSourceEntries.length; const index = this._audioSourceEntries.length;

View File

@ -346,14 +346,15 @@
<input type="number" min="0" max="100" id="audio-playback-volume" class="form-control" data-setting="audio.volume"> <input type="number" min="0" max="100" id="audio-playback-volume" class="form-control" data-setting="audio.volume">
</div> </div>
<div class="form-group" id="text-to-speech-voice-container" hidden> <div class="form-group" id="text-to-speech-voice-container">
<label for="text-to-speech-voice">Text-to-speech voice</label> <label for="text-to-speech-voice">Text-to-speech voice</label>
<div class="input-group"> <div class="input-group">
<select class="form-control" id="text-to-speech-voice" data-setting="audio.textToSpeechVoice"></select> <select class="form-control" id="text-to-speech-voice" data-setting="audio.textToSpeechVoice"></select>
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn btn-default" id="text-to-speech-voice-test" title="Test voice" data-speech-text="よみちゃん"><span class="glyphicon glyphicon-volume-up"></span></button> <button class="btn btn-default" id="text-to-speech-voice-test" title="Test voice"><span class="glyphicon glyphicon-volume-up"></span></button>
</div> </div>
</div> </div>
<input type="text" value="よみちゃん" id="text-to-speech-voice-test-text" hidden>
</div> </div>
<div class="form-group options-advanced"> <div class="form-group options-advanced">
@ -363,9 +364,9 @@
<div class="form-group ignore-form-changes"> <div class="form-group ignore-form-changes">
<label>Audio playback sources</label> <label>Audio playback sources</label>
<div class="audio-source-list generic-input-list"></div> <div class="audio-source-list generic-input-list" id="audio-source-list"></div>
<div class="input-group audio-source-options"> <div class="input-group audio-source-options">
<button class="btn btn-default audio-source-add" title="Add audio playback source"><span class="glyphicon glyphicon-plus"></span></button> <button class="btn btn-default audio-source-add" title="Add audio playback source" id="audio-source-add"><span class="glyphicon glyphicon-plus"></span></button>
</div> </div>
<template id="audio-source-template"><div class="input-group audio-source"> <template id="audio-source-template"><div class="input-group audio-source">