Saltar al contenido principal

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

ArgumentoTipoDescripción
lengthintLongitud en frames. Valor entero de segundos × 93.75
lyricstringLetra. Los silencios usan cadena vacía ''
keyint|nullNúmero de nota MIDI. Los silencios usan null
idstring|nullID 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

NotaMIDI
C460
D462
E464
F465
G467
A469
B471
C572

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.
  1. Guarda un JSON con formato propio
  2. Cárgalo y conviértelo en Note / Score
  3. 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

Última modificación el 13 de julio de 2026