Mercurial Hosting > lang
changeset 31:1e7d855afde3
voices
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 03 Aug 2025 17:05:38 -0600 |
parents | d48f48e1b790 |
children | d34d709a7a8e |
files | src/chat.html.luan src/chat.js src/lib/Chat.luan src/lib/Shared.luan src/save_chat.js.luan src/site.js src/tts.mp3.luan |
diffstat | 7 files changed, 46 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/src/chat.html.luan Sun Aug 03 12:25:01 2025 -0600 +++ b/src/chat.html.luan Sun Aug 03 17:05:38 2025 -0600 @@ -1,5 +1,6 @@ local Luan = require "luan:Luan.luan" local error = Luan.error +local ipairs = Luan.ipairs or error() local Parsers = require "luan:Parsers.luan" local json_string = Parsers.json_string or error() local Io = require "luan:Io.luan" @@ -8,6 +9,7 @@ local head = Shared.head or error() local header = Shared.header or error() local started = Shared.started or error() +local voices = Shared.voices or error() local User = require "site:/lib/User.luan" local current_user = User.current_required or error() local Chat = require "site:/lib/Chat.luan" @@ -67,6 +69,14 @@ <input name=name required><br> <span error></span> </p> + <p> + <label>Voice</label><br> + <select name=voice> +<% for _, voice in ipairs(voices) do %> + <option value="<%=voice.code%>"><%=voice.name%></option> +<% end %> + <select> + </p> <div buttons> <button type=button onclick="closeModal(this)">Cancel</button> <button type=submit>Save</button>
--- a/src/chat.js Sun Aug 03 12:25:01 2025 -0600 +++ b/src/chat.js Sun Aug 03 17:05:38 2025 -0600 @@ -3,13 +3,22 @@ let chat; function setChat(newChat) { + let audioChanged = chat && chat.voice != newChat.voice; chat = newChat; document.querySelector('[content] [name]').textContent = chat.name; + if(audioChanged) { + let s = `voice=${chat.voice}&`; + let audios = document.querySelectorAll('audio[src]'); + for( let audio of audios ) { + audio.src = audio.src.replace(/voice=[^&]+&/,s); + } + } } function editChat(name) { let dialog = document.querySelector('dialog[edit]'); dialog.querySelector('input[name=name]').value = chat.name; + dialog.querySelector('select[name=voice]').value = chat.voice; dialog.showModal(); } @@ -50,7 +59,7 @@ } function handleChatMarkdown() { - handleMarkdown(chat.language_region); + handleMarkdown(chat.language_region,chat.voice); } function scrollToEnd() { @@ -83,7 +92,7 @@ textarea.parentNode.scrollIntoViewIfNeeded(false); if( !audio ) audio = document.querySelector('div[buttons] audio'); - audio.src = `/tts.mp3?lang=${chat.language_region}&text=${encodeURIComponent(textarea.value)}`; + audio.src = `/tts.mp3?lang=${chat.language_region}&voice=${chat.voice}&text=${encodeURIComponent(textarea.value)}`; } function askAi() {
--- a/src/lib/Chat.luan Sun Aug 03 12:25:01 2025 -0600 +++ b/src/lib/Chat.luan Sun Aug 03 17:05:38 2025 -0600 @@ -15,6 +15,8 @@ local get_first = Utils.get_first or error() local Course = require "site:/lib/Course.luan" local get_course_by_id = Course.get_by_id or error() +local Shared = require "site:/lib/Shared.luan" +local voices = Shared.voices or error() local Chat = {} @@ -30,6 +32,7 @@ ai_thread = doc.ai_thread language = doc.language language_region = doc.language_region + voice = doc.voice } end @@ -44,6 +47,7 @@ ai_thread = chat.ai_thread language = chat.language or error() language_region = chat.language_region or error() + voice = chat.voice or error() } end @@ -54,6 +58,7 @@ function Chat.new(chat) chat.updated = chat.updated or time_now() chat.language_region = chat.language_region or first_region(chat.language) + chat.voice = chat.voice or voices[1].code function chat.save() local doc = to_doc(chat) @@ -73,6 +78,7 @@ return { id = chat.id language_region = chat.language_region + voice = chat.voice name = chat.name } end
--- a/src/lib/Shared.luan Sun Aug 03 12:25:01 2025 -0600 +++ b/src/lib/Shared.luan Sun Aug 03 17:05:38 2025 -0600 @@ -62,4 +62,15 @@ end ) end +Shared.voices = { + { + name = "Brandon" + code = "en-US-BrandonMultilingualNeural" + } + { + name = "Jenny" + code = "en-US-JennyMultilingualNeural" + } +} + return Shared
--- a/src/save_chat.js.luan Sun Aug 03 12:25:01 2025 -0600 +++ b/src/save_chat.js.luan Sun Aug 03 17:05:38 2025 -0600 @@ -15,10 +15,12 @@ return function() local chat = Http.request.parameters.chat or error() local name = Http.request.parameters.name or error() + local voice = Http.request.parameters.voice or error() run_in_transaction( function() chat = get_chat_by_id(chat) or error() chat.user_id == current_user().id or error() chat.name = name + chat.voice = voice chat.save() end ) Io.stdout = Http.response.text_writer()
--- a/src/site.js Sun Aug 03 12:25:01 2025 -0600 +++ b/src/site.js Sun Aug 03 17:05:38 2025 -0600 @@ -124,7 +124,7 @@ let mdDiv = document.createElement('div'); -function handleMarkdown(lang) { +function handleMarkdown(lang,voice) { let converter = window.markdownit({html: true}); let divs = document.querySelectorAll('[markdown]'); for( let div of divs ) { @@ -138,7 +138,7 @@ rt.remove(); } //console.log(mdDiv.textContent); - text += `\n<p><audio controls preload=none src="/tts.mp3?lang=${lang}&text=${encodeURIComponent(mdDiv.textContent)}"></audio></p>\n`; + text += `\n<p><audio controls preload=none src="/tts.mp3?lang=${lang}&voice=${voice}&text=${encodeURIComponent(mdDiv.textContent)}"></audio></p>\n`; } div.innerHTML = text; div.removeAttribute('markdown');
--- a/src/tts.mp3.luan Sun Aug 03 12:25:01 2025 -0600 +++ b/src/tts.mp3.luan Sun Aug 03 17:05:38 2025 -0600 @@ -18,10 +18,10 @@ ["X-Microsoft-OutputFormat"] = "audio-16khz-128kbitrate-mono-mp3" } -local function text_to_speech(lang,text) +local function text_to_speech(lang,voice,text) local xml = `%> <speak version='1.0' xml:lang='<%=lang%>'> - <voice name='en-US-BrandonMultilingualNeural'> + <voice name='<%=voice%>'> <%= xml_encode(text) %> </voice> </speak> @@ -36,7 +36,8 @@ return function() local lang = Http.request.parameters.lang or error() + local voice = Http.request.parameters.voice or error() local text = Http.request.parameters.text or error() - local input = text_to_speech(lang,text) + local input = text_to_speech(lang,voice,text) Http.response.binary_writer().write_from(input) end