diff --git a/mimic3_http/app.py b/mimic3_http/app.py
index ee4798a..789c3ca 100644
--- a/mimic3_http/app.py
+++ b/mimic3_http/app.py
@@ -36,6 +36,7 @@ from quart import (
from swagger_ui import api_doc
from mimic3_tts import DEFAULT_VOICE, Mimic3Settings, Mimic3TextToSpeechSystem
+from mimic3_tts.utils import LANG_NAMES
from ._resources import _DIR, _PACKAGE
from .args import _MISSING
@@ -157,6 +158,7 @@ def get_app(args: argparse.Namespace, request_queue: Queue, temp_dir: str):
"index.html",
show_openapi=show_openapi,
max_text_length=args.max_text_length,
+ default_voice=args.default_voice,
)
@app.route("/api/tts", methods=["GET", "POST"])
@@ -225,9 +227,26 @@ def get_app(args: argparse.Namespace, request_queue: Queue, temp_dir: str):
@app.route("/api/voices", methods=["GET"])
async def api_voices():
- voices_dict = {v.key: v for v in _MIMIC3.get_voices()}
- voices = sorted(voices_dict.values(), key=lambda v: v.key)
- return jsonify([dataclasses.asdict(v) for v in voices])
+ voices_by_key = {v.key: v for v in _MIMIC3.get_voices()}
+ sorted_voices = sorted(voices_by_key.values(), key=lambda v: v.key)
+ voice_dicts = [dataclasses.asdict(v) for v in sorted_voices]
+
+ # Add more fields to voices
+ for voice_dict in voice_dicts:
+ voice_lang = voice_dict["language"]
+ lang_name = LANG_NAMES.get(voice_lang, voice_lang)
+
+ if isinstance(lang_name, str):
+ # Native and English language name are the same
+ native_lang, english_lang = lang_name, lang_name
+ else:
+ # Native and English language name are different
+ native_lang, english_lang = lang_name
+
+ voice_dict["language_native"] = native_lang
+ voice_dict["language_english"] = english_lang
+
+ return jsonify(voice_dicts)
@app.route("/process", methods=["GET", "POST"])
async def api_process():
diff --git a/mimic3_http/args.py b/mimic3_http/args.py
index 7cf5e67..0110bf2 100644
--- a/mimic3_http/args.py
+++ b/mimic3_http/args.py
@@ -86,6 +86,10 @@ def get_args(argv=None) -> argparse.Namespace:
type=int,
help="Maximum length of input text to process (default: no limit)",
)
+ parser.add_argument(
+ "--default-voice",
+ help="Default voice key to select in web interface",
+ )
parser.add_argument(
"--debug", action="store_true", help="Print DEBUG messages to console"
)
diff --git a/mimic3_http/templates/index.html b/mimic3_http/templates/index.html
index 9355091..c6db391 100644
--- a/mimic3_http/templates/index.html
+++ b/mimic3_http/templates/index.html
@@ -87,8 +87,13 @@
-
-
+
+
+
@@ -246,8 +251,8 @@
q('#speak-button').addEventListener('click', do_tts)
async function synthesize(text) {
- var voiceList = q('#voice-list')
- var voice = voiceList.options[voiceList.selectedIndex].value
+ var voiceName = q('#voice-name')
+ var voice = voiceName.options[voiceName.selectedIndex].value
var noiseScale = q('#noise-scale').value || '0.667'
var noiseW = q('#noise-w').value || '0.8'
@@ -289,8 +294,52 @@
}
}
- function voiceChanged() {
- var voiceList = q('#voice-list')
+ function langChanged(indexToSelect) {
+ // Called when voice language is changed
+
+ // If set, select a specific language by index
+ indexToSelect = indexToSelect === undefined ? -1 : indexToSelect
+
+ var voiceLang = q('#voice-language')
+
+ // Reset names
+ var voiceName = q('#voice-name')
+ for (var i = voiceName.options.length - 1; i >= 0; i--) {
+ voiceName.options[i].remove()
+ }
+
+ if (indexToSelect >= 0) {
+ // Select specific language
+ voiceLang.selectedIndex = indexToSelect
+ }
+
+ var selectedLang = voiceLang.options[voiceLang.selectedIndex].value
+ var nameIndexToSelect = -1
+
+ Object.values(voicesInfo).forEach(function(voice) {
+ if (voice.language == selectedLang) {
+ voiceName.insertAdjacentHTML(
+ 'beforeend', ''
+ )
+
+ if ((indexToSelect >= 0) && (voice.key == '{{ default_voice }}')) {
+ // Record voice name index to select
+ nameIndexToSelect = voiceName.options.length - 1
+ }
+ }
+ })
+
+ // Trigger voice name change
+ nameChanged(nameIndexToSelect)
+ }
+
+ function nameChanged(indexToSelect) {
+ // Called when voice name is changed
+
+ // If set, select a specific voice by index
+ indexToSelect = indexToSelect === undefined ? -1 : indexToSelect
+
+ var voiceName = q('#voice-name')
// Reset audio
q('#audio-message').hidden = true
@@ -304,7 +353,12 @@
speakerList.options[i].remove()
}
- var voiceKey = voiceList.options[voiceList.selectedIndex].value
+ if (indexToSelect >= 0) {
+ // Select a specific voice by index
+ voiceName.selectedIndex = indexToSelect
+ }
+
+ var voiceKey = voiceName.options[voiceName.selectedIndex].value
var voice = voicesInfo[voiceKey]
if (voice.speakers && voice.speakers.length > 0) {
@@ -329,17 +383,22 @@
}
}
- q('#voice-list').addEventListener('change', voiceChanged)
-
function loadVoices() {
voicesInfo = {}
// Remove previous voices
- var voiceList = q('#voice-list')
- for (var i = voiceList.options.length - 1; i >= 0; i--) {
- voiceList.options[i].remove()
+ var voiceLang = q('#voice-language')
+ for (var i = voiceLang.options.length - 1; i >= 0; i--) {
+ voiceLang.options[i].remove()
}
+ var voiceName = q('#voice-name')
+ for (var i = voiceName.options.length - 1; i >= 0; i--) {
+ voiceName.options[i].remove()
+ }
+
+ var langs = new Set();
+
fetch('api/voices')
.then(function(res) {
if (!res.ok) throw Error(res.statusText)
@@ -352,12 +411,19 @@
voices.forEach(function(voice) {
voicesInfo[voice.key] = voice
- voiceList.insertAdjacentHTML(
- 'beforeend', ''
- )
+ if (!langs.has(voice.language)) {
+ voiceLang.insertAdjacentHTML(
+ 'beforeend', ''
+ )
+ langs.add(voice.language)
+ }
+
+ if (voice.key == '{{ default_voice }}') {
+ indexToSelect = voiceLang.options.length - 1
+ }
})
- voiceChanged()
+ langChanged(indexToSelect)
}).catch(function(err) {
q('#message').textContent = 'Error: ' + err.message
q('#speak-button').disabled = false
@@ -366,6 +432,10 @@
window.addEventListener('load', function() {
loadVoices()
+
+ q('#voice-language').addEventListener('change', langChanged)
+ q('#voice-name').addEventListener('change', nameChanged)
+
})
diff --git a/mimic3_tts/utils.py b/mimic3_tts/utils.py
index 21d0752..f5a7720 100644
--- a/mimic3_tts/utils.py
+++ b/mimic3_tts/utils.py
@@ -28,8 +28,8 @@ LANG_NAMES = {
"af_ZA": "Afrikaans",
"da_DK": ("Dansk", "Danish"),
"de_DE": ("Deutsch", "German"),
- "en_UK": "English",
- "en_US": "English",
+ "en_UK": "English (UK)",
+ "en_US": "English (US)",
"el_GR": ("Ελληνικά", "Greek"),
"es_ES": ("Español", "Spanish"),
"fa": ("فارسی", "Persian"),