diff --git a/public/src/chat.js b/public/src/chat.js index bfce1a8..0dd0174 100644 --- a/public/src/chat.js +++ b/public/src/chat.js @@ -240,7 +240,7 @@ export async function handleUserInput() { playPauseButton.textContent = "Processing..."; playPauseButton.disabled = true; // Disable button while processing const mainVoiceSelect = document.getElementById("read-page-voice"); - const selectedVoice = mainVoiceSelect ? mainVoiceSelect.value : "aria"; + const selectedVoice = mainVoiceSelect ? mainVoiceSelect.value : "clara"; const result = await speakText(accumulatedContent, selectedVoice, 0.9); aiAudio = result.audio; aiBlob = result.blob; diff --git a/public/src/page-reader.js b/public/src/page-reader.js index 77cd4ad..b1802c4 100644 --- a/public/src/page-reader.js +++ b/public/src/page-reader.js @@ -30,8 +30,8 @@ export async function readPageWithHermes(button = null) { const processedContent = await processContentWithHermes(content); - // Get voice preference from settings (format: "model:voice", e.g., "tts-1-f5:aria") - let voice = "aria"; + // Get voice preference from settings (format: "model:voice", e.g., "tts-1-f5:clara") + let voice = "clara"; let model = "tts-1-f5"; try { const savedVoice = localStorage.getItem("uncloseai_selected_voice"); diff --git a/public/src/tts-modal.js b/public/src/tts-modal.js index e73da00..621451a 100644 --- a/public/src/tts-modal.js +++ b/public/src/tts-modal.js @@ -116,10 +116,10 @@ function renderVoiceOptions(voiceSelection, voices) { voiceSelection.innerHTML = ''; // Clear existing options // Get saved voice preference (vault first, fallback to default) - let savedVoice = 'tts-1-f5:aria'; // Default + let savedVoice = 'tts-1-f5:clara'; // Default try { if (typeof window !== 'undefined' && window.UncloseVault && window.UncloseVault.isUnlocked()) { - savedVoice = window.UncloseVault.get('uncloseai_selected_voice', 'tts-1-f5:aria'); + savedVoice = window.UncloseVault.get('uncloseai_selected_voice', 'tts-1-f5:clara'); } } catch (error) { console.warn('Failed to read voice preference:', error); diff --git a/public/src/tts.js b/public/src/tts.js index f6217ca..076d292 100644 --- a/public/src/tts.js +++ b/public/src/tts.js @@ -350,7 +350,7 @@ function cleanTextForTTS(text) { // Function to read text using TTS - for Read Page button (uses Hermes). // Multi-chunk uses pipelined playlist: playback starts as soon as chunk 1 lands. -export async function speakText(text, voice = "aria", rate = 0.9, model = "tts-1-f5") { +export async function speakText(text, voice = "clara", rate = 0.9, model = "tts-1-f5") { try { const spokenText = await extractSpokenTokens(text); const chunks = splitTextIntoChunks(spokenText); @@ -415,11 +415,13 @@ const LEGACY_OPENAI_VOICES = new Set([ ]); // Normalize a model+voice pair: remap legacy / unknown models to F5, and -// remap legacy OpenAI voice names to F5's "aria" default. +// remap legacy OpenAI voice names + aria (which has loud F5 phantom bleed) to +// "clara" which produces clean audio per multi-voice transcription test. +const PHANTOM_VOICES = new Set(["aria", "luna", "sage", "cole", "jude"]); export function normalizeModelVoice(model, voice) { const isQwen = !model || model === "tts-1" || model.includes("qwen"); const normModel = isQwen ? "tts-1-f5" : model; - const normVoice = LEGACY_OPENAI_VOICES.has(voice) ? "aria" : voice; + const normVoice = LEGACY_OPENAI_VOICES.has(voice) || PHANTOM_VOICES.has(voice) ? "clara" : voice; return { model: normModel, voice: normVoice }; } @@ -448,7 +450,7 @@ export function primeAudioPlayback() { // Direct TTS function for modal - no Hermes preprocessing. // Multi-chunk uses pipelined playlist: playback starts as soon as chunk 1 lands. -export async function speakTextDirect(text, voice = "aria", rate = 0.9, model = "tts-1-f5") { +export async function speakTextDirect(text, voice = "clara", rate = 0.9, model = "tts-1-f5") { try { const cleanedText = cleanTextForTTS(text); const chunks = splitTextIntoChunks(cleanedText); @@ -476,7 +478,7 @@ export async function speakTextDirect(text, voice = "aria", rate = 0.9, model = // Streaming TTS function - plays audio as chunks arrive using MediaSource API // Returns immediately with audio element; done promise resolves when stream completes // Automatically chunks long text and streams each chunk sequentially -export async function speakTextStreaming(text, voice = "aria", rate = 0.9, model = "tts-1-f5") { +export async function speakTextStreaming(text, voice = "clara", rate = 0.9, model = "tts-1-f5") { const cleanedText = cleanTextForTTS(text); const textChunks = splitTextIntoChunks(cleanedText); console.log("TTS streaming input preview:", cleanedText.substring(0, 100) + "..."); @@ -793,7 +795,7 @@ export async function generateTitleForTTS(text) { // Handle TTS - respects streaming mode setting // Caches audio blobs in memory so replay doesn't regenerate // Cache is keyed by text+voice+model and clears when the tab closes -export async function handleTTS(text, voice = "aria", rate = 0.9, model = "tts-1-f5") { +export async function handleTTS(text, voice = "clara", rate = 0.9, model = "tts-1-f5") { try { const cacheKey = `${model}:${voice}:${text}`; diff --git a/public/src/vault.js b/public/src/vault.js index 115cb56..7181de3 100644 --- a/public/src/vault.js +++ b/public/src/vault.js @@ -409,7 +409,7 @@ const UncloseVault = { // Model selection selectedModel: null, selectedEndpoint: null, - selectedVoice: 'tts-1-f5:aria', + selectedVoice: 'tts-1-f5:clara', // Custom API useCustomAPI: false,