tts: switch default voice aria -> clara (aria has loud F5 phantom bleed)
This commit is contained in:
parent
ad3568ff3c
commit
378662b62f
5 changed files with 14 additions and 12 deletions
|
|
@ -240,7 +240,7 @@ export async function handleUserInput() {
|
||||||
playPauseButton.textContent = "Processing...";
|
playPauseButton.textContent = "Processing...";
|
||||||
playPauseButton.disabled = true; // Disable button while processing
|
playPauseButton.disabled = true; // Disable button while processing
|
||||||
const mainVoiceSelect = document.getElementById("read-page-voice");
|
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);
|
const result = await speakText(accumulatedContent, selectedVoice, 0.9);
|
||||||
aiAudio = result.audio;
|
aiAudio = result.audio;
|
||||||
aiBlob = result.blob;
|
aiBlob = result.blob;
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ export async function readPageWithHermes(button = null) {
|
||||||
|
|
||||||
const processedContent = await processContentWithHermes(content);
|
const processedContent = await processContentWithHermes(content);
|
||||||
|
|
||||||
// Get voice preference from settings (format: "model:voice", e.g., "tts-1-f5:aria")
|
// Get voice preference from settings (format: "model:voice", e.g., "tts-1-f5:clara")
|
||||||
let voice = "aria";
|
let voice = "clara";
|
||||||
let model = "tts-1-f5";
|
let model = "tts-1-f5";
|
||||||
try {
|
try {
|
||||||
const savedVoice = localStorage.getItem("uncloseai_selected_voice");
|
const savedVoice = localStorage.getItem("uncloseai_selected_voice");
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,10 @@ function renderVoiceOptions(voiceSelection, voices) {
|
||||||
voiceSelection.innerHTML = ''; // Clear existing options
|
voiceSelection.innerHTML = ''; // Clear existing options
|
||||||
|
|
||||||
// Get saved voice preference (vault first, fallback to default)
|
// Get saved voice preference (vault first, fallback to default)
|
||||||
let savedVoice = 'tts-1-f5:aria'; // Default
|
let savedVoice = 'tts-1-f5:clara'; // Default
|
||||||
try {
|
try {
|
||||||
if (typeof window !== 'undefined' && window.UncloseVault && window.UncloseVault.isUnlocked()) {
|
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) {
|
} catch (error) {
|
||||||
console.warn('Failed to read voice preference:', error);
|
console.warn('Failed to read voice preference:', error);
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ function cleanTextForTTS(text) {
|
||||||
|
|
||||||
// Function to read text using TTS - for Read Page button (uses Hermes).
|
// 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.
|
// 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 {
|
try {
|
||||||
const spokenText = await extractSpokenTokens(text);
|
const spokenText = await extractSpokenTokens(text);
|
||||||
const chunks = splitTextIntoChunks(spokenText);
|
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
|
// 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) {
|
export function normalizeModelVoice(model, voice) {
|
||||||
const isQwen = !model || model === "tts-1" || model.includes("qwen");
|
const isQwen = !model || model === "tts-1" || model.includes("qwen");
|
||||||
const normModel = isQwen ? "tts-1-f5" : model;
|
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 };
|
return { model: normModel, voice: normVoice };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +450,7 @@ export function primeAudioPlayback() {
|
||||||
|
|
||||||
// Direct TTS function for modal - no Hermes preprocessing.
|
// Direct TTS function for modal - no Hermes preprocessing.
|
||||||
// Multi-chunk uses pipelined playlist: playback starts as soon as chunk 1 lands.
|
// 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 {
|
try {
|
||||||
const cleanedText = cleanTextForTTS(text);
|
const cleanedText = cleanTextForTTS(text);
|
||||||
const chunks = splitTextIntoChunks(cleanedText);
|
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
|
// Streaming TTS function - plays audio as chunks arrive using MediaSource API
|
||||||
// Returns immediately with audio element; done promise resolves when stream completes
|
// Returns immediately with audio element; done promise resolves when stream completes
|
||||||
// Automatically chunks long text and streams each chunk sequentially
|
// 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 cleanedText = cleanTextForTTS(text);
|
||||||
const textChunks = splitTextIntoChunks(cleanedText);
|
const textChunks = splitTextIntoChunks(cleanedText);
|
||||||
console.log("TTS streaming input preview:", cleanedText.substring(0, 100) + "...");
|
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
|
// Handle TTS - respects streaming mode setting
|
||||||
// Caches audio blobs in memory so replay doesn't regenerate
|
// Caches audio blobs in memory so replay doesn't regenerate
|
||||||
// Cache is keyed by text+voice+model and clears when the tab closes
|
// 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 {
|
try {
|
||||||
const cacheKey = `${model}:${voice}:${text}`;
|
const cacheKey = `${model}:${voice}:${text}`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -409,7 +409,7 @@ const UncloseVault = {
|
||||||
// Model selection
|
// Model selection
|
||||||
selectedModel: null,
|
selectedModel: null,
|
||||||
selectedEndpoint: null,
|
selectedEndpoint: null,
|
||||||
selectedVoice: 'tts-1-f5:aria',
|
selectedVoice: 'tts-1-f5:clara',
|
||||||
|
|
||||||
// Custom API
|
// Custom API
|
||||||
useCustomAPI: false,
|
useCustomAPI: false,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue