Update TTS page: 42+ voices, new voice names, API endpoint links
- Update all examples from "alloy" to real voice names (aria, atlas) - Add clickable links to /v1/voices and /v1/models endpoints - Update voice count from 20+ to 42+ built-in cloned voices - Bump TTS modal cache version to 4 - Update deployment instructions (remove vars.sh, add deploy-cpu) - Add in-memory TTS cache for replay without regeneration
This commit is contained in:
parent
017201b5c9
commit
42eccafcac
3 changed files with 67 additions and 38 deletions
|
|
@ -11,7 +11,7 @@ async function populateVoiceDropdown(voiceSelection) {
|
|||
const cacheTimeKey = 'uncloseai_voices_cache_time';
|
||||
const cacheVersionKey = 'uncloseai_voices_cache_version';
|
||||
const cacheExpiration = 60000; // 1 minute in milliseconds
|
||||
const currentVersion = '3'; // Increment this when voice models change (v3: 20 voices)
|
||||
const currentVersion = '4'; // Increment this when voice models change (v4: 42+ built-in cloned voices)
|
||||
|
||||
try {
|
||||
const cachedTime = localStorage.getItem(cacheTimeKey);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ function detectAudioFormat() {
|
|||
|
||||
const AUDIO_FORMAT = detectAudioFormat();
|
||||
|
||||
// In-memory TTS cache - keyed by text+voice+model, cleared when tab closes
|
||||
const ttsCache = new Map();
|
||||
|
||||
// Simple JavaScript text cleaning for TTS
|
||||
function cleanTextForTTS(text) {
|
||||
return text
|
||||
|
|
@ -457,20 +460,43 @@ export async function generateTitleForTTS(text) {
|
|||
}
|
||||
|
||||
// Handle TTS - respects streaming mode setting
|
||||
// In streaming mode, returns immediately with audio playing and a `done` promise
|
||||
// In buffered mode, waits for full download then returns
|
||||
// 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 = "alloy", rate = 0.9, model = "tts-1-qwen") {
|
||||
try {
|
||||
const cacheKey = `${model}:${voice}:${text}`;
|
||||
|
||||
// Check cache - return cached blob as a new Audio element
|
||||
if (ttsCache.has(cacheKey)) {
|
||||
console.log("handleTTS: cache hit, replaying");
|
||||
const cached = ttsCache.get(cacheKey);
|
||||
const audioUrl = URL.createObjectURL(cached.blob);
|
||||
const audio = new Audio(audioUrl);
|
||||
audio.playbackRate = rate;
|
||||
setLastTTS(text, { audio, blob: cached.blob });
|
||||
return { audio, blob: cached.blob, blobUrl: audioUrl, streamed: false, done: Promise.resolve({ blob: cached.blob, blobUrl: audioUrl }) };
|
||||
}
|
||||
|
||||
const mode = getTTSMode();
|
||||
console.log(`handleTTS: using ${mode} mode`);
|
||||
let result;
|
||||
if (mode === "streaming") {
|
||||
return await speakTextStreaming(text, voice, rate, model);
|
||||
result = await speakTextStreaming(text, voice, rate, model);
|
||||
} else {
|
||||
const result = await speakTextDirect(text, voice, rate, model);
|
||||
// Add done: already-resolved promise for consistent interface
|
||||
result = await speakTextDirect(text, voice, rate, model);
|
||||
result.done = Promise.resolve({ blob: result.blob, blobUrl: result.blobUrl });
|
||||
return result;
|
||||
}
|
||||
|
||||
// Cache the blob once available
|
||||
if (result.done) {
|
||||
result.done.then(({ blob }) => {
|
||||
if (blob) {
|
||||
ttsCache.set(cacheKey, { blob });
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error in handleTTS:", error);
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="theme-color" content="#43a047">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title>Open-Source Text-to-Speech | uncloseai-speech | uncloseai.com</title>
|
||||
<meta name="description" content="Self-hostable OpenAI-compatible text-to-speech API with 5 TTS engines, 20+ cloned voices, and zero API keys. Default: Qwen3-TTS with voice cloning.">
|
||||
<meta name="description" content="Self-hostable OpenAI-compatible text-to-speech API with 5 TTS engines, 42+ built-in cloned voices, and zero API keys. Default: Qwen3-TTS with voice cloning.">
|
||||
|
||||
<!-- PicoCSS -->
|
||||
<link rel="stylesheet" href="/css/pico.classless.min.css">
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
<li><strong>Zero API Keys:</strong> No registration, no tracking, no rate limits on your own infrastructure</li>
|
||||
<li><strong>OpenAI-Compatible:</strong> Drop-in replacement - change one URL and you're running</li>
|
||||
<li><strong>Five TTS Engines:</strong> Qwen3-TTS (default, voice cloning), Piper (fast), XTTS (quality), Silero (CPU-friendly), Kokoro (lightweight)</li>
|
||||
<li><strong>20+ Cloned Voices:</strong> With 10-language support including Chinese, Japanese, Korean, and European languages</li>
|
||||
<li><strong>42+ Built-In Cloned Voices:</strong> With 10-language support including Chinese, Japanese, Korean, and European languages</li>
|
||||
<li><strong>Self-Hostable:</strong> Docker compose, Makefile-driven, runs on your hardware</li>
|
||||
<li><strong>AGPL v3 Licensed:</strong> Keeps TTS libre forever - even network service users get source code</li>
|
||||
</ul>
|
||||
|
|
@ -118,21 +118,21 @@
|
|||
|
||||
<p>Test our community-hosted endpoint before self-hosting:</p>
|
||||
|
||||
<pre><code class="bash"># Auto-detect model from voice name
|
||||
<pre><code class="bash"># Female voice (aria) - auto-detects Qwen3-TTS engine
|
||||
curl https://speech.ai.unturf.com/v1/audio/speech \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input": "Welcome to uncloseai-speech. Four TTS engines, zero API keys.",
|
||||
"voice": "alloy"
|
||||
"input": "Welcome to uncloseai-speech. Five TTS engines, 42 voices, zero API keys.",
|
||||
"voice": "aria"
|
||||
}' > output.mp3
|
||||
|
||||
# Explicitly specify model (tts-1-qwen, tts-1, tts-1-hd, tts-1-silero, tts-1-kokoro)
|
||||
# Male voice (atlas) - explicitly specify Qwen3-TTS model
|
||||
curl https://speech.ai.unturf.com/v1/audio/speech \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "tts-1-qwen",
|
||||
"input": "Qwen3-TTS voice cloning with 20 voices.",
|
||||
"voice": "alloy"
|
||||
"input": "Qwen3-TTS voice cloning with 42 built-in voices.",
|
||||
"voice": "atlas"
|
||||
}' > output-qwen.mp3</code></pre>
|
||||
|
||||
<h3 id="voice-auto-detection">Auto-Detection Magic</h3>
|
||||
|
|
@ -148,9 +148,14 @@ client = OpenAI(
|
|||
|
||||
# Auto-detects the right engine for each voice
|
||||
client.audio.speech.create(
|
||||
voice="alloy", # → Piper (fast CPU)
|
||||
input="Fast synthesis on CPU"
|
||||
).stream_to_file("piper.mp3")
|
||||
voice="aria", # → Qwen3-TTS (female, voice cloning)
|
||||
input="Voice cloning with 42 built-in voices"
|
||||
).stream_to_file("qwen.mp3")
|
||||
|
||||
client.audio.speech.create(
|
||||
voice="atlas", # → Qwen3-TTS (male, voice cloning)
|
||||
input="Male voice synthesis"
|
||||
).stream_to_file("qwen-male.mp3")
|
||||
|
||||
client.audio.speech.create(
|
||||
voice="en_50", # → Silero (native voice name)
|
||||
|
|
@ -164,13 +169,13 @@ client.audio.speech.create(
|
|||
|
||||
<h3 id="discover-voices">Discover Voices</h3>
|
||||
|
||||
<p>Extended <code>/v1/voices</code> endpoint shows all 227 voices with engine metadata:</p>
|
||||
<p>Browse all available voices with engine metadata: <a href="https://speech.ai.unturf.com/v1/voices" target="_blank"><code>speech.ai.unturf.com/v1/voices</code></a></p>
|
||||
|
||||
<pre><code class="bash">curl https://speech.ai.unturf.com/v1/voices | jq .</code></pre>
|
||||
|
||||
<h3 id="discover-models">Discover Models</h3>
|
||||
|
||||
<p>Standard OpenAI <code>/v1/models</code> endpoint shows available TTS models:</p>
|
||||
<p>See all available TTS engines: <a href="https://speech.ai.unturf.com/v1/models" target="_blank"><code>speech.ai.unturf.com/v1/models</code></a></p>
|
||||
|
||||
<pre><code class="bash">curl https://speech.ai.unturf.com/v1/models | jq .</code></pre>
|
||||
|
||||
|
|
@ -187,13 +192,10 @@ cd uncloseai-speech
|
|||
# See all available commands
|
||||
make help
|
||||
|
||||
# Deploy locally with Docker
|
||||
make local-deploy
|
||||
# Deploy with Docker (GPU)
|
||||
make deploy
|
||||
|
||||
# Download voice models (Piper + XTTS samples)
|
||||
make voices
|
||||
|
||||
# Test it
|
||||
# Test it (Qwen3-TTS model downloads automatically on first use)
|
||||
make test
|
||||
|
||||
# Watch logs
|
||||
|
|
@ -201,22 +203,21 @@ make logs</code></pre>
|
|||
|
||||
<p><strong>That's it.</strong> You now have a production TTS API running locally.</p>
|
||||
|
||||
<h3 id="remote-deployment">Remote Deployment (Production)</h3>
|
||||
<h3 id="gpu-deployment">GPU Deployment (Voice Cloning)</h3>
|
||||
|
||||
<p>Deploy to a GPU server for XTTS voice cloning:</p>
|
||||
<p>Deploy with GPU support for Qwen3-TTS voice cloning:</p>
|
||||
|
||||
<pre><code class="bash"># Configure deployment target
|
||||
cp vars.sh.example vars.sh
|
||||
# Edit vars.sh with your server details
|
||||
|
||||
# Deploy to remote server (syncs, builds, restarts)
|
||||
<pre><code class="bash"># Build and start with GPU (requires NVIDIA Docker runtime)
|
||||
make deploy
|
||||
|
||||
# Download all voice models
|
||||
make voices
|
||||
# Or CPU-only (slower, but works anywhere)
|
||||
make deploy-cpu
|
||||
|
||||
# Test remote endpoint
|
||||
make test</code></pre>
|
||||
# Test the endpoint
|
||||
make test
|
||||
|
||||
# Test all 42+ voices
|
||||
make hydrate</code></pre>
|
||||
|
||||
<h2 id="the-engines">The Five Engines</h2>
|
||||
|
||||
|
|
@ -224,7 +225,7 @@ make test</code></pre>
|
|||
<ul>
|
||||
<li><strong>Quality:</strong> State-of-the-art voice cloning from 3-second samples</li>
|
||||
<li><strong>Languages:</strong> 10 languages (Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian)</li>
|
||||
<li><strong>Voices:</strong> 20 cloned voices (alloy, echo, fable, onyx, nova, shimmer + 14 extended)</li>
|
||||
<li><strong>Voices:</strong> 42+ built-in cloned voices (aria, clara, elena, atlas, caleb, felix, and <a href="https://speech.ai.unturf.com/v1/voices" target="_blank">more</a>)</li>
|
||||
<li><strong>Use Case:</strong> Default engine for all TTS — high quality with fast 97ms first-packet latency</li>
|
||||
<li><strong>Tech:</strong> 1.7B params, Apache 2.0 license, actively maintained by Alibaba</li>
|
||||
</ul>
|
||||
|
|
@ -287,6 +288,8 @@ make test</code></pre>
|
|||
<ul>
|
||||
<li><strong>Repository:</strong> <a href="https://git.unturf.com/engineering/unturf/uncloseai-speech" target="_blank">git.unturf.com/engineering/unturf/uncloseai-speech</a></li>
|
||||
<li><strong>API Endpoint:</strong> <code>https://speech.ai.unturf.com/v1</code></li>
|
||||
<li><strong>Voices:</strong> <a href="https://speech.ai.unturf.com/v1/voices" target="_blank">speech.ai.unturf.com/v1/voices</a></li>
|
||||
<li><strong>Models:</strong> <a href="https://speech.ai.unturf.com/v1/models" target="_blank">speech.ai.unturf.com/v1/models</a></li>
|
||||
<li><strong>Documentation:</strong> See <code>docs/</code> directory in repository</li>
|
||||
<li><strong>Community:</strong> Comments section below, or open an issue on GitLab</li>
|
||||
</ul>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue