From abb3912c65fb5203b07ee79595ee73e8ad190e21 Mon Sep 17 00:00:00 2001 From: dsward2 Date: Mon, 28 Sep 2026 19:34:19 -0500 Subject: [PATCH] Text to Speech page: Speak Text section with voice menu and markup guide A new section at the bottom of the web UI's Text to Speech page: a text box (prefilled with an SSML example), a Voice menu of installed voices ("Default voice" = the Text to Speech voice setting), and a Speak button that speaks the text once through the live pipeline. Text starting with --- .../Services/AntennaHeadHTTPServer.swift | 67 +++++++++++++++++++ AntennaHead/Services/SDRController.swift | 37 +++++++++- Web/js/antennahead.js | 30 +++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/AntennaHead/Services/AntennaHeadHTTPServer.swift b/AntennaHead/Services/AntennaHeadHTTPServer.swift index b85fd4b..8860c27 100644 --- a/AntennaHead/Services/AntennaHeadHTTPServer.swift +++ b/AntennaHead/Services/AntennaHeadHTTPServer.swift @@ -765,6 +765,13 @@ final class AntennaHeadHTTPServer { sequence: sequence, repeatForever: repeatForever) return okResponse() + case "/speaktextbuttonclicked.html": + // Body: {text, voice}. The Text to Speech page's Speak Text + // section; `voice` "" = the Text to Speech voice setting. + let so = jsonObject(fromBody: request.body) + sdrController?.speakText(so.string("text"), voiceIdentifier: so.string("voice")) + return okResponse() + case "/playaudiofileslistenbuttonclicked.html": // Same payload shape as Text to Speech, plus an optional `playlist` // name: {sequence, repeat, files, playlist}. A non-empty playlist @@ -2459,9 +2466,69 @@ final class AntennaHeadHTTPServer { s += "onclick=\"textToSpeechListenButtonClicked(getElementById('textToSpeechForm'));\" " s += "title='Synthesize the selected folder's text files and stream them through the live audio pipeline.'>" s += "
 
" + s += speakTextFormHTML() + return s + } + + /// The SSML example the Speak Text box starts with. + nonisolated static let speakTextExample = + #"It is four oh nine on AntennaHead Radio."# + + /// "Speak Text" section at the bottom of the Text to Speech page: type + /// text, pick a voice, Speak → `/speaktextbuttonclicked.html` → + /// `SDRController.speakText`. Text starting with ` String { + var s = "
" + s += "" + s += "

Type text and speak it once through the live audio pipeline. Text that starts with " + s += "<speak> is read as SSML markup (see below).

" + s += "