diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js
index 31c23ce8b..f5e760109 100644
--- a/public/scripts/extensions/tts/index.js
+++ b/public/scripts/extensions/tts/index.js
@@ -34,11 +34,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 +58,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) {
@@ -75,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) {
@@ -127,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 //
//##################//
@@ -136,11 +170,15 @@ let audioElement = new Audio()
let audioJobQueue = []
let currentAudioJob
let audioPaused = false
-let queueProcessorReady = true
+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
@@ -185,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'
@@ -195,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()
}
@@ -207,7 +256,8 @@ function addAudioControl() {
}
function completeCurrentAudioJob() {
- queueProcessorReady = true
+ audioQueueProcessorReady = true
+ currentAudioJob = null
lastAudioPosition = 0
// updateUiPlayState();
}
@@ -227,16 +277,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
}
}
@@ -245,7 +295,7 @@ async function processAudioJobQueue() {
//################//
let ttsJobQueue = []
-let currentTtsJob
+let currentTtsJob // Null if nothing is currently being processed
let currentMessageNumber = 0
function completeTtsJob() {
@@ -334,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) {
@@ -421,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');
@@ -509,6 +565,10 @@ $(document).ready(function () {
Enabled
+