最初OVRLipSyncを使って実装したけど、iOSリリース出来ないらしいので別の手段を使う。
VRMをマイク音量から口パク
こちらの記事のスクリプトを少し変えて実装しました。こちらのLive2Dキャラを口パクさせている。
こんな感じになった
using UnityEngine;
using VRM;
/// <summary>
/// 口パクを行うクラス
/// </summary>
public class SimpleLipSyncer : MonoBehaviour
{
[SerializeField] private AudioSource audioSource = null;
[SerializeField] private VRMBlendShapeProxy blendShapeProxy;
float velocity = 0.0f;
float currentVolume = 0.0f;
[SerializeField] private float Power = 50f;
[SerializeField, Range(0f, 1f)] private float Threshold = 0.1f;
void Start()
{
// Audio Source の Audio Clip をマイク入力に設定
// 引数は、デバイス名(null ならデフォルト)、ループ、何秒取るか、サンプリング周波数
audioSource.clip = Microphone.Start(null, true, 1, 44100);
// マイクが Ready になるまで待機(一瞬)
while (Microphone.GetPosition(null) <= 0) { }
// 再生開始(録った先から再生、スピーカーから出力するとハウリングします)
audioSource.Play();
audioSource.loop = true;
}
private void LateUpdate()
{
float targetVolume = GetAveragedVolume() * Power;
//Threshold以下だったらtargetVolumeを0にする
targetVolume = targetVolume < Threshold ? 0 : targetVolume;
//currentVolumeからtargetVolumeへ緩やかに変化
currentVolume = Mathf.SmoothDamp(currentVolume, targetVolume, ref velocity, 0.05f);
if (blendShapeProxy == null)
{
Debug.LogError("blendShapeProxyが設定されていません");
return;
}
//音量の値をBlendShapeのAの口に入れている
blendShapeProxy.ImmediatelySetValue(BlendShapePreset.A, Mathf.Clamp01(currentVolume));
}
float GetAveragedVolume()
{
float[] data = new float[256];
float a = 0;
audioSource.GetOutputData(data, 0);
foreach (float s in data)
{
a += Mathf.Abs(s);
}
return a / 255.0f;
}
}
音量(currentVolume)の数値をBlendShapePreset.Aの数字にセットしている
AudioSourceの音をもとに口パクするなら(Micを使わないなら)Start関数は要らない
設定
VRMアバターからBlendShapeProxyセット。AudioSourceもセット
数値
PowerとThresholdの数値は環境によって変わりそう。実行しながら調整した。
Powerは音量に対する口パクの大きさ。
普通にしゃべった時に口がマックスに開いて、小さい声だとちょっと開くみたいになれば理想。
Thresholdは雑音や小さな音をどれくらい許容するか。
21、0.8にした。
でも最初にやった時は50、0.1とかがよかったんだけどなー。何が変化したんだろう
結果
実行したらこんな感じ
(比較)OVRLipSyncだとこんなかんじ
最後に
SALSAとか使ったほうが良いのかな