Passer au contenu principal

Objectif

Cette page clarifie les spécifications de Score et Note, souvent sources de confusion en synthèse Song. L’implémentation de référence est celle des sources de Revolution\Voicevox\Song\Note / Score / SongAudioQuery.

Classe Note

Note représente une seule note (ou un silence).
use Revolution\Voicevox\Song\Note;

$note = Note::make(
    length: 94,
    lyric: 'ド',
    key: 60,
    id: 'intro-1',
);

Arguments du constructeur

ArgumentTypeDescription
lengthintLongueur en frames. Valeur secondes × 93.75 convertie en entier
lyricstringParoles. Chaîne vide '' pour un silence
keyint|nullNuméro de note MIDI. null pour un silence
idstring|nullID auxiliaire copié dans note_id lors de la création du FrameAudioQuery

Calcul de la longueur en frames

length est en frames. Elle se calcule à partir des secondes ainsi :
length = secondes × 93.75
Cette valeur n’étant pas intuitive, si vous êtes habitué au MIDI, l’usage pratique consiste à utiliser Note::len().
Note::len(int $ticks, int $bpm = 125): int
  • Considère la noire comme valant 480 ticks
  • Par défaut, 125 BPM
  • Applique round() en interne pour retourner un entier : les erreurs d’arrondi s’accumulent sur les morceaux longs
Note::make(length: Note::len(480, 120), lyric: 'ド', key: 60);  // noire
Note::make(length: Note::len(960, 120), lyric: 'ミ', key: 64);  // blanche
Note::len() est pratique, mais faites attention à l’erreur cumulée sur les morceaux longs. Réajustez au besoin les longueurs par section.

Représentation d’un silence

Un silence s’exprime avec la combinaison suivante.
Note::make(length: 15, lyric: '', key: null);

Repères pour les numéros de note MIDI

NoteMIDI
C460
D462
E464
F465
G467
A469
B471
C572

Classe Score

Score est un objet partition qui contient un tableau de Note.
use Revolution\Voicevox\Song\Score;

$score = Score::make([
    Note::make(length: 15, lyric: '', key: null),
    Note::make(length: Note::len(480, 120), lyric: 'ド', key: 60),
]);

Règle importante

La première note doit obligatoirement être un silence.

Principales API

  • Création via Score::make(array $notes)
  • Ajout via add(Note $note)
  • Conversion en chaîne JSON via toJson()
$score = Score::make([
    Note::make(length: 15, lyric: '', key: null),
]);

$score->add(Note::make(length: Note::len(480, 120), lyric: 'ド', key: 60));

$json = $score->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
Le constructeur de Score inclut une implémentation qui absorbe les chaînes vides / null de lyric lors d’entrées en tableau, ce qui facilite aussi les entrées de formulaire passant par le middleware ConvertEmptyStringsToNull.

Pattern pratique : gestion via fichiers JSON (recommandé)

Dans un produit réel, plutôt que d’écrire la partition en dur dans le PHP, il est plus commode de la gérer en JSON.
  1. Stocker un JSON dans un format personnalisé
  2. Le lire et convertir en Note / Score
  3. Le passer à Voicevox::song() pour générer

Exemple de JSON

{
  "teacher": 6000,
  "speaker": 3001,
  "bpm": 120,
  "notes": [
    { "ticks": 120, "lyric": "", "key": null, "id": "r0" },
    { "ticks": 480, "lyric": "ド", "key": 60, "id": "n1" },
    { "ticks": 480, "lyric": "レ", "key": 62, "id": "n2" },
    { "ticks": 960, "lyric": "ミ", "key": 64, "id": "n3" },
    { "ticks": 120, "lyric": "", "key": null, "id": "r1" }
  ]
}

Lecture et synthèse

use Illuminate\Support\Facades\Storage;
use Revolution\Voicevox\Song\Note;
use Revolution\Voicevox\Song\Score;
use Revolution\Voicevox\Voicevox;

$chart = json_decode(
    Storage::disk('local')->get('songs/sample.json'),
    true,
    flags: JSON_THROW_ON_ERROR,
);

$score = Score::make(
    collect($chart['notes'])
        ->map(fn (array $note) => Note::make(
            length: Note::len(ticks: $note['ticks'], bpm: $chart['bpm']),
            lyric: $note['lyric'] ?? '',
            key: $note['key'],
            id: $note['id'] ?? null,
        ))
        ->all(),
);

$response = Voicevox::song($score, teacher: $chart['teacher'])
    ->generate(id: $chart['speaker']);

$response->storeAs('songs', 'sample.wav');

Pages associées

Dernière modification le 13 juillet 2026