Obiettivo
Chiarire le specifiche di Score e Note, spesso motivo di confusione nella sintesi Song.
I riferimenti implementativi si trovano nel codice di Revolution\Voicevox\Song\Note / Score / SongAudioQuery.
Classe Note
Note rappresenta una singola nota (o una pausa).
use Revolution\Voicevox\Song\Note;
$note = Note::make(
length: 94,
lyric: 'ド',
key: 60,
id: 'intro-1',
);
Argomenti del costruttore
| Argomento | Tipo | Descrizione |
|---|
length | int | Lunghezza in frame. Valore ottenuto arrotondando a intero secondi × 93.75 |
lyric | string | Testo. Per la pausa stringa vuota '' |
key | int|null | Numero di nota MIDI. Per la pausa null |
id | string|null | ID ausiliario copiato in note_id quando si genera il FrameAudioQuery |
Calcolo della lunghezza in frame
length è in unità di frame. Da secondi si ricava con:
Poiché questo valore non è intuitivo, se sei abituato a MIDI è più pratico usare Note::len().
Note::len(int $ticks, int $bpm = 125): int
- Considera la semiminima come
480 ticks
125 BPM è il valore predefinito
- Internamente esegue
round() per arrotondare a intero, quindi in brani lunghi l’errore di arrotondamento si accumula gradualmente
Note::make(length: Note::len(480, 120), lyric: 'ド', key: 60); // semiminima
Note::make(length: Note::len(960, 120), lyric: 'ミ', key: 64); // minima
Note::len() è comodo, ma nei brani di lunga durata attenzione all’errore cumulativo. Se necessario riaggiusta le durate a livello di sezione.
Rappresentare una pausa
Una pausa è la seguente combinazione.
Note::make(length: 15, lyric: '', key: null);
Riferimento numeri di nota MIDI
| Nota | MIDI |
|---|
| C4 | 60 |
| D4 | 62 |
| E4 | 64 |
| F4 | 65 |
| G4 | 67 |
| A4 | 69 |
| B4 | 71 |
| C5 | 72 |
Classe Score
Score è l’oggetto partitura che contiene un array di 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),
]);
Regola importante
La prima nota deve sempre essere una pausa.
API principali
Score::make(array $notes) per creare
add(Note $note) per aggiungere
toJson() per convertire in stringa JSON
$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);
Il costruttore di Score, in input array, gestisce stringa vuota/null per lyric, così da rendere agevole anche l’input via form passato attraverso il middleware ConvertEmptyStringsToNull.
Pattern pratico: gestione tramite file JSON (consigliato)
In prodotti reali è più gestibile mantenere le partiture in JSON invece di scriverle direttamente in PHP.
- Salva un JSON in formato personalizzato
- Caricalo e convertilo in
Note / Score
- Passalo a
Voicevox::song() per generare
Esempio 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" }
]
}
Caricamento e sintesi
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');
Pagine correlate