Refactor into a single package

This commit is contained in:
Michael Hansen 2022-05-02 15:24:11 -04:00
commit 5e866e4fca
94 changed files with 198 additions and 2867 deletions

View file

@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Mimic 3 text to speech server">
<meta name="author" content="Michael Hansen">
<link rel="icon" type="image/png" href="img/favicon.png" />
<title>Mimic 3</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<style>
body {
padding-top: 0;
}
@media (min-width: 992px) {
body {
padding-top: 0;
}
}
#mimic-logo {
height: 5rem;
}
#mycroft-logo {
height: 2rem;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
<!-- Page Content -->
<div id="main" class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h1>
<img id="mimic-logo" src="img/Mimic_color.png" />
Mimic 3
</h1>
</div>
</div>
<div class="row mt-3">
<div class="col">
<textarea id="text" placeholder="Type here..." class="form-control" rows="3" name="text" alt="Text to generate speech from"></textarea>
</div>
<div class="col-auto">
<button id="speak-button" name="speak" class="btn btn-lg btn-primary" alt="Generate speech">Speak</button>
{% if show_openapi %}
<br/><br />
<a href="/openapi/" title="OpenAPI page" target="_blank" class="badge badge-info">API</a>
{% endif %}
</div>
</div>
<div class="row mt-3">
<div class="col-auto">
<label for="voice-list" title="Voice name">Voice:</label>
<select id="voice-list" name="voices">
</select>
</div>
<div class="col-auto">
<label for="speaker" title="Name of speaker">Speaker:</label>
<select id="speaker-list" name="speaker">
</select>
</div>
<div class="col-auto">
<input type="checkbox" id="ssml">
<label class="ml-1" for="ssml">SSML</label>
</div>
</div>
<div id="audio-message" class="row mt-3" hidden>
<div class="col">
<audio id="audio" preload="none" controls autoplay hidden></audio>
<p id="message"></p>
</div>
</div>
<div class="row mt-3">
<div class="col-auto">
<label for="noise-scale" title="Voice volatility">Noise:</label>
<input type="number" id="noise-scale" name="noiseScale" size="5" min="0" max="1" step="0.001" value="0.667">
<label for="noise-w" class="ml-2" title="Voice volatility 2">Noise W:</label>
<input type="number" id="noise-w" name="noiseW" size="5" min="0" max="1" step="0.001" value="0.8">
<label for="length-scale" class="ml-2" title="Voice speed (< 1 is faster)">Length:</label>
<input type="number" id="length-scale" name="lengthScale" size="5" min="0" step="0.001" value="1">
</div>
</div>
<hr class="mt-5" />
<div class="row mt-5 justify-content-center">
<a href="https://mycroft.ai" title="Mycroft AI">
<img id="mycroft-logo" src="img/Mycroft_logo_two_typeonly.png" />
</a>
</div>
<div class="row mt-3 justify-content-center">
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html" title="AGPLv3">License</a>
</div>
</div>
<!-- Bootstrap core JavaScript -->
<script>
var voicesInfo = {}
function q(selector) {return document.querySelector(selector)}
q('#text').focus()
function do_tts(e) {
text = q('#text').value
if (text) {
q('#message').textContent = 'Synthesizing...'
q('#speak-button').disabled = true
q('#audio').hidden = true
synthesize(text)
}
e.preventDefault()
return false
}
q('#speak-button').addEventListener('click', do_tts)
async function synthesize(text) {
var voiceList = q('#voice-list')
var voice = voiceList.options[voiceList.selectedIndex].value
var noiseScale = q('#noise-scale').value || '0.667'
var noiseW = q('#noise-w').value || '0.8'
var lengthScale = q('#length-scale').value || '1.0'
var speakerList = q('#speaker-list')
var speaker = speakerList.options[speakerList.selectedIndex].value
if (speaker.length > 0) {
voice = voice + "#" + speaker
}
var ssml = q('#ssml').value || 'false'
q('#audio-message').hidden = false
var startTime = performance.now()
res = await fetch(
'api/tts?text=' + encodeURIComponent(text) +
'&voice=' + encodeURIComponent(voice) +
'&noiseScale=' + encodeURIComponent(noiseScale) +
'&noiseW=' + encodeURIComponent(noiseW) +
'&lengthScale=' + encodeURIComponent(lengthScale) +
'&ssml=' + encodeURIComponent(ssml),
{cache: 'no-cache'})
if (res.ok) {
blob = await res.blob()
var elapsedTime = performance.now() - startTime
q('#message').textContent = (elapsedTime / 1000) + ' second(s)'
q('#speak-button').disabled = false
q('#audio').src = URL.createObjectURL(blob)
q('#audio').hidden = false
} else {
message = await res.text()
q('#message').textContent = message
q('#speak-button').disabled = false
}
}
function voiceChanged() {
var voiceList = q('#voice-list')
// Reset audio
q('#audio-message').hidden = true
q('#message').textContent = ''
q('#audio').hidden = true
q('#audio').autoplay = true
// Reset speakers
var speakerList = q('#speaker-list')
for (var i = speakerList.options.length - 1; i >= 0; i--) {
speakerList.options[i].remove()
}
var voiceKey = voiceList.options[voiceList.selectedIndex].value
var voice = voicesInfo[voiceKey]
if (voice.speakers && voice.speakers.length > 0) {
voice.speakers.forEach(function(speaker) {
speakerList.insertAdjacentHTML(
'beforeend', '<option value="' + speaker + '">' + speaker + '</option>'
)
})
} else {
// Add default speaker
speakerList.insertAdjacentHTML(
'beforeend', '<option value="">default</option>'
)
}
// Update inference settings
if (voice.properties) {
q('#length-scale').value = voice.properties.length_scale || 1.0
q('#noise-scale').value = voice.properties.noise_scale || 0.667
q('#noise-w').value = voice.properties.noise_w || 0.8
}
}
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()
}
fetch('api/voices')
.then(function(res) {
if (!res.ok) throw Error(res.statusText)
return res.json()
}).then(function(voices) {
voicesInfo = {}
// Populate select
var indexToSelect = -1
voices.forEach(function(voice) {
voicesInfo[voice.key] = voice
voiceList.insertAdjacentHTML(
'beforeend', '<option value="' + voice.key + '">' + voice.language + '/' + voice.name + '</option>'
)
})
voiceChanged()
}).catch(function(err) {
q('#message').textContent = 'Error: ' + err.message
q('#speak-button').disabled = false
})
}
window.addEventListener('load', function() {
loadVoices()
})
</script>
</body>
</html>