Pick a voice on the left
to edit it here
No matching sources found.${errorText?" Source errors: "+escHtml(errorText):""}
${more} more matches. Narrow the search or filters to see them.
Add at least one source URL, then scrape again.
Scrape failed: ${escHtml(e.message)}
Check that the TTS Voice Creator backend is restarted and that at least one source URL is reachable.
Table View Mode
Click a row to exit table view and edit.
Set the path where your .wav voice files live inside the container.
Map a host folder via docker-compose:
+ - /your/host/path:/voices:rw
+ or set VOICE_HOST_DIR=/your/host/path in the stack env.
Create your first voice from a recording or download ready-made voices.
+Compare the saved reference WAV with a fresh synthesis of the same reference text. Restart TTS first after editing a voice, otherwise the backend may still use a cached version.
+Preview first. Saving creates a new active WAV voice from the current reference text. Same-voice style only works when the selected backend knows this voice and honors instruct; CustomVoice is style-aware; Base/Streaming are fastest but often ignore style.
This permanently removes the selected voice${plural} from your library. This cannot be undone.
+No saved rehearsals yet.
Parse a script below or drop a file to start.
[tags] (e.g. [whisper], [excited], [laughing]). You can also type a custom tone like [professional broadcast tone] \u2014 S2 supports free-form descriptions. Fish-Speech S2 \u2197`),warn.hidden=!1;else if(!b.style_aware&&hasTone){const styleAware=all.find(x=>x.style_aware),suggest=styleAware?` Switch to ${escHtml(styleAware.label)} for reliable tone \u2014 but expect each voice to drift between lines.`:"";txtEl&&(txtEl.innerHTML=`${escHtml(b.label)} keeps each character\u2019s voice consistent but has weak tone control \u2014 tone picks may have little effect.${suggest}`),warn.hidden=!1}else if(b.style_aware&&!b.uses_wav){const wavBackend=all.find(x=>x.uses_wav),suggest=wavBackend?` Switch to ${escHtml(wavBackend.label)} to keep each character\u2019s voice identical throughout.`:"",qwenHint=/qwen|voice design|custom/i.test((b.id||"")+" "+(b.label||""))?" Qwen3TTS tone is sent as the per-line style/instruct text, so this is the right path for directed delivery.":"";txtEl&&(txtEl.innerHTML=`${escHtml(b.label)} gives strong tone but re-generates a fresh voice each line, so a character won\u2019t sound the same throughout.${qwenHint}${suggest}`),warn.hidden=!1}else warn.hidden=!0}(_Fa=$("reh-tone-warn-close"))==null||_Fa.addEventListener("click",()=>{const w=$("reh-tone-warn");w&&(w.hidden=!0)}),(_Ga=$("reh-tb-play"))==null||_Ga.addEventListener("click",()=>{rehState.playing?pausePlay():startPlay()}),(_Ha=$("reh-tb-stop"))==null||_Ha.addEventListener("click",()=>{var _a2;stopPlay(),rehState.lineIndex=(_a2=rehState.practiceStart)!=null?_a2:0,highlightCurrentLine(),hideRecOverlay()}),(_Ia=$("reh-tb-prev"))==null||_Ia.addEventListener("click",()=>{stopPlay(),rehState.lineIndex=Math.max(0,rehState.lineIndex-1),highlightCurrentLine(),hideRecOverlay()}),(_Ja=$("reh-tb-next"))==null||_Ja.addEventListener("click",()=>{stopPlay(),rehState.lineIndex=Math.min(rehState.lines.length-1,rehState.lineIndex+1),highlightCurrentLine(),hideRecOverlay()}),(_Ka=$("reh-tb-repeat"))==null||_Ka.addEventListener("click",()=>{var _a2;rehState.repeat=!rehState.repeat,(_a2=$("reh-tb-repeat"))==null||_a2.classList.toggle("reh-btn-active",rehState.repeat)}),(_La=$("reh-skip-desc-toggle"))==null||_La.addEventListener("change",function(){rehState.skipDescriptions=this.checked}),(_Ma=$("reh-edit-script-btn"))==null||_Ma.addEventListener("click",openScriptEditorModal),(_Na=$("reh-fountain-btn"))==null||_Na.addEventListener("click",exportFountain),(_Oa=$("reh-fountain-export-p4"))==null||_Oa.addEventListener("click",exportFountain),(_Pa=$("reh-fdx-export-btn"))==null||_Pa.addEventListener("click",exportFDX),(_Qa=$("reh-osf-export-btn"))==null||_Qa.addEventListener("click",exportOSF),(_Ra=$("reh-exit-btn"))==null||_Ra.addEventListener("click",()=>{stopPlay(),stopRehMic(),rehState.clips.length?(renderSummary(),showPhase(4)):showPhase(2)}),(_Sa=$("reh-page-title"))==null||_Sa.addEventListener("dblclick",function(){this.contentEditable="true",this.style.outline="2px solid var(--accent)",this.style.borderRadius="3px",this.focus();const range=document.createRange();range.selectNodeContents(this),window.getSelection().removeAllRanges(),window.getSelection().addRange(range)}),(_Ta=$("reh-page-title"))==null||_Ta.addEventListener("blur",function(){if(this.contentEditable==="true"){this.contentEditable="false",this.style.outline="";const v=this.textContent.trim()||"Script";this.textContent=v,$("reh-script-title")&&($("reh-script-title").value=v)}}),(_Ua=$("reh-page-title"))==null||_Ua.addEventListener("keydown",function(e){var _a2;e.key==="Enter"&&(e.preventDefault(),this.blur()),e.key==="Escape"&&(this.textContent=((_a2=$("reh-script-title"))==null?void 0:_a2.value)||"Script",this.blur())});let _rehPlayCtx=null;const rehDecodedBuffers=new Map;let rehCurrentSource=null,rehWordHighlightRaf=null;function rehPlayCtx(){return _rehPlayCtx||(_rehPlayCtx=new(window.AudioContext||window.webkitAudioContext)),_rehPlayCtx.state==="suspended"&&_rehPlayCtx.resume().catch(()=>{}),_rehPlayCtx}const REH_DECODE_WINDOW=8;function _rehEvictDecoded(keepIdx){if(rehDecodedBuffers.size<=REH_DECODE_WINDOW*2+4)return;const lo=keepIdx-REH_DECODE_WINDOW,hi=keepIdx+REH_DECODE_WINDOW;for(const k of rehDecodedBuffers.keys())(kNo clips in this session.
';return}list.innerHTML=rehState.clips.map((clip,idx)=>{const line=rehState.lines[clip.lineIndex]||{text:"\u2014",speaker:clip.speaker},c=rehState.cast[clip.speaker]||{color:"#89b4fa"},typeLabel=clip.type==="me"?"\u{1F3A4} Recorded":clip.type==="tts"?"\u{1F50A} Synthesized":"\u23ED Skipped",audioHtml=clip.blob?``:"",dlHtml=clip.blob?``:"";return`Open a document in the Reader tab and click "Cast as audiobook" to start extracting characters.
This stage will show the live passage-by-passage extraction view, prompt editor, and generated sheets once you start a cast.
${escHtml(text)}
+ ${_clRecords.length?"No characters match your filter.":"No characters yet."}
+Run Character sheets from Read Aloud or the Script Rehearser to populate your library.
+No books yet.
Open Read Aloud, import a PDF or text, and save it to your library.
No theater plays yet.
Open Script Rehearsal \u2192 Import / Export to add a script, or cast a book as an audiobook.
No characters yet.
Open a book in Read Aloud, cast it as an audiobook, then click Cast Characters to generate character sheets \u2014 or import an existing cast from SillyTavern.
| Name | \u26A5 | Zeilen | Sprache | Gut/B\xF6se | Stimme | Tags | ST | TTS | Bild | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
'+avatarInner+' | '+tierBadge+escHtml(rec.name)+" | "+(genderIcon?'':'\u2014')+" | "+(sh.line_count!=null?sh.line_count:'\u2014')+" | "+(voiceLang?escHtml(voiceLang):'\u2014')+" | "+(pct!=null?'':'\u2014')+' | '+(voiceId?escHtml(voiceId):'Keine Stimme')+' | "+promptCell("silly_tavern_prompt","SillyTavern")+" | "+promptCell("voice_design_prompt","TTS Voice")+" | "+promptCell("image_prompt","Bild")+' |