uncloseai.com/public/text-to-speech.html

437 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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 4 TTS engines, 227+ voices, and zero API keys. Learn ML by building real TTS infrastructure.">
<!-- PicoCSS -->
<link rel="stylesheet" href="/css/pico.classless.min.css">
<!-- ChunkFive Font -->
<link rel="stylesheet" href="/css/chunkfive/stylesheet.css" type="text/css" charset="utf-8" />
<!-- Sidebar Theme -->
<link rel="stylesheet" href="/css/sidebar-theme.css">
<!-- Highlight.js for syntax highlighting -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/a11y-dark.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<!-- Theme Switcher Script -->
<script>
function switchTheme(theme) {
if (theme === "auto") {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
var iframe = document.getElementById('remarkbox-iframe');
if (iframe && window.buildRbSrc) {
iframe.src = buildRbSrc();
}
}
window.UNCLOSEAI_CUSTOM_STYLING = false;
</script>
<script src="https://uncloseai.com/uncloseai.js" type="module"></script>
</head>
<body>
<!-- Mobile menu toggle -->
<button class="sidebar-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')">
</button>
<!-- Left sidebar - Main Navigation -->
<aside class="sidebar">
<div class="table-of-contents">
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/text-to-speech.html" class="active">Text-to-Speech</a></li>
<li><a href="/csharp-examples.html">C# Examples</a></li>
<li><a href="/dart-examples.html">Dart Examples</a></li>
<li><a href="/elixir-examples.html">Elixir Examples</a></li>
<li><a href="/go-examples.html">Go Examples</a></li>
<li><a href="/java-examples.html">Java Examples</a></li>
<li><a href="/kotlin-examples.html">Kotlin Examples</a></li>
<li><a href="/nodejs-examples.html">Node.js Examples</a></li>
<li><a href="/php-examples.html">PHP Examples</a></li>
<li><a href="/python-examples.html">Python Examples</a></li>
<li><a href="/ruby-examples.html">Ruby Examples</a></li>
<li><a href="/rust-examples.html">Rust Examples</a></li>
<li><a href="/swift-examples.html">Swift Examples</a></li>
<li><a href="/uncloseai-js.html">uncloseai.js Docs</a></li>
<li><a href="/inference.html">Inference Setup</a></li>
<li><a href="/text-to-speech.html">Text-to-Speech</a></li>
<li><a href="/languages" target="_blank">🔗 All Languages</a></li>
<li><a href="https://shop.unturf.com/p/8486f492-a93e-11f0-b477-02dfe05770ee/uncloseai-machine-learning-reference-guide-to-inference-clients" target="_blank">📚 Book</a></li>
</ul>
</nav>
</div>
</aside>
<!-- Main content -->
<main>
<header>
<hgroup>
<a href="https://uncloseai.com"><h1 class="unturf" style="font-family: 'ChunkFiveRegular';">uncloseai.</h1></a>
<p>Open-Source Text-to-Speech Infrastructure</p>
</hgroup>
<nav>
<ul>
<li><a href="#" onclick="switchTheme('auto')">Auto</a></li>
<li><a href="#" onclick="switchTheme('light')">Light</a></li>
<li><a href="#" onclick="switchTheme('dark')">Dark</a></li>
</ul>
</nav>
</header>
<h2 id="raccoon-mission">🦝 Raccoon Mission: Rescuing Abandoned TTS Models</h2>
<p><strong>uncloseai-speech</strong> is our community-driven initiative to rescue, preserve, and unify abandoned text-to-speech models into a single, resilient, self-hostable API.</p>
<blockquote>
<p><strong>"Why raccoons?"</strong> Because like raccoons, we dig through the digital dumpsters of abandoned GitHub repos and archived projects, rescuing valuable open-source TTS models that orgs have left behind. We give them a new home, maintain them, and make them accessible to everyone.</p>
</blockquote>
<h3 id="why-this-matters">Why This Matters</h3>
<p>Proprietary TTS APIs lock you into vendor ecosystems, charge per character, and can vanish overnight. We're building something different:</p>
<ul>
<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>Four TTS Engines:</strong> Piper (fast), XTTS (quality), Silero (CPU-friendly), Kokoro (lightweight)</li>
<li><strong>227+ Voices:</strong> Including multilingual support across 5 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>
<h2 id="try-it-now">Try It Now (Free API)</h2>
<p>Test our community-hosted endpoint before self-hosting:</p>
<pre><code class="bash"># Auto-detect model from voice name
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"
}' > output.mp3
# Explicitly specify model (tts-1, tts-1-hd, tts-1-silero, tts-1-kokoro)
curl https://speech.ai.unturf.com/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "tts-1-hd",
"input": "High quality XTTS voice cloning.",
"voice": "alloy"
}' > output-hd.mp3</code></pre>
<h3 id="voice-auto-detection">Auto-Detection Magic</h3>
<p>Our voice routing is smart - no need to specify the model:</p>
<pre><code class="python">from openai import OpenAI
client = OpenAI(
api_key="not-needed", # Seriously, any string works
base_url="https://speech.ai.unturf.com/v1"
)
# 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")
client.audio.speech.create(
voice="en_50", # → Silero (native voice name)
input="CPU-friendly multilingual"
).stream_to_file("silero.mp3")
client.audio.speech.create(
voice="af_heart", # → Kokoro (lightweight)
input="Decoder-only architecture"
).stream_to_file("kokoro.mp3")</code></pre>
<h3 id="discover-voices">Discover Voices</h3>
<p>Extended <code>/v1/voices</code> endpoint shows all 227 voices with engine metadata:</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>
<pre><code class="bash">curl https://speech.ai.unturf.com/v1/models | jq .</code></pre>
<h2 id="self-hosting">Self-Hosting</h2>
<p>For complete installation instructions and documentation, see the <a href="https://git.unturf.com/engineering/unturf/uncloseai-speech" target="_blank">uncloseai-speech repository</a>.</p>
<h3 id="quick-start">Quick Start</h3>
<pre><code class="bash"># Clone the repository
git clone https://git.unturf.com/engineering/unturf/uncloseai-speech.git
cd uncloseai-speech
# See all available commands
make help
# Deploy locally with Docker
make local-deploy
# Download voice models (Piper + XTTS samples)
make voices
# Test it
make test
# Watch logs
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>
<p>Deploy to a GPU server for XTTS 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)
make deploy
# Download all voice models
make voices
# Test remote endpoint
make test</code></pre>
<h2 id="the-engines">The Four Engines</h2>
<h3 id="piper">🏃 Piper TTS (tts-1)</h3>
<ul>
<li><strong>Speed:</strong> Real-time on CPU (fastest)</li>
<li><strong>Voices:</strong> 100+ English voices via LibriTTS</li>
<li><strong>Use Case:</strong> High-volume, low-latency synthesis</li>
<li><strong>Tech:</strong> ONNX runtime, 22.05kHz output</li>
</ul>
<h3 id="xtts">🎙️ XTTS v2 (tts-1-hd)</h3>
<ul>
<li><strong>Quality:</strong> Voice cloning from 6-second samples</li>
<li><strong>Languages:</strong> 16 languages with auto-detection</li>
<li><strong>Use Case:</strong> Custom voices, audiobooks, high-quality synthesis</li>
<li><strong>Tech:</strong> Coqui TTS, requires ~4GB GPU VRAM</li>
</ul>
<h3 id="silero">⚡ Silero TTS (tts-1-silero)</h3>
<ul>
<li><strong>Speed:</strong> Real-time CPU inference</li>
<li><strong>Voices:</strong> 148 voices across 5 languages (English, Russian, German, Spanish, French)</li>
<li><strong>Use Case:</strong> CPU-only servers, multilingual applications</li>
<li><strong>Tech:</strong> PyTorch, 48kHz output, actively maintained</li>
</ul>
<h3 id="kokoro">🪶 Kokoro TTS (tts-1-kokoro)</h3>
<ul>
<li><strong>Architecture:</strong> Lightweight decoder-only (82M params)</li>
<li><strong>Voices:</strong> 34 intentionally OpenAI-themed voices (American/British English)</li>
<li><strong>Use Case:</strong> Edge devices, low-resource environments</li>
<li><strong>Tech:</strong> Apache 2.0 license, 24kHz output</li>
</ul>
<h2 id="get-involved">Get Involved</h2>
<p>We run a free public endpoint at <code>https://speech.ai.unturf.com/v1</code> but we need help scaling:</p>
<h3 id="contribute-infrastructure">Contribute Infrastructure</h3>
<ul>
<li><strong>Donate GPU Time:</strong> Run an instance, we'll load-balance community traffic</li>
<li><strong>Host Regional Mirrors:</strong> Reduce latency for users in your region</li>
<li><strong>Sponsor Server Costs:</strong> Help keep the public endpoint free for everyone</li>
</ul>
<h3 id="contribute-code">Contribute Code</h3>
<ul>
<li><strong>Integrate New Engines:</strong> StyleTTS2, Fish Speech, Chatterbox are on the roadmap</li>
<li><strong>Add Voice Samples:</strong> Expand XTTS voice library with diverse accents</li>
<li><strong>Improve Documentation:</strong> Write tutorials, create video walkthroughs</li>
<li><strong>Build Tools:</strong> Voice editor UI, quality benchmarking, automated testing</li>
</ul>
<h2 id="resources">Resources</h2>
<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>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>
<h2 id="example-walkthrough">Complete Example: Voice Cloning</h2>
<p>Let's clone your voice and use it via the API:</p>
<h3>Step 1: Record Your Voice Sample</h3>
<pre><code class="bash"># Record 10 seconds of clean audio (22050 Hz, mono)
ffmpeg -f alsa -i default -ac 1 -ar 22050 -t 10 -y my_voice.wav
# Clean up background noise
ffmpeg -i my_voice.wav \
-af "highpass=f=200, lowpass=f=3000, afftdn=nf=25" \
-ac 1 -ar 22050 my_voice_clean.wav</code></pre>
<h3>Step 2: Add to Voice Configuration</h3>
<pre><code class="bash"># Copy sample to voices directory
cp my_voice_clean.wav ~/uncloseai-speech/voices/me.wav
# Add to config/voice_to_speaker.yaml
cat >> config/voice_to_speaker.yaml << 'EOF'
tts-1-hd:
my_voice:
model: xtts
speaker: voices/me.wav
language: en
EOF
# Restart server
make deploy</code></pre>
<h3>Step 3: Use Your Voice</h3>
<pre><code class="python">from openai import OpenAI
client = OpenAI(
api_key="not-needed",
base_url="https://speech.ai.unturf.com/v1"
)
# Generate speech with YOUR voice
client.audio.speech.create(
model="tts-1-hd",
voice="my_voice",
input="This is my cloned voice speaking!"
).stream_to_file("cloned_output.mp3")</code></pre>
<p><strong>That's the power of open source TTS.</strong> No API keys, no usage limits, your voice, your infrastructure.</p>
<h2 id="discussions">Questions & Community</h2>
<p>Ask questions, share your deployments, or discuss TTS research below!</p>
<div id="remarkbox-div">
<noscript>
<iframe id=remarkbox-iframe src="https://my.remarkbox.com/embed?nojs=true" style="height:600px;width:100%;border:none!important" tabindex=0></iframe>
</noscript>
</div>
<script src="https://my.remarkbox.com/static/js/iframe-resizer/iframeResizer.min.js"></script>
<script>
var rb_owner_key = "944c8dfa-8b2b-11ef-af0e-29ab4fb285a0";
var thread_uri = window.location.href;
var thread_title = window.document.title;
var thread_fragment = window.location.hash;
function getCurrentTheme() {
var theme = document.documentElement.getAttribute('data-theme');
if (theme) {
return theme;
}
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
function buildRbSrc() {
var mode = getCurrentTheme();
return "https://my.remarkbox.com/embed" +
"?rb_owner_key=" + rb_owner_key +
"&thread_title=" + encodeURI(thread_title) +
"&thread_uri=" + encodeURIComponent(thread_uri) +
"&mode=" + mode +
thread_fragment;
}
var rb_src = buildRbSrc();
function create_remarkbox_iframe() {
var ifrm = document.createElement("iframe");
ifrm.setAttribute("id", "remarkbox-iframe");
ifrm.setAttribute("scrolling", "no");
ifrm.setAttribute("src", rb_src);
ifrm.setAttribute("frameborder", "0");
ifrm.setAttribute("tabindex", "0");
ifrm.setAttribute("title", "Remarkbox");
ifrm.style.width = "100%";
document.getElementById("remarkbox-div").appendChild(ifrm);
}
create_remarkbox_iframe();
iFrameResize(
{
checkOrigin: ["https://my.remarkbox.com"],
inPageLinks: true,
initCallback: function(e) { e.iFrameResizer.moveToAnchor(thread_fragment) }
},
document.getElementById("remarkbox-iframe")
);
</script>
<script>hljs.highlightAll();</script>
<footer>
<small>© uncloseai. 2025</small>
<br>
<small>Stylesheets by <a href="https://picocss.com" target="_blank">PicoCSS</a></small>
<small>& <a href="https://highlightjs.org/" target="_blank">highlight.js</a></small>
<br>
<small><a href="/privacy-policy.html">Privacy Policy</a> | <a href="/terms-of-use.html">Terms of Use</a></small>
</footer>
<script>
document.querySelectorAll('.table-of-contents a').forEach(link => {
link.addEventListener('click', function() {
document.querySelectorAll('.table-of-contents a').forEach(a => a.classList.remove('active'));
this.classList.add('active');
});
});
const sections = document.querySelectorAll('h2[id], h3[id]');
const navLinks = document.querySelectorAll('.table-of-contents a');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (window.scrollY >= sectionTop - 100) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + current) {
link.classList.add('active');
}
});
});
</script>
</main>
</body>
</html>