Newer
Older
remotion_docker_devcontainer / voicevox-remotion-template / src / lib / VQRemotionLib / scenario.ts
export type VQChronologicalScenario<Event> = Readonly<{
  timeline: readonly Event[];
  gapFrames: number;
  durationForEvent: (event: Event, fps: number) => number;
  assetWorkflow?: VQScenarioAssetWorkflow;
}>;

export type VQScenarioVoicevoxWorkflow = Readonly<{
  scriptPath: string;
  outputDir: string;
  manifestPath: string;
}>;

export type VQScenarioRhubarbWorkflow = Readonly<{
  sourceManifestPath: string;
  manifestPath: string;
  outputDir: string;
  rawOutputDir: string;
}>;

export type VQScenarioAssetWorkflow = Readonly<{
  voicevox: VQScenarioVoicevoxWorkflow;
  rhubarb: VQScenarioRhubarbWorkflow;
}>;

export type VQScheduledScenarioSegment<Event> = Readonly<{
  event: Event;
  index: number;
  from: number;
  durationInFrames: number;
}>;

export const defineVQChronologicalScenario = <Event>(
  scenario: VQChronologicalScenario<Event>
) => scenario;

export const defineVQScenarioAssetWorkflow = (
  workflow: VQScenarioAssetWorkflow
) => workflow;

export const scheduleVQChronologicalScenario = <Event>(
  scenario: VQChronologicalScenario<Event>,
  fps: number
): VQScheduledScenarioSegment<Event>[] => {
  let cursor = 0;

  return scenario.timeline.map((event, index) => {
    const durationInFrames = scenario.durationForEvent(event, fps);
    const segment = {
      event,
      index,
      from: cursor,
      durationInFrames,
    };

    cursor += durationInFrames;
    if (index < scenario.timeline.length - 1) {
      cursor += scenario.gapFrames;
    }

    return segment;
  });
};

export const activeVQChronologicalScenarioSegmentForFrame = <Event>(
  segments: readonly VQScheduledScenarioSegment<Event>[],
  frame: number
) => {
  let activeSegment = segments[0];

  for (const segment of segments) {
    if (frame >= segment.from) {
      activeSegment = segment;
    } else {
      break;
    }
  }

  return activeSegment;
};

export const totalVQChronologicalScenarioDurationInFrames = <Event>(
  scenario: VQChronologicalScenario<Event>,
  fps: number
) =>
  scheduleVQChronologicalScenario(scenario, fps).reduce(
    (total, segment, index) =>
      total +
      segment.durationInFrames +
      (index < scenario.timeline.length - 1 ? scenario.gapFrames : 0),
    0
  );