| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>SuperTonic3 - Local TTS</title> |
| <link rel="stylesheet" href="/static/css/style.css" /> |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| <link |
| href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" |
| rel="stylesheet" |
| /> |
| </head> |
| <body> |
| <script> |
| window.SUPERTONIC3_CONFIG = { |
| isDocker: {{ 'true' if is_docker else 'false' }} |
| }; |
| </script> |
|
|
| <div class="container"> |
| <header> |
| <h1>SuperTonic3</h1> |
| <p class="subtitle">Local text-to-speech with 31 languages & 10 built-in voices</p> |
| <p |
| class="version-strip" |
| data-server-version="{{ versions.server }}" |
| data-supertonic3-version="{{ versions.supertonic3 }}" |
| > |
| Server v{{ versions.server }} |
| · supertonic3 v{{ versions.supertonic3 }} |
| </p> |
| </header> |
|
|
| <div class="content"> |
| <div class="control-group"> |
| <label for="text-input">Text Prompt</label> |
| <textarea |
| id="text-input" |
| placeholder="Enter text to generate speech..." |
| >Hello! I am SuperTonic3, running locally on your machine.</textarea> |
| </div> |
|
|
| <div class="control-group"> |
| <label for="voice-input">Voice Selection</label> |
| <div class="input-with-action"> |
| <input |
| id="voice-input" |
| type="text" |
| autocomplete="off" |
| placeholder="Select a voice..." |
| /> |
| <button |
| type="button" |
| class="clear-btn" |
| id="voice-clear-btn" |
| aria-label="Clear voice search" |
| > |
| <svg |
| width="14" |
| height="14" |
| viewBox="0 0 24 24" |
| fill="none" |
| stroke="currentColor" |
| stroke-width="2" |
| stroke-linecap="round" |
| stroke-linejoin="round" |
| > |
| <line x1="18" y1="6" x2="6" y2="18"></line> |
| <line x1="6" y1="6" x2="18" y2="18"></line> |
| </svg> |
| </button> |
| <ul |
| id="voice-list" |
| class="voice-list" |
| aria-label="Available voices" |
| > |
| </ul> |
| </div> |
| <div id="voice-id-display" class="voice-id-helper hidden"> |
| ID: <span class="voice-id-text"></span> |
| <button |
| class="copy-btn" |
| id="voice-id-copy-btn" |
| title="Copy to clipboard" |
| > |
| <svg |
| width="12" |
| height="12" |
| viewBox="0 0 24 24" |
| fill="none" |
| stroke="currentColor" |
| stroke-width="2" |
| stroke-linecap="round" |
| stroke-linejoin="round" |
| > |
| <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect> |
| <path |
| d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" |
| ></path> |
| </svg> |
| </button> |
| </div> |
| </div> |
|
|
| <div class="control-group"> |
| <label for="format-select">Output Format</label> |
| <select |
| id="format-select" |
| style=" |
| width: 100%; |
| padding: 8px; |
| border-radius: 4px; |
| border: 1px solid var(--border-color); |
| background: var(--input-bg); |
| color: var(--text-color); |
| " |
| > |
| <option value="mp3">MP3</option> |
| <option value="opus">Opus</option> |
| <option value="aac">AAC</option> |
| <option value="flac">FLAC</option> |
| <option value="wav" selected>WAV</option> |
| <option value="pcm">PCM (Raw)</option> |
| </select> |
| </div> |
|
|
| <div class="control-group"> |
| <label |
| for="stream-toggle" |
| style="display: inline-flex; align-items: center; cursor: pointer" |
| > |
| <input |
| type="checkbox" |
| id="stream-toggle" |
| checked |
| style="width: auto; margin-right: 8px" |
| /> |
| Enable Streaming Response |
| </label> |
| <p |
| id="format-info" |
| style=" |
| font-size: 0.85em; |
| color: var(--text-secondary); |
| margin-top: 8px; |
| line-height: 1.4; |
| " |
| ></p> |
| </div> |
|
|
| <button id="generate-btn" class="btn-primary"> |
| <span>Generate Speech</span> |
| <div class="spinner"></div> |
| </button> |
|
|
| <div class="output-section" id="output-section"> |
| <audio id="audio-player" controls></audio> |
| <br /> |
| <a href="#" id="download-btn" class="download-link">Download Audio</a> |
| </div> |
| </div> |
|
|
| <div class="api-docs"> |
| <h2>API Documentation</h2> |
| <p class="api-intro"> |
| OpenAI-compatible TTS API. Use any OpenAI TTS client by pointing it to |
| this server. |
| </p> |
|
|
| <div class="endpoint"> |
| <div class="endpoint-header"> |
| <span class="method get">GET</span> |
| <code class="path">/health</code> |
| </div> |
| <p class="endpoint-desc"> |
| Health check endpoint for container orchestration and monitoring. |
| </p> |
| <div class="response-example"> |
| <strong>Response:</strong> |
| <pre> |
| { |
| "status": "healthy", |
| "model_loaded": true, |
| "sample_rate": 44100 |
| }</pre> |
| </div> |
| </div> |
|
|
| <div class="endpoint"> |
| <div class="endpoint-header"> |
| <span class="method get">GET</span> |
| <code class="path">/v1/voices</code> |
| </div> |
| <p class="endpoint-desc"> |
| List all available built-in voices. |
| </p> |
| <div class="response-example"> |
| <strong>Response:</strong> |
| <pre> |
| { |
| "object": "list", |
| "data": [ |
| {"id": "M1", "name": "M1", "object": "voice", "type": "builtin"}, |
| {"id": "F1", "name": "F1", "object": "voice", "type": "builtin"} |
| ] |
| }</pre> |
| </div> |
| </div> |
|
|
| <div class="endpoint"> |
| <div class="endpoint-header"> |
| <span class="method post">POST</span> |
| <code class="path">/v1/audio/speech</code> |
| </div> |
| <p class="endpoint-desc"> |
| Generate speech audio from text. OpenAI-compatible endpoint. |
| </p> |
|
|
| <div class="params-section"> |
| <strong>Request Body (JSON):</strong> |
| <table class="params-table"> |
| <thead> |
| <tr> |
| <th>Parameter</th> |
| <th>Type</th> |
| <th>Required</th> |
| <th>Default</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td><code>input</code></td> |
| <td>string</td> |
| <td>✓</td> |
| <td>-</td> |
| <td>The text to generate speech from</td> |
| </tr> |
| <tr> |
| <td><code>voice</code></td> |
| <td>string</td> |
| <td></td> |
| <td><code>M1</code></td> |
| <td> |
| Voice ID. See <code>/v1/voices</code> |
| </td> |
| </tr> |
| <tr> |
| <td><code>model</code></td> |
| <td>string</td> |
| <td></td> |
| <td>-</td> |
| <td>Ignored (for OpenAI compatibility)</td> |
| </tr> |
| <tr> |
| <td><code>response_format</code></td> |
| <td>string</td> |
| <td></td> |
| <td><code>mp3</code></td> |
| <td> |
| Audio format: <code>mp3</code>, <code>opus</code>, |
| <code>aac</code>, <code>flac</code>, <code>wav</code>, |
| <code>pcm</code> |
| </td> |
| </tr> |
| <tr> |
| <td><code>stream</code></td> |
| <td>boolean</td> |
| <td></td> |
| <td><code>false</code></td> |
| <td>Enable streaming response</td> |
| </tr> |
| <tr> |
| <td><code>lang</code></td> |
| <td>string</td> |
| <td></td> |
| <td><code>en</code></td> |
| <td>Language code for pronunciation</td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
|
|
| <div class="example-section"> |
| <strong>Example Request (curl):</strong> |
| <pre> |
| curl -X POST http://localhost:7860/v1/audio/speech \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "input": "Hello world!", |
| "voice": "M1", |
| "response_format": "mp3" |
| }' \ |
| --output speech.mp3</pre> |
| </div> |
|
|
| <div class="example-section"> |
| <strong>Example (Python with OpenAI client):</strong> |
| <pre> |
| from openai import OpenAI |
|
|
| client = OpenAI( |
| base_url="http://localhost:7860/v1", |
| api_key="not-needed" |
| ) |
|
|
| response = client.audio.speech.create( |
| model="tts-1", |
| voice="M1", |
| input="Hello world!" |
| ) |
| response.stream_to_file("output.mp3")</pre> |
| </div> |
|
|
| <div class="response-example"> |
| <strong>Response:</strong> |
| <p>Audio file in the requested format (binary stream).</p> |
| <p> |
| <strong>Content-Type:</strong> <code>audio/mpeg</code>, |
| <code>audio/wav</code>, <code>audio/opus</code>, etc. |
| </p> |
| </div> |
|
|
| <div class="error-section"> |
| <strong>Error Response:</strong> |
| <pre> |
| { |
| "error": "Missing required field: input" |
| }</pre> |
| </div> |
| </div> |
|
|
| <div class="voices-reference"> |
| <h3>Built-in Voices</h3> |
| <p>10 voices available: M1-M5 (male), F1-F5 (female)</p> |
| <div class="voice-chips"> |
| <span class="voice-chip">M1</span> |
| <span class="voice-chip">M2</span> |
| <span class="voice-chip">M3</span> |
| <span class="voice-chip">M4</span> |
| <span class="voice-chip">M5</span> |
| <span class="voice-chip">F1</span> |
| <span class="voice-chip">F2</span> |
| <span class="voice-chip">F3</span> |
| <span class="voice-chip">F4</span> |
| <span class="voice-chip">F5</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script src="/static/js/app.js"></script> |
| </body> |
| </html> |
|
|