メインコンテンツまでスキップ

動画とサウンド

<Video>

動画を音声とともに配置します(ミュート可能)。 Studio では <video>、レンダー時は WebSocket + Canvas で再生します。

import { Video } from "../src/lib/video/video"

<Video video="assets/demo.mp4" />

trim でソースの切り出しも可能です(フレーム単位)。

<Video video="assets/demo.mp4" trim={{ from: 30, duration: 120 }} />

タイムラインの波形表示は60秒未満のクリップは自動で有効です。 長いクリップは showWaveform を指定した場合のみ表示されます。

<Video video="assets/demo.mp4" showWaveform />

<Img>

レンダラーがフレームを取得する前にデコード完了を待つ画像コンポーネントです。

import { Img } from "../src/lib/image"

<Img src="assets/intro.png" />

video_length

動画の長さを取得します。

const length = video_length({ path: "assets/demo.mp4" })

<Sound>

Studio で音声を再生しつつ、レンダリング後にも該当箇所に音をつけます。

import { Sound } from "../src/lib/sound/sound"

<Sound sound="assets/music.mp3" trim={{ trimStart: 30 }} />

タイムラインの波形表示は60秒未満のクリップは自動で有効です。 長いクリップは showWaveform を指定した場合のみ表示されます。

<Sound sound="assets/music.mp3" showWaveform />

<Character>

音量に応じて口の閉じた/開いた画像を切り替えます。 clipLabel を指定すると、そのラベルのクリップ内の音声にのみ反応します。

import { Character } from "../src/lib/sound/character"

<Clip label="Voice">
<Sound sound="assets/voice.mp3" />
</Clip>

<Character
mouthClosed="assets/char_closed.png"
mouthOpen="assets/char_open.png"
threshold={0.12}
clipLabel="Voice"
/>

<PsdCharacter>

PSD形式の立ち絵を利用した口パクなどのアニメーションを宣言します。 <PsdCharacter>コンポーネント内で、専用のコンポーネントを利用してPSDのオプションを制御し、canvasへ描画します。 コンポーネントを作成することもできますが、内部でフックを使うことは出来ません。

import { BEZIER_SMOOTH } from "../src/lib/animation/functions"
import { seconds } from "../src/lib/frame"
import { PsdCharacter, MotionSequence, MotionWithVars, createSimpleLipSync } from "../src/lib/character/character-unit"


const SimpleLipSync = createSimpleLipSync({
kind: "bool",
options: {
Default: "表情/口/1",
Open: "表情/口/1",
Closed: "表情/口/5",
}
})

<PsdCharacter psd="../assets/char.psd" className="char">
<MotionSequence>
<SimpleLipSync voice="../assets/001_char.wav" />
<MotionWithVars
variables={{t: 0 as number}}
animation={async (ctx, variables) => {
await ctx.move(variables.t).to(1, seconds(1), BEZIER_SMOOTH)

}}
motion={(variables, frames) => {
const t = variables.time.get(frames[0])
if (t > 0.5) {
return {
"表情/目/9": false,
"表情/目/17": true
}
} else {
return {}
}
}}
/>

</MotionSequence>
</PsdCharacter>

主なコンポーネントは次の通りです。

<MotionSequence>

子要素を直列化します。 内部的には<Sequence>を利用しており、子要素は<Clip>で囲われます。

import { MotionSequence, Voice } from "../src/lib/character/character-unit"

<MotionSequence>
<Voice voice="../assets/001_char.wav" />
<Voice voice="../assets/002_char.wav" />
</MotionSequence>

<MotionClip>

MotionSequence直下で使用して、子要素を並列化します。

import { MotionSequence, MotionClip, Voice } from "../src/lib/character/character-unit"

<MotionSequence>
<Voice voice="../assets/001_char.wav" />
<MotionClip>
<Voice voice="../assets/002_char.wav" />
<Voice voice="../assets/003_char.wav" />
</MotionClip>
</MotionSequence>

<Voice>

音声を配置します。 内部的には音声はClipで囲われます。

import { Voice } from "../src/lib/character/character-unit"

<Voice voice="../assets/001_char.wav" />

<MotionWithVars>

変数を使用したアニメーションを作成します。 variablesで変数を宣言し、次にanimationでアニメーションを宣言し、最後にmotionでPSDのオプションを返します。 オプションはag-psd-psdtoolのrenderPsdの引数dataに準拠します。

PsdCharacter以下ではフックが使えないため、Variableの値はframes[0]を利用して、getメソッドから得てください。

frames[0]にはuseCurrentFrameで得られるフレーム数が入っていますが、MotionWithVars自体は<Clip>で囲われないことに注意してください。

import { BEZIER_SMOOTH } from "../src/lib/animation/functions"
import { seconds } from "../src/lib/frame"
import { MotionWithVars } from "../src/lib/character/character-unit"

<MotionWithVars
variables={{t: 0 as number}}
animation={async (ctx, variables) => {
await ctx.move(variables.t).to(1, seconds(1), BEZIER_SMOOTH)

}}
motion={(variables, frames) => {
const t = variables.time.get(frames[0])
if (t > 0.5) {
return {
"表情/目/9": false,
"表情/目/17": true
}
} else {
return {}
}
}}
/>

createSimpleLipSync

PSDファイルに対応した音量ベースの口パクを行うコンポーネントを返す関数です。 PSDの口の状態をレイヤー、オプションに対応させる辞書を受け取り、コンポーネントを返します。 辞書は次のように指定します。

  • psd-tool-kitに対応している場合

    kindにはenumを指定します。

    MouthにはPSDの口のレイヤーを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のオプションを指定します。

    Open / Closedにはそれぞれ対応するオプションを指定します。

  • psd-tool-kitに対応していない場合

    kindにはboolを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のレイヤーを指定します。

    Open / Closedにはそれぞれ対応するレイヤーを指定します。

import { createSimpleLipSync } from "../src/lib/character/character-unit"

const SimpleLipSync = createSimpleLipSync({
kind: "bool",
options: {
Default: "表情/口/1",
Open: "表情/口/1",
Closed: "表情/口/5",
}
})

<SimpleLipSync voice="../assets/001_char.wav" />

createLipSync

PSDファイルに対応した母音ベースの口パクを行うコンポーネントを返す関数です。

PSDの口の状態をレイヤー、オプションに対応させる辞書を受け取り、コンポーネントを返します。 コンポーネントはdataとしてタイミング情報を受け取り、口パクを制御します。 dataはrhubarb( https://github.com/DanielSWolf/rhubarb-lip-sync )の出力に対応します。 辞書は次のように指定します。

  • psd-tool-kitに対応している場合

    kindにはenumを指定します。

    MouthにはPSDの口のレイヤーを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のオプションを指定します。

    Open / Closedにはそれぞれ対応するオプションを指定します。

  • psd-tool-kitに対応していない場合

    kindにはboolを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のレイヤーを指定します。

    Open / Closedにはそれぞれ対応するレイヤーを指定します。

import { createLipSync } from "../src/lib/character/character-unit"

const LipSync = createLipSync({
kind: "enum",
options: {
Mouth: "表情/口",
Default: "1",
A: "1",
I: "2",
U: "3",
E: "4",
O: "5",
X: "6",
}
})

const lipsync = {
mouthCues: [
{ start: 0.00, end: 0.03, value: "A" },
{ start: 0.03, end: 0.09, value: "B" },
{ start: 0.09, end: 0.29, value: "C" }
}

<PsdCharacter psd="../assets/char.psd">
<Voice voice="../assets/001_char.wav" />
<LipSync data={lipsync} />
</PsdCharacter>

PSDファイルに対応した目パチを行うコンポーネントを返す関数です。

PSDの目の状態をレイヤー、オプションに対応させる辞書を受け取り、コンポーネントを返します。 コンポーネントはdataとしてタイミング情報を受け取り、目パチを制御します。 辞書は次のように指定します。

  • psd-tool-kitに対応している場合

    kindにはenumを指定します。

    MouthにはPSDの口のレイヤーを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のオプションを指定します。

    Open / Closedにはそれぞれ対応するオプションを指定します。

  • psd-tool-kitに対応していない場合

    kindにはboolを指定します。

    DefaultにはPSDファイルがデフォルトで表示する口のレイヤーを指定します。

    Open / Closedにはそれぞれ対応するレイヤーを指定します。

dataのvalueは次のように対応します。

valueoption
"A""Open"
"B""HalfOpen"
"C""HalfClosed"
"D""Closed"
import { createBlink, generateBlinkData } from "../src/lib/character/character-unit"

const Blink = createBlink({
kind: "enum",
options: {
Mouth: "表情/目",
Default: "1",
Open: "1",
HalfOpen: "2",
HalfClosed: "3",
Closed: "4",
}
})

// const blink = generateBlinkData(0, 10)
const blink = {
blinkCues: [
{ start: 0.00, end: 0.01, value: "A" },
{ start: 0.01, end: 0.02, value: "B" },
{ start: 0.02, end: 0.03, value: "C" },
{ start: 0.03, end: 0.04, value: "D" },
{ start: 0.04, end: 0.05, value: "C" },
{ start: 0.05, end: 0.06, value: "B" },
{ start: 0.06, end: 0.07, value: "A" }
}

<PsdCharacter psd="../assets/char.psd">
<Voice voice="../assets/001_char.wav" />
<Blink data={blink} />
</PsdCharacter>

<DialogueScenerio>

会話形式のシナリオにおいてPSD形式の立ち絵を利用して口パクなどのアニメーションを制御します。

子要素として使用できる主なコンポーネントは次の通りです。

<DeclareCharacters>

<DeclareCharacter>を子要素にとり、利用するキャラクターを宣言します。

<DeclareCharacter>

<DeclareCharacters>内で使用して利用するキャラクターを宣言します。 子要素として<PsdCharacter>の子要素と同様のコンポーネントを取ることができ、非話者時の動作として割り当てられます。

<Scenario>

<Chapter>を並べて会話を宣言します。

<Chapter>

<Scenario>内で使用してキャラクターの話す単位を宣言します。 <Speaker>を使用して話者を宣言します。 その他Reactコンポーネントを配置することもできます。

<Speaker>

<Chapter>内で使用して、話者の動作を宣言します。 <PsdCharacter>の子要素と同様のコンポーネントを取ります。 <DeclareCharacter>で宣言したnameを指定して、対応するPSDを表示します。

import { DialogueScenario, DeclareCharacters, DeclareCharacter, Scenario, Chapter, Speaker } from "../src/lib/character/character-manager"
import { Voice } from "../src/lib/character/character-unit"

<DialogueSenario>
<DeclareCharacters>
<DeclareCharacter idleClassName="akane" speakingClassName="akane" name="akane" psd="../assets/akane.psd" />
<DeclareCharacter idleClassName="aoi" speakingClassName="aoi" name="aoi" psd="../assets/aoi.psd" />
</DeclareCharacters>
<Scenario>
<Chapter>
<Speaker name="aoi">
<Voice voice="../assets/001_aoi.wav"/>
</Speaker>
</Chapter>
<Chapter>
<Speaker name="akane">
<Voice voice="../assets/002_akane.wav" />
</Speaker>
</Chapter>
</Scenario>
</DialogueSenario>