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
| Argument | Type | Description |
|---|
length | int | Longueur en frames. Valeur secondes × 93.75 convertie en entier |
lyric | string | Paroles. Chaîne vide '' pour un silence |
key | int|null | Numéro de note MIDI. null pour un silence |
id | string|null | ID 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
| Note | MIDI |
|---|
| C4 | 60 |
| D4 | 62 |
| E4 | 64 |
| F4 | 65 |
| G4 | 67 |
| A4 | 69 |
| B4 | 71 |
| C5 | 72 |
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.
- Stocker un JSON dans un format personnalisé
- Le lire et convertir en
Note / Score
- 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