Newer
Older
remotion_docker_devcontainer / voicevox-remotion-template / scripts / lipsync-utils.test.js
import assert from "node:assert/strict";
import {test} from "node:test";
import {normalizeRhubarbJson} from "./lipsync-utils.js";

test("maps Rhubarb mouth shapes to Japanese mouth shapes", () => {
  const {timeline} = normalizeRhubarbJson(
    {
      metadata: {duration: 1.2},
      mouthCues: [
        {start: 0, end: 0.1, value: "X"},
        {start: 0.1, end: 0.2, value: "D"},
        {start: 0.2, end: 0.3, value: "F"},
      ],
    },
    {audio: "audio/example.wav"}
  );

  assert.deepEqual(
    timeline.cues.map((cue) => cue.mouth),
    ["rest", "a", "u"]
  );
});

test("uses rest for unknown shapes and reports a warning", () => {
  const {timeline, warnings} = normalizeRhubarbJson(
    {
      mouthCues: [{start: 0, end: 0.1, value: "Z"}],
    },
    {audio: "audio/example.wav"}
  );

  assert.equal(timeline.cues[0].mouth, "rest");
  assert.match(warnings[0], /Unknown Rhubarb mouth shape "Z"/);
});

test("uses metadata duration when available", () => {
  const {timeline} = normalizeRhubarbJson({
    metadata: {duration: 2.5},
    mouthCues: [{start: 0, end: 0.1, value: "X"}],
  });

  assert.equal(timeline.duration, 2.5);
});

test("falls back to the last cue end for duration", () => {
  const {timeline} = normalizeRhubarbJson({
    mouthCues: [
      {start: 0, end: 0.4, value: "X"},
      {start: 0.4, end: 0.8, value: "D"},
    ],
  });

  assert.equal(timeline.duration, 0.8);
});