Zum Hauptinhalt springen

Entwicklungsleitfaden für VOICEVOX-Engine-API-Integrationsanwendungen

VOICEVOX Song
Implementierungsnotizen für die Erstellung von Client-Anwendungen, die die VOICEVOX-Engine-API aufrufen. Basierend auf Implementierungserfahrungen mit einer Song-App für macOS werden Punkte zusammengestellt, um die Engine-API stabil aus externen Anwendungen heraus zu nutzen – ohne die interne Struktur des offiziellen VOICEVOX-Editors unverändert zu übernehmen. Damit die Inhalte auch außerhalb von Xcode/Swift nutzbar sind, wird zunächst eine allgemeine Vorgehensweise beschrieben; am Ende jedes Abschnitts folgen Xcode-spezifische Ergänzungen.
Diese Seite basiert auf dem Originalguide und dokumentiert dessen Inhalte vollständig.

1. Zuerst die Engine per URL registrieren

VOICEVOX Song
Der offizielle VOICEVOX-Editor startet die mitgelieferte oder angegebene Engine-Ausführungsdatei selbst und verwaltet sie basierend auf den Engine-Einstellungen und engine_manifest.json. In einer externen Client-Anwendung ist es dagegen einfacher, zunächst „die URL eines bereits laufenden HTTP-API-Servers” zu registrieren. Typische URLs sind:
EngineBeispiel
Offizielle VOICEVOX-Enginehttp://127.0.0.1:50021
Laravel-Version der Enginehttp://127.0.0.1:50513
Die offizielle Engine hat eine automatische Port-Anpassung: Ist 50021 belegt, wird sie auf dem nächsten freien Port ab 50022 gestartet. Bei der Laravel-Version wird sie ebenfalls ab 8001 gestartet, wenn sie ohne --port=50513 mit dem Standard-Port 8000 gestartet wird; wird der Port jedoch explizit angegeben, ist die Anpassung deaktiviert. Speichern Sie bei der Registrierung nicht nur den URL-String, sondern rufen Sie als Verbindungstest mindestens die folgenden APIs auf und speichern Sie die abgerufenen Metainformationen mit.
APIZweck
GET /engine_manifestAbrufen von uuid, Engine-Name, frame_rate, Standard-Sampling-Rate usw.
GET /versionAbrufen der Version zur Anzeige und Kompatibilitätsprüfung
GET /singersAbrufen der Liste der Gesangsstile
GET /singer_info?speaker_uuid=...&resource_format=...Abrufen von Icons und Zusatzinformationen
Wichtig: In Projekten und Tracks nicht die URL, sondern engineId und styleId speichern. Die URL ändert sich je nach Benutzerumgebung, aber engineId entspricht der uuid aus /engine_manifest und lässt sich gut mit singer.engineId in .vvproj abgleichen. Xcode-Ergänzung: In der Song-App hält RegisteredEngine die Werte baseURL, engineId, name, frameRate, defaultSamplingRate, version, singers und speichert sie in UserDefaults. Wenn Sie den Trailing Slash, Query und Fragment der URL vor der Nutzung normalisieren, können Sie http://127.0.0.1:50021/ und http://127.0.0.1:50021 gleich behandeln. Wenn Sie in einer macOS-App HTTP-Verbindungen zu lokalen Diensten verwenden, prüfen Sie auch die Einstellungen für Sandbox und App Transport Security.

2. Nicht bis zum „Engine-Start” wie in der offiziellen App reingehen

Wenn Sie in einer externen App von Anfang an dieselbe Engine-Start-Verwaltung wie im offiziellen Editor implementieren möchten, müssen Sie OS-spezifische Prozessstarts, mitgelieferte Binaries, Port-Konflikte, Updates und Logmanagement handhaben. Besonders bei Nicht-Offiziellen-Engine-Implementierungen passt das Modell „Pfad zur Engine-Ausführungsdatei angeben und starten” schlecht. Legen Sie zunächst folgendes Vorgehen fest, das ist sicherer.
  1. Der Benutzer startet die Engine zuerst.
  2. In der App gibt der Benutzer die URL ein.
  3. Die App holt die Metainformationen und registriert sie.
  4. Beim Rendern wird aus der registrierten engineId die URL aufgelöst.
Mit diesem Vorgehen können Sie die offizielle Engine, die Laravel-Version, Docker-Engines und Engines auf anderen Hosts einheitlich abstrahieren. Wenn keine Verbindung möglich ist, zeigen Sie die nächste Aktion für den Benutzer klar an, z. B. „Bitte starten Sie die Engine und verbinden Sie sich erneut”. Xcode-Ergänzung: In SwiftUI lohnt es sich, den Bildschirm „Engine-Verbindung” eigenständig zu halten, mit einem TextField für die URL, Registrieren / Neu verbinden-Buttons, Preset-Buttons für offizielle/Laravel-Version und einer Liste registrierter Engines. Während einer asynchronen Verbindung ein ProgressView anzeigen; bei Verbindungsfehlern empfiehlt sich, den LocalizedError-Text nicht direkt zu zeigen, sondern eine benutzerfreundliche Erklärung voranzustellen.

3. Sänger und Style erst nach der Engine-Registrierung auswählen lassen

In der VOICEVOX-API wird die letztlich verwendete Stimme durch die Style-ID im Query-Parameter speaker bestimmt. Da Songs pro Track einen Sänger-Style haben, wird in der UI aus der registrierten Engine die Liste der singers aufgeklappt und Sängername, Style-Name sowie Style-ID als Auswahl angeboten. Zu speichern sind nur die folgenden zwei Werte.
{
  "singer": {
    "engineId": "engine-uuid",
    "styleId": 6000
  }
}
Mit dieser Struktur passt es gut zu .vvproj-kompatiblen Song-Daten und ist für Multi-Engine leicht erweiterbar. Wenn beim Öffnen des Projekts die engineId nicht registriert ist, zeigen Sie sie als „Nicht registrierte Engine” an und fordern Sie den Benutzer zur URL-Registrierung auf. Xcode-Ergänzung: In Swift lässt man Singer(engineId: String, styleId: Int) an einem Track hängen und flach für die Anzeige aus RegisteredEngine.singers in ein ViewModel-Array wie SingerStyleOption verwandeln. Wenn Sie die Sängerzuordnung ändern, invalidieren Sie den bereits gerenderten Ton.

4. Für .vvproj-Kompatibilität ein tick-basiertes Song-Modell verwenden

Die Song-Daten des VOICEVOX-Editors halten Noten und Tempi tick-basiert. Die Engine-API selbst benötigt letztlich Frame-Längen, doch das Bearbeitungsmodell in der App sollte tick-basiert sein – das eignet sich besser für Piano-Roll, Tempoänderungen, Taktarten, Undo/Redo und .vvproj-Lesen/-Schreiben. Die Minimalstruktur lautet:
ElementWichtige Felder
Songtpqn, tempos, timeSignatures, tracks, trackOrder
Trackname, singer, notes, gain, pan, solo, mute
Noteid, position, duration, noteNumber, lyric
Tempoposition, bpm
TimeSignaturemeasureNumber, beats, beatType
Nur beim Rendern wandeln Sie mit der Tempo-Map die Ticks in Sekunden um und berechnen die frame_length passend zur frame_rate der Engine. Xcode-Ergänzung: Wenn Sie mit Codable .vvproj-artiges JSON lesen/schreiben, halten Sie tracks als Dictionary mit ID als Key und trennen Sie trackOrder als Anzeige-Reihenfolgen-Array. Prüfen Sie vor dem Speichern trackOrder auf Duplikate, nicht existierende IDs und nicht sortierte Tracks – so vermeiden Sie UI-Fehler nach dem Laden.

5. Song-Synthese als vierstufige API implementieren

Das Song-Rendering hat mehr Schritte als bei Talk mit /audio_query/synthesis. Die grundlegende Pipeline hat vier Stufen. Bei /sing_frame_audio_query, /sing_frame_f0, /sing_frame_volume wird die Style-ID 6000 des Singing Teacher verwendet; nur bei /frame_synthesis am Ende die tatsächlich gewählte styleId des Sängers. Dieser Wert kann sowohl in der offiziellen als auch in der Laravel-Version der Engine gleich behandelt werden. Wichtig ist, bei den Requests jeder API die Entsprechung zwischen Score und FrameAudioQuery zu wahren. Score.notes[].frame_length wird aus der frame_rate der Engine berechnet; Stille wird als key: null, lyric: "" dargestellt. Xcode-Ergänzung: In Swift ist eine Protocol-Definition wie VoicevoxEngineSongAPI mit einer URLSession-Client-Implementierung testfreundlich. Bei JSON-Endpunkten setzen Sie Content-Type / Accept auf application/json; die Response von /frame_synthesis behandeln Sie als WAV-Binary Data. Bei HTTP-Status außerhalb von 2xx erzeugen Sie einen Fehler, der Statuscode und Response-Body enthält.

6. Pro Phrase aufteilen und cachen

Wenn Sie den gesamten Song jedes Mal in einem Rutsch synthetisieren, führt schon eine kleine Änderung zur Neuerzeugung aller Tracks – die UI wird träge. Wie im offiziellen Editor empfiehlt sich ein Design, das aufeinanderfolgende Noten als Phrasen gruppiert und an Pausen aufteilt. Cachen Sie pro Phrase die folgenden Stufen.
CacheBestandteile des Inputs
AudioQueryEngine-ID, Frame Rate, Tempo, Score, Tonlagenanpassung
F0AudioQuery, Pitch-Edits, Phrasenposition
VolumeAudioQuery, F0, Werte, die die Volume-Erzeugung beeinflussen
Voice/WAVSynthese-Query, endgültige Style-ID
In der ersten Implementierung können Sie den gerenderten Ton des betroffenen Tracks bei jeder Bearbeitung komplett invalidieren. Erzeugen Sie den Cache-Key aus einem Input-Hash, damit Sie später eine feinere Invalidierung pro Phrase ergänzen können. Xcode-Ergänzung: Wenn Sie den Rendering-Cache in einem actor halten, ist die Zustandsverwaltung unter Swift Concurrency einfacher. Beim Start eines neuen Renderings erhöhen Sie eine Generationsnummer und übernehmen alte API-Responses nicht. Die Kombination aus Task.checkCancellation() und Generationsprüfung deckt sowohl Abbruch als auch erneutes Rendern ab.

7. Frame-Rate und Pausen aus den Engine-Metainformationen bestimmen

ScoreNote.frame_length wird aus Sekunden und frame_rate berechnet. frame_rate nicht als festen Wert im Code hinterlegen, sondern bei der Registrierung aus /engine_manifest abrufen und speichern. Bei der Song-Synthese sollten am Anfang und Ende der Phrase Pausen eingefügt werden. Wenn die Anfangspause zu kurz ist, kann die Phonemerzeugung instabil werden – in der Implementierung sollten Sie die „tatsächliche Pause”, die „Viertelnote” und den „aus einer Mindestdauer zurückgerechneten Tick” abwägen. Auch am Ende eine kurze Stille einfügen und bei Bedarf den letzten Stille-Abschnitt ausblenden. Außerdem die Frame-Länge so korrigieren, dass sie durch Rundung nicht ≤ 0 wird. Bei kurzen Noten oder direkt nach Tempowechseln entstehen leicht 0-Frame-Werte durch Rundungsfehler. Xcode-Ergänzung: Definieren Sie tickToSecond / secondToTick als reine Funktionen und testen Sie sie. So können Sie in Piano-Roll, Playhead, Rendering und Videoausgabe dieselbe Umrechnung nutzen. Für das Ergebnis von frameLength = Int(round(seconds * frameRate)) sollten Sie sicherstellen, dass mindestens 1 Frame vorhanden ist – etwa durch Weiterreichen des Differenzbetrags an die Nachbarnote.

8. Bei Parameter-Edits unterscheiden, wo „vor der Erzeugung” und wo „vor der Synthese” gewirkt wird

Im Song wirken sich Bearbeitungen von Tonlage, Lautstärke, Pitch, Volume und Phonem-Timing jeweils auf unterschiedliche Stufen aus.
BearbeitungWirkungszeitpunkt
keyRangeAdjustmentKey bei der Score-Erzeugung, Pitch-Shift nach der F0-Erzeugung
volumeRangeAdjustmentVolume-Array vor /frame_synthesis
Pitch-BearbeitungF0-Array nach /sing_frame_f0
Volume-BearbeitungVolume-Array nach /sing_frame_volume
Phonem-Timing-BearbeitungFrameAudioQuery.phonemes
Wenn Sie früh festlegen, welche Bearbeitung welchen Cache invalidiert, bricht das Rendering-Ergebnis auch beim späteren Hinzufügen von UI-Editierfunktionen nicht zusammen. Xcode-Ergänzung: Verteilen Sie die Anwendung der Parameter-Edits nicht über das ViewModel, sondern lagern Sie sie in reine Logik wie SongParameterEditApplicator aus – das ist testfreundlicher. Zugriffe außerhalb des Array-Bereichs oder negative Lautstärken sollten explizit korrigiert oder als Fehler behandelt werden.

9. Für Wiedergabe, Mix und Export dieselbe Track-Bewertung verwenden

Nach dem Rendering wird die WAV pro Phrase auf der Timeline platziert und wiedergegeben. Bei Multi-Track-Unterstützung sollten die Bewertungen für solo/mute/gain/pan bei normaler Wiedergabe, WAV-Gesamtexport und Stem-Export nicht abweichen – sonst ist das Verhalten für Benutzer schwer vorhersehbar. Als gemeinsame Regel gilt: Sobald es mindestens einen Solo-Track gibt, werden nur Solo-Tracks ausgegeben; ohne Solo werden alle nicht gemuteten Tracks ausgegeben. Beim Stem-Export geben Sie den Ziel-Track eigenständig aus, unabhängig vom normalen Solo/Mute-Zustand. Xcode-Ergänzung: Auf macOS können Sie mit AVAudioEngine, AVAudioPlayerNode, AVAudioMixerNode einen Wiedergabe-Graph aufbauen. Beim Export bieten Sie einen separaten Pfad für Offline-Rendering zusätzlich zur normalen Wiedergabe und vereinheitlichen die WAV-Kodierung. Wird während der Wiedergabe editiert und der gerenderte Ton dadurch invalidiert, stoppen Sie die Wiedergabe und zeigen den Zustand deutlich an.

10. Die UI unterscheidet „nicht registriert”, „nicht zugewiesen” und „nicht gerendert”

In VOICEVOX-Engine-API-Integrationsanwendungen gibt es mehrere Fehlerursachen.
ZustandAnzuzeigender Hinweis
Engine nicht registriertAuf dem Engine-Verbindungsbildschirm URL registrieren
Engine nicht gestartetEngine starten und neu verbinden
engineId des Projekts nicht registriertDie entsprechende Engine-URL zusätzlich registrieren
Kein Sänger dem Track zugewiesenRegistrierten Sänger-Style auswählen
Ton nach Bearbeitung veraltetNeurendering erforderlich
Wird gerade gerendertAbbruch ermöglichen
Fasst man all das zu „Wiedergabe nicht möglich” zusammen, ist die Ursache nicht ersichtlich. Zustände unterteilen und die nächste Aktion in der UI anzeigen. Xcode-Ergänzung: In SwiftUI-ViewModels bewährt sich renderingState als Enum mit idle, rendering, rendered, stale, failed, um Button-Disabling und Statusanzeige konsistent zu halten. Fehlermeldungen sollten Sie nicht nur in einer Statusleiste, sondern auch als Alert ausgeben können.

11. Bei Multi-Engine unterschiedliche Lebensdauern von „URL” und „engineId”

Bei Multi-Engine-Unterstützung führt schnell zu Verwirrung, dass URL und engineId unterschiedliche Rollen haben.
WertLebensdauerVerwendung
URLÄndert sich je BenutzerumgebungAls API-Ziel
engineIdKennzeichner der Engine-Implementierung/-ModelleAls Referenz im Projekt
styleIdStyle-Kennzeichner innerhalb der EngineAls speaker-Parameter
Betten Sie in der Projektdatei nicht die URL ein, sondern speichern Sie engineId und styleId. Halten Sie in den App-Einstellungen die Zuordnung engineId -> URL. So können auch Projekte, die Sie von anderen erhalten, in Ihrer Umgebung gerendert werden, sobald Sie dieselbe Engine per URL registrieren. Xcode-Ergänzung: Lösen Sie vor dem Rendering die engineId in RegisteredEngine auf, z. B. Dictionary(uniqueKeysWithValues: registeredEngines.map { ($0.engineID, $0) }). Wird sie nicht gefunden, senden Sie keinen HTTP-Request, sondern lassen Sie vorher mit „Die Engine des Sängers ist nicht registriert” fehlschlagen.

12. Tests: HTTP-Client, Modellumwandlung und Rendering-Plan trennen

Da es aufwendig ist, für Tests immer den VOICEVOX-Engine-Kern zu starten, sollten Sie in üblichen automatisierten Tests HTTP mocken und die Request-Erzeugung sowie Zustandsübergänge der App prüfen. Mit Priorität testenswert sind:
GegenstandZu prüfen
URL-NormalisierungTrailing Slash und URLs mit Pfad werden erwartungsgemäß behandelt
Engine-Registrierung/engine_manifest, /version, /singers, /singer_info aufrufen und das Speichermodell erzeugen können
.vvproj-I/OKonsistenz von trackOrder und tracks, Umgang mit alten/zukünftigen Versionen
Tick/Sekunden-UmrechnungUmrechnung über Tempoänderungen hinweg ist reversibel
Score-ErzeugungPausen, frame_length, Standardtexte, Tonlagenanpassung
Rendering-PlanNicht registrierte Engines, nicht gesetzte Sänger und engineId-Mismatches vor dem API-Aufruf erkennen
CacheWiederverwendung bei gleichem Input, Invalidierung nach Bearbeitung
Xcode-Ergänzung: Mit einer URLSessionConfiguration.ephemeral, in der URLProtocol ersetzt ist, können Sie den URLSession-Client ohne echten HTTP-Server testen. Injizieren Sie die API im Renderer über ein Protocol; für den Cache-Zustand des actor stellen Sie eine Methode bereit, die einen Snapshot zurückgibt – dann lässt sich das leichter verifizieren.

Empfohlene Implementierungsreihenfolge

Wenn Sie mit einer Minimalkonfiguration starten, empfiehlt sich folgende Reihenfolge.
1

Engine-URL registrieren und Metainformationen speichern

Registrieren Sie die URL und speichern Sie sie zusammen mit den aus /engine_manifest oder /version gewonnenen Metainformationen.
2

Sänger-Style-Liste und Zuweisung

Zeigen Sie die Sänger-Style-Liste an und weisen Sie den Tracks engineId + styleId zu.
3

Tick-basiertes Modell

Festigen Sie zuerst das tick-basierte Song/Track/Note-Modell.
4

Vierstufiger API-Client

Implementieren Sie die Score-Erzeugung und den vierstufigen Song-API-Client.
5

Phrasen-basiertes Rendering

Führen Sie phrasenweises Rendering und Caching ein.
6

Wiedergabefunktion

Ermöglichen Sie die Wiedergabe der gerenderten WAV auf der Timeline.
7

Multi-Track-Bewertung

Vereinheitlichen Sie die Bewertung von solo/mute/gain/pan bei Multi-Track.
8

Export

Fügen Sie WAV-Export und Stem-Export hinzu.
9

Editierfunktionen

Fügen Sie Pitch-, Volume- und Phonem-Timing-Bearbeitung hinzu.
10

Erweiterte Funktionen

Gehen Sie zu erweiterten Funktionen wie .vvproj-kompatiblem Lesen/Schreiben und Videoausgabe über.
Statt gleich alle Funktionen des offiziellen Editors nachzubauen, ist es einfacher, zuerst URL-Registrierung, Sängerzuweisung, vierstufige API und den Rerendering-Zustand zu festigen – dann können Sie sich sowohl an der offiziellen als auch an der Laravel-Version orientieren.
Zuletzt geändert am 13. Juli 2026