Entwicklungsleitfaden für VOICEVOX-Engine-API-Integrationsanwendungen

Diese Seite basiert auf dem Originalguide und dokumentiert dessen Inhalte vollständig.
1. Zuerst die Engine per URL registrieren

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:
| Engine | Beispiel |
|---|---|
| Offizielle VOICEVOX-Engine | http://127.0.0.1:50021 |
| Laravel-Version der Engine | http://127.0.0.1:50513 |
--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.
| API | Zweck |
|---|---|
GET /engine_manifest | Abrufen von uuid, Engine-Name, frame_rate, Standard-Sampling-Rate usw. |
GET /version | Abrufen der Version zur Anzeige und Kompatibilitätsprüfung |
GET /singers | Abrufen der Liste der Gesangsstile |
GET /singer_info?speaker_uuid=...&resource_format=... | Abrufen von Icons und Zusatzinformationen |
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.- Der Benutzer startet die Engine zuerst.
- In der App gibt der Benutzer die URL ein.
- Die App holt die Metainformationen und registriert sie.
- Beim Rendern wird aus der registrierten
engineIddie URL aufgelöst.
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-Parameterspeaker 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.
.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:
| Element | Wichtige Felder |
|---|---|
| Song | tpqn, tempos, timeSignatures, tracks, trackOrder |
| Track | name, singer, notes, gain, pan, solo, mute |
| Note | id, position, duration, noteNumber, lyric |
| Tempo | position, bpm |
| TimeSignature | measureNumber, beats, beatType |
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.| Cache | Bestandteile des Inputs |
|---|---|
| AudioQuery | Engine-ID, Frame Rate, Tempo, Score, Tonlagenanpassung |
| F0 | AudioQuery, Pitch-Edits, Phrasenposition |
| Volume | AudioQuery, F0, Werte, die die Volume-Erzeugung beeinflussen |
| Voice/WAV | Synthese-Query, endgültige Style-ID |
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.| Bearbeitung | Wirkungszeitpunkt |
|---|---|
keyRangeAdjustment | Key bei der Score-Erzeugung, Pitch-Shift nach der F0-Erzeugung |
volumeRangeAdjustment | Volume-Array vor /frame_synthesis |
| Pitch-Bearbeitung | F0-Array nach /sing_frame_f0 |
| Volume-Bearbeitung | Volume-Array nach /sing_frame_volume |
| Phonem-Timing-Bearbeitung | FrameAudioQuery.phonemes |
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 mitAVAudioEngine, 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.| Zustand | Anzuzeigender Hinweis |
|---|---|
| Engine nicht registriert | Auf dem Engine-Verbindungsbildschirm URL registrieren |
| Engine nicht gestartet | Engine starten und neu verbinden |
engineId des Projekts nicht registriert | Die entsprechende Engine-URL zusätzlich registrieren |
| Kein Sänger dem Track zugewiesen | Registrierten Sänger-Style auswählen |
| Ton nach Bearbeitung veraltet | Neurendering erforderlich |
| Wird gerade gerendert | Abbruch ermöglichen |
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 undengineId unterschiedliche Rollen haben.
| Wert | Lebensdauer | Verwendung |
|---|---|---|
| URL | Ändert sich je Benutzerumgebung | Als API-Ziel |
engineId | Kennzeichner der Engine-Implementierung/-Modelle | Als Referenz im Projekt |
styleId | Style-Kennzeichner innerhalb der Engine | Als speaker-Parameter |
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:| Gegenstand | Zu prüfen |
|---|---|
| URL-Normalisierung | Trailing 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/O | Konsistenz von trackOrder und tracks, Umgang mit alten/zukünftigen Versionen |
| Tick/Sekunden-Umrechnung | Umrechnung über Tempoänderungen hinweg ist reversibel |
| Score-Erzeugung | Pausen, frame_length, Standardtexte, Tonlagenanpassung |
| Rendering-Plan | Nicht registrierte Engines, nicht gesetzte Sänger und engineId-Mismatches vor dem API-Aufruf erkennen |
| Cache | Wiederverwendung bei gleichem Input, Invalidierung nach Bearbeitung |
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.Engine-URL registrieren und Metainformationen speichern
Registrieren Sie die URL und speichern Sie sie zusammen mit den aus
/engine_manifest oder /version gewonnenen Metainformationen.Sänger-Style-Liste und Zuweisung
Zeigen Sie die Sänger-Style-Liste an und weisen Sie den Tracks
engineId + styleId zu.Vierstufiger API-Client
Implementieren Sie die
Score-Erzeugung und den vierstufigen Song-API-Client.