Objetivo
Organiza las especificaciones de Score y Note que suelen generar dudas al sintetizar canciones.
La base de la implementación son los archivos fuente de Revolution\Voicevox\Song\Note / Score / SongAudioQuery.
Clase Note
Note representa una nota (o un silencio).
use Revolution\Voicevox\Song\Note;
$note = Note::make(
length: 94,
lyric: 'ド',
key: 60,
id: 'intro-1',
);
Argumentos del constructor
| Argumento | Tipo | Descripción |
|---|
length | int | Longitud en frames. Valor entero de segundos × 93.75 |
lyric | string | Letra. Los silencios usan cadena vacía '' |
key | int|null | Número de nota MIDI. Los silencios usan null |
id | string|null | ID auxiliar que se copia a note_id al generar FrameAudioQuery |
Cálculo de la longitud en frames
length está en frames. A partir de los segundos se calcula así:
length = segundos × 93.75
Este valor no es intuitivo, así que si estás acostumbrado a MIDI, en la práctica conviene usar Note::len().
Note::len(int $ticks, int $bpm = 125): int
- Se asume que la negra son
480 ticks
- Por defecto son
125 BPM
- Internamente se redondea con
round() y se convierte en entero, así que en canciones largas el error de redondeo se acumula poco a poco
Note::make(length: Note::len(480, 120), lyric: 'ド', key: 60); // Negra
Note::make(length: Note::len(960, 120), lyric: 'ミ', key: 64); // Blanca
Note::len() es cómodo, pero en canciones largas hay que tener cuidado con el error acumulado. Si es necesario, reajusta las longitudes por secciones.
Representación de silencios
Los silencios son esta combinación:
Note::make(length: 15, lyric: '', key: null);
Referencia de números de nota MIDI
| Nota | MIDI |
|---|
| C4 | 60 |
| D4 | 62 |
| E4 | 64 |
| F4 | 65 |
| G4 | 67 |
| A4 | 69 |
| B4 | 71 |
| C5 | 72 |
Clase Score
Score es un objeto de partitura que contiene un array 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),
]);
Regla importante
La primera nota debe ser siempre un silencio.
API principal
- Se crea con
Score::make(array $notes)
- Se añade con
add(Note $note)
- Se convierte a JSON con
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);
El constructor de Score incluye una implementación que absorbe lyric vacío o null al recibir un array, así que también encaja bien con entradas de formulario que pasan por el middleware ConvertEmptyStringsToNull.
Patrón práctico: gestión con archivo JSON (recomendado)
En un producto real, es más manejable no escribir la partitura directamente en PHP y gestionarla en JSON.
- Guarda un JSON con formato propio
- Cárgalo y conviértelo en
Note / Score
- Pásalo a
Voicevox::song() para generar
Ejemplo 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" }
]
}
Carga y síntesis
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');
Páginas relacionadas