From a8684993f49e7914c903bcb314b924d7a281bd52 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Tue, 16 May 2023 18:56:39 -0500 Subject: [PATCH 1/2] fix one message tts playback --- public/scripts/extensions/tts/index.js | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 31c23ce8b..4ac7b4e47 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -23,6 +23,24 @@ let ttsProviders = { } let ttsProvider let ttsProviderName +let autoGeneration = true; + +function resetTtsPlayback() { + // Clear currently processing jobs + currentTtsJob = null; + currentAudioJob = null; + + // Reset audio element + audioElement.currentTime = 0; + audioElement.src = null; + + // Clearn any queue items + ttsJobQueue.splice(0, ttsJobQueue.length); + audioJobQueue.splice(0, audioJobQueue.length); + + // Set audio ready to process again + audioQueueProcessorReady = true; +} async function onNarrateOneMessage() { cancelTtsPlay(); @@ -34,11 +52,7 @@ async function onNarrateOneMessage() { return; } - currentTtsJob = null; - audioElement.pause(); - audioElement.currentTime = 0; - ttsJobQueue.splice(0, ttsJobQueue.length); - audioJobQueue.splice(0, audioJobQueue.length); + resetTtsPlayback() ttsJobQueue.push(message); moduleWorker(); } @@ -62,7 +76,7 @@ async function moduleWorkerWrapper() { } async function moduleWorker() { - // Primarily determinign when to add new chat to the TTS queue + // Primarily determining when to add new chat to the TTS queue const enabled = $('#tts_enabled').is(':checked') $('body').toggleClass('tts', enabled); if (!enabled) { @@ -136,7 +150,7 @@ let audioElement = new Audio() let audioJobQueue = [] let currentAudioJob let audioPaused = false -let queueProcessorReady = true +let audioQueueProcessorReady = true let lastAudioPosition = 0 @@ -207,7 +221,7 @@ function addAudioControl() { } function completeCurrentAudioJob() { - queueProcessorReady = true + audioQueueProcessorReady = true lastAudioPosition = 0 // updateUiPlayState(); } @@ -227,16 +241,16 @@ async function addAudioJob(response) { async function processAudioJobQueue() { // Nothing to do, audio not completed, or audio paused - stop processing. - if (audioJobQueue.length == 0 || !queueProcessorReady || audioPaused) { + if (audioJobQueue.length == 0 || !audioQueueProcessorReady || audioPaused) { return } try { - queueProcessorReady = false + audioQueueProcessorReady = false currentAudioJob = audioJobQueue.pop() playAudioData(currentAudioJob) } catch (error) { console.error(error) - queueProcessorReady = true + audioQueueProcessorReady = true } } From 21f4bccaa41478649d10a23b69c71de03417eedd Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Tue, 16 May 2023 20:04:22 -0500 Subject: [PATCH 2/2] Provide option for manual generation --- public/scripts/extensions/tts/index.js | 97 +++++++++++++++++++------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 4ac7b4e47..f5e760109 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -23,24 +23,6 @@ let ttsProviders = { } let ttsProvider let ttsProviderName -let autoGeneration = true; - -function resetTtsPlayback() { - // Clear currently processing jobs - currentTtsJob = null; - currentAudioJob = null; - - // Reset audio element - audioElement.currentTime = 0; - audioElement.src = null; - - // Clearn any queue items - ttsJobQueue.splice(0, ttsJobQueue.length); - audioJobQueue.splice(0, audioJobQueue.length); - - // Set audio ready to process again - audioQueueProcessorReady = true; -} async function onNarrateOneMessage() { cancelTtsPlay(); @@ -89,6 +71,12 @@ async function moduleWorker() { processTtsQueue() processAudioJobQueue() updateUiAudioPlayState() + + + // Auto generation is disabled + if (extension_settings.tts.auto_generation == false){ + return + } // no characters or group selected if (!context.groupId && context.characterId === undefined) { @@ -141,6 +129,38 @@ async function moduleWorker() { ttsJobQueue.push(message) } + +function resetTtsPlayback() { + // Clear currently processing jobs + currentTtsJob = null; + currentAudioJob = null; + + // Reset audio element + audioElement.currentTime = 0; + audioElement.src = null; + + // Clear any queue items + ttsJobQueue.splice(0, ttsJobQueue.length); + audioJobQueue.splice(0, audioJobQueue.length); + + // Set audio ready to process again + audioQueueProcessorReady = true; +} + +function isTtsProcessing() { + let processing = false + + // Check job queues + if (ttsJobQueue.length > 0 || audioJobQueue > 0){ + processing = true + } + // Check current jobs + if (currentTtsJob != null || currentAudioJob != null) { + processing = true + } + return processing +} + //##################// // Audio Control // //##################// @@ -155,6 +175,10 @@ let audioQueueProcessorReady = true let lastAudioPosition = 0 async function playAudioData(audioBlob) { + // Since current audio job can be cancelled, don't playback if it is null + if (currentAudioJob == null){ + console.log("Cancelled TTS playback because currentAudioJob was null") + } const reader = new FileReader() reader.onload = function (e) { const srcUrl = e.target.result @@ -199,9 +223,13 @@ async function onTtsVoicesClick() { function updateUiAudioPlayState() { if (extension_settings.tts.enabled == true) { audioControl.style.display = 'flex' - const img = !audioElement.paused - ? 'fa-solid fa-circle-pause' - : 'fa-solid fa-circle-play' + let img + // Give user feedback that TTS is active by setting the stop icon if processing or playing + if (!audioElement.paused || isTtsProcessing()){ + img = 'fa-solid fa-stop-circle' + } else { + img = 'fa-solid fa-circle-play' + } audioControl.className = img } else { audioControl.style.display = 'none' @@ -209,7 +237,14 @@ function updateUiAudioPlayState() { } function onAudioControlClicked() { - audioElement.paused ? audioElement.play() : audioElement.pause() + let context = getContext() + // Not pausing, doing a full stop to anything TTS is doing. Better UX as pause is not as useful + if (!audioElement.paused || isTtsProcessing()){ + resetTtsPlayback() + } else { + // Default play behavior if not processing or playing is to play the last message. + ttsJobQueue.push(context.chat[context.chat.length - 1]) + } updateUiAudioPlayState() } @@ -222,6 +257,7 @@ function addAudioControl() { function completeCurrentAudioJob() { audioQueueProcessorReady = true + currentAudioJob = null lastAudioPosition = 0 // updateUiPlayState(); } @@ -259,7 +295,7 @@ async function processAudioJobQueue() { //################// let ttsJobQueue = [] -let currentTtsJob +let currentTtsJob // Null if nothing is currently being processed let currentMessageNumber = 0 function completeTtsJob() { @@ -348,14 +384,15 @@ function loadSettings() { ) $('#tts_narrate_dialogues').prop('checked', extension_settings.tts.narrate_dialogues_only) $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only) + $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation) $('body').toggleClass('tts', extension_settings.tts.enabled); } const defaultSettings = { voiceMap: '', ttsEnabled: false, - currentProvider: "ElevenLabs" - + currentProvider: "ElevenLabs", + auto_generation: true } function setTtsStatus(status, success) { @@ -435,6 +472,11 @@ function onEnableClick() { saveSettingsDebounced() } +function onAutoGenerationClick() { + extension_settings.tts.auto_generation = $('#tts_auto_generation').prop('checked'); + saveSettingsDebounced() +} + function onNarrateDialoguesClick() { extension_settings.tts.narrate_dialogues_only = $('#tts_narrate_dialogues').prop('checked'); @@ -523,6 +565,10 @@ $(document).ready(function () { Enabled +