Markdown Read Aloud
Eine VS Code Extension, die Markdown-Dateien laut vorliest – ohne die Markdown-Syntax mitzusprechen. Piper ist der schnelle Modus, Supertonic 3 der Qualitätsmodus. Pocket TTS kann als deutsche Beta zugeschaltet werden; außerdem lassen sich externe lokale, OpenAI-kompatible TTS-Server anbinden. Ohne eine bewusst konfigurierte Server-URL werden keine Texte an einen Dienst gesendet.
Überblick
Die Extension bereinigt den Text der aktiven Datei (Überschriften, Listen, Links, Betonungen, Tabellen; Bilder werden durch ihren Alt-Text ersetzt, Code-Blöcke standardmäßig übersprungen), zerlegt ihn in Sätze und lässt die gewählte Engine daraus Audio erzeugen. Während ein Abschnitt läuft, werden die nächsten bereits vorbereitet.
Zwei Dinge machen das Ganze schnell:
- Ein Engine-Prozess bleibt offen und bekommt die Sätze der Reihe nach. Das Stimmmodell wird dadurch einmal geladen statt bei jedem Satz erneut.
- Fertige Audiodaten landen im Cache unter
os.tmpdir()/markdown-read-aloud-cache, adressiert über einen Hash aus Stimme, Tempo und Satztext. Der Cache wird nach Alter und Gesamtgröße automatisch begrenzt.
Sprachausgaben
Welche Engine läuft, entscheidet allein die gewählte Stimmen-ID – es gibt keine separate Einstellung dafür.
|
Piper – schnell |
Supertonic 3 – Qualität |
Pocket TTS – Beta |
| Stimmen-ID |
z. B. de_DE-thorsten-high |
supertonic-de-f1 … supertonic-de-m5 |
pocket-de-juergen |
| Klang |
solide, hörbar synthetisch |
deutlich natürlicher, 44,1 kHz statt 22,05 kHz |
experimenteller deutscher Langtextmodus |
| Standard |
schnelle Wiedergabe |
8 Schritte; Export wahlweise 8 oder 12 |
german_24l, Stimme Jürgen |
| Download |
~25 MB Programm + 60–110 MB pro Stimme |
einmalig ~180 MB für alle Sprachen und Stimmen |
mehrere GB; erst nach ausdrücklicher Zustimmung |
| Prozess |
persistent |
persistent |
persistenter lokaler FastAPI-Prozess |
| Fehlerfall |
Einzelprozess-Fallback |
Prozessneustart |
automatisch Supertonic, danach Piper |
Wer wenig CPU abgeben will, bleibt bei Piper oder nimmt de_DE-thorsten-medium statt -high. Supertonic nutzt standardmäßig 8 Schritte; der Cache und die Vorausberechnung halten die Wiedergabe trotzdem flüssig.
Gemessener Supertonic-Schrittvergleich
Der mitgelieferte deutsche 20-Satz-Korpus wurde auf einem AMD Ryzen 5 5500U unter Windows mit zwei ONNX-Threads und demselben persistenten Prozess gemessen. Die erzeugte Audiodauer betrug je Lauf 185,632 Sekunden:
| Schritte |
Synthesezeit |
Echtzeitfaktor |
| 4 |
52,992 s |
0,2855 |
| 8 |
103,466 s |
0,5574 |
| 12 |
123,995 s |
0,6680 |
8 Schritte kosten hier knapp doppelt so viel Rechenzeit wie 4, bleiben aber schneller als die Wiedergabe. 12 Schritte sind deshalb für den Offline-Export vorgesehen.
Pocket TTS Beta
Beim ersten Auswählen von „Jürgen · beta“ erklärt ein modaler Dialog den mehrere GB großen Download. Erst nach Zustimmung startet uvx das gepinnte Paket pocket-tts==2.1.0, richtet Python und Abhängigkeiten ein und lädt german_24l. Fehlt uvx, lädt die Extension nach derselben Zustimmung eine gepinnte, prüfsummenverifizierte uv-Laufzeit (~20 MB). Kyutais Modellbedingungen müssen zuvor akzeptiert sein; für das zugangsbeschränkte Modell kann HF_TOKEN nötig sein. Ohne Zustimmung wird nichts geladen.
Die Beta hält Modell und Stimme in einem lokalen Prozess auf 127.0.0.1. Scheitert dieser nach erfolgreicher Einrichtung, nutzt normales Vorlesen automatisch eine installierte deutsche Supertonic-Stimme und danach Piper. Im Vergleichstest ist dieser Fallback absichtlich deaktiviert.
Externe lokale Server
Mit externalServerUrl kann ein OpenAI-kompatibler lokaler TTS-Server über POST /v1/audio/speech angeschlossen werden. Damit lassen sich beispielsweise separat installierte GPU-Server für Qwen3-TTS oder Chatterbox verwenden, ohne deren Modelle mit der Extension auszuliefern.
Was Supertonic braucht
Beim ersten Auswählen einer Supertonic-Stimme werden nachgeladen:
- das Modell-Bundle von Hugging Face (~140 MB, MIT-Lizenz),
- die sherpa-onnx-Runtime aus der npm-Registry (~10 MB),
- eine Node-Laufzeit, falls kein
node (≥ 18) im PATH liegt (~30 MB).
Alle drei sind auf feste Versionen gepinnt und werden gegen Prüfsummen verifiziert; sie liegen im globalen Storage der Extension. Punkt 3 ist keine Bequemlichkeit: Electron verbietet die externen N-API-Buffer, über die sherpa-onnx seine Audiodaten zurückgibt („External buffers are not allowed"), deshalb kann der Extension-Host das Addon nicht selbst laden. Die Synthese läuft in einem eigenen Node-Prozess – siehe src/nodeRuntime.ts.
Wird die letzte Supertonic-Stimme über „Installierte Stimmen verwalten" entfernt, verschwinden Modell, Runtime und eine selbst geladene Node-Laufzeit wieder.
Funktionen
- Vorlesen – ganzes Dokument, ab Cursor oder nur die Auswahl
- Echtes Pause/Fortsetzen sowie satzweise Navigation
- Hervorhebung des gerade gelesenen Satzes im Editor, inklusive Mitscrollen
- Piper-Schnellmodus und Supertonic-Qualitätsmodus, Pocket TTS als deutsche Beta sowie ein Adapter für externe lokale Server
- Automatische Sprachwahl anhand von Frontmatter
lang: oder einer Wort-Heuristik – nur unter bereits installierten Stimmen
- Audio-Export als einzelne Hörbuch-Datei oder als Datei pro Abschnitt
- Leseposition pro Datei wird gemerkt
- Beliebige Dateitypen – Markdown, MDX, Quarto und R Markdown werden bereinigt, alles andere als Klartext gelesen
Bedienung
Die Activity-Bar-Ansicht Vorlesen enthält Transportleiste, Fortschritt, den aktuellen Satz sowie Stimme und Tempo. Zusätzlich stehen Befehle, Tastenkürzel und Menüeinträge bereit.
| Befehl |
ID |
Tastenkürzel |
| Vorlesen |
markdown-read-aloud.startReadAloud |
|
| Wiedergabe / Pause |
markdown-read-aloud.togglePlayPause |
Ctrl+Alt+R |
| Vorlesen beenden |
markdown-read-aloud.stopReadAloud |
Ctrl+Alt+Shift+R |
| Ab Cursor vorlesen |
markdown-read-aloud.readFromCursor |
|
| Auswahl vorlesen |
markdown-read-aloud.readSelection |
|
| Nächster Satz |
markdown-read-aloud.nextSection |
Ctrl+Alt+. |
| Vorheriger Satz |
markdown-read-aloud.prevSection |
Ctrl+Alt+, |
| Dokument als Audio exportieren |
markdown-read-aloud.compileToAudio |
|
| Deutschen TTS-Vergleich ausführen |
markdown-read-aloud.runGermanTtsComparison |
|
| Stimme auswählen |
markdown-read-aloud.selectVoice |
|
| Installierte Stimmen verwalten |
markdown-read-aloud.manageVoices |
|
| Lesegeschwindigkeit ändern |
markdown-read-aloud.changeSpeed |
|
| Audio-Cache leeren |
markdown-read-aloud.clearCache |
|
| Protokoll anzeigen |
markdown-read-aloud.showLog |
|
Während der Wiedergabe zeigt die Status-Bar links 12/148 an und öffnet per Klick das Panel.
Einstellungen
Alle Einstellungen liegen unter markdown-read-aloud.*.
| Einstellung |
Standard |
Bedeutung |
voice |
de_DE-thorsten-high |
Stimmen-ID; bestimmt auch die Engine |
speed |
1.0 |
Faktor – größer = schneller |
autoDetectLanguage |
true |
Dokumentsprache erkennen und passende installierte Stimme wählen |
playbackEngine |
auto |
auto, webview (Panel) oder system |
persistentProcess |
true |
Piper-Prozess offenhalten |
supertonicSteps |
8 |
Supertonic-Qualität beim Vorlesen |
supertonicExportSteps |
12 |
Supertonic-Exportqualität: 8 oder 12 Schritte |
supertonicThreads |
2 |
Nur Supertonic: CPU-Threads; über 2 bringt kaum noch etwas |
pocketTtsCommand / pocketTtsPort |
uvx / 8765 |
Startbefehl und Loopback-Port der Pocket-TTS-Beta |
externalServerUrl |
leer |
Basis-URL eines optionalen externen lokalen TTS-Servers |
externalServerProtocol |
openai |
openai (/v1/audio/speech) oder pocket (/tts) |
externalServerVoice / externalServerModel |
default / tts-1 |
Parameter für den lokalen Server |
prefetchCount |
2 |
Wie viele Abschnitte im Voraus erzeugt werden |
highlightCurrentSentence |
true |
Aktuellen Satz im Editor markieren |
rememberPosition |
true |
Leseposition pro Datei merken |
readCodeBlocks |
false |
Inhalt von Code-Blöcken mitlesen |
chunkTargetChars / chunkMaxChars |
180 / 300 |
Länge der gesprochenen Abschnitte |
exportDirectory |
audio_output |
Zielordner des Exports |
exportSingleFile |
true |
Zu einer WAV-Datei zusammenfügen |
exportSilenceMs |
350 |
Pause zwischen den Sätzen beim Zusammenfügen |
exportConcurrency |
0 |
Parallele Piper-Prozesse (0 = automatisch) |
cacheMaxAgeDays / cacheMaxSizeMb |
30 / 512 |
Grenzen des Audio-Caches |
Die frühere Einstellung language wird beim ersten Start automatisch in voice überführt. Ebenso wird speed umgerechnet: bis 0.0.1 war der Wert Pipers length_scale (kleiner = schneller), jetzt ist es ein echter Geschwindigkeitsfaktor.
Projektstruktur
| Datei |
Aufgabe |
| src/extension.ts |
Aktivierung, Befehle, Status-Bar, Panel-Anbindung |
| src/readerSession.ts |
Zustandsmaschine des Vorlesens (Play/Pause, Navigation, Hervorhebung, Leseposition) |
| src/speechEngine.ts |
Gemeinsame Schnittstelle der Sprachausgaben |
| src/piperEngine.ts / src/piperBackend.ts |
Piper-Ausführung: persistenter Prozess mit Fallback auf Einzelprozesse |
| src/supertonicEngine.ts / src/supertonicWorker.ts |
Supertonic-Arbeitsprozess und dessen Ansteuerung |
| src/supertonicInstaller.ts / src/supertonicVoices.ts |
Installation von Runtime und Modell, Stimmenliste |
| src/pocketTtsBackend.ts |
Persistenter Pocket-TTS-Beta-Prozess, Zustimmung und Fallback |
| src/localTtsServer.ts / src/externalServerBackend.ts |
Allgemeiner lokaler HTTP-TTS-Adapter |
| src/ttsBenchmark.ts / src/germanBenchmarkSentences.ts |
Reproduzierbarer deutscher 20-Satz-Vergleich |
| src/nodeRuntime.ts |
Node-Binary für den Supertonic-Prozess finden oder nachladen |
| src/ttsService.ts |
Cache + Engines hinter einer Schnittstelle |
| src/audioCache.ts |
Atomare Cache-Schreibvorgänge, Validierung, LRU-Bereinigung |
| src/audioPlayer.ts |
Wiedergabe im Webview bzw. über den Systemplayer |
| src/textSegmenter.ts |
Blockerkennung, Satz-Splitting, Chunking, Quelltext-Offsets |
| src/markdownCleaner.ts |
Inline-Markdown entfernen, mit Offset-Mapping |
| src/piperDownloader.ts |
Installation von Binary und Stimmen |
| src/voiceCatalog.ts / src/voiceManager.ts |
Stimmen-Katalog und -Auswahl |
| src/net/download.ts / src/net/archive.ts |
Downloads mit Prüfsumme und Proxy, Entpacken mit Traversal-Schutz |
| src/wavUtils.ts |
WAV prüfen und zusammenfügen |
| src/exportAudio.ts |
Audio-Export |
| src/controlsView.ts + media/ |
Bedien-Panel |
Entwicklung
Voraussetzungen: Node.js 20+, VS Code ab 1.85.
npm install
npm run compile # Typprüfung, Ausgabe nach out/
npm run watch # im Watch-Modus
npm run lint
npm test # Unit-Tests (node:test)
npm run bundle # esbuild -> dist/extension.js
npm run package # VSIX bauen
F5 startet einen Extension Development Host. Die Extension hat keine Laufzeit-Abhängigkeiten; Downloads, Entpacken und die Markdown-Bereinigung sind selbst implementiert.
Standardmäßig spielt das Panel das Audio ab – ein Weg für alle Betriebssysteme. Ist das Panel nie geöffnet worden, springt der Systemplayer ein:
- Windows – PowerShell
System.Media.SoundPlayer
- macOS –
afplay
- Linux –
aplay, sonst paplay, play oder ffplay
Über den Systemplayer ist kein echtes Pausieren möglich; „Fortsetzen" beginnt dann am Anfang des aktuellen Satzes.
Piper wird passend zu Plattform und Architektur geladen (Windows amd64, macOS x64/aarch64, Linux x86_64/aarch64/armv7l) und auf ein festes Release gepinnt.
Supertonic gibt es für Windows x64, macOS x64/arm64 und Linux x64/arm64. Auf anderen Plattformen – etwa Linux armv7l oder Windows on ARM – erscheint beim Auswählen einer Supertonic-Stimme ein Hinweis, und Piper bleibt die Wahl.
Bekannte Einschränkungen
- Wird das Dokument während des Vorlesens bearbeitet, verschwindet die Satz-Hervorhebung; die Wiedergabe läuft weiter.
- Der Systemplayer kann nicht pausieren (siehe oben).
- Der Piper-Stimmen-Katalog wird von Hugging Face geladen. Ohne Netzverbindung stehen die drei eingebauten Piper-Stimmen zur Verfügung; die Supertonic-Stimmen brauchen keinen Katalog, wohl aber ihr Modell.
- Supertonic nutzt beim Export nur einen Prozess;
exportConcurrency wirkt dort nicht.
- Die Zuordnung der zehn Supertonic-Sprecher zu den Kürzeln
f1–m5 ist nicht dokumentiert, sondern über die Grundfrequenz gemessen (siehe Kommentar in src/supertonicVoices.ts).
Lizenz
MIT – siehe LICENSE.