#目次
##やること
今回は前回作成した、Oculus Quest 2のアプリに追加機能をつけていきたいと思います。
実際に付ける内容は、
- 一定時間話しかけた後で、頷くor 相槌をうってもらう
- 特定のフレーズや単語に対して、反応してもらう
の2つの機能をつけていきます。
では、さっそくやっていきましょう!
##いいタイミングで頷いてもらおう!
###マイクを取り付けよう!
まず、音声の取得に必要なマイクをunityに入れていきます。
OVRCamearaRigの子に空のオブジェクトを作成して、名前をMICとしてください。
###音声を取得してみよう!
以下のGetVoice.csファイルをMICにアタッチしてください。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class GetVoice : MonoBehaviour
{
[SerializeField]
AudioSource listenSource;
[SerializeField]
Animator animator;
//private fields
private float gain = 8000.0f;
float volume;
float frequency;
bool isSpeaking;
bool isRecording;
float bufferTimer; //話した後の余韻の時間 or 言葉に詰まった時の時間 の計測時間
float speakingTimer; //実際に話している時間
//constant values
const int RECORD_SECONDS = 2;
const int SAMPLING_FREQUENCY = 16000; // MSのSSTはサンプリングレートが16000Hz
const int FFTSAMPLES = 2 << 8; //256bitのサンプルをFFTでは選んでとる。
const float MIN_FREQ = 100.0f; //母音のFrequency は 100Hz 以上 1400Hz以下。 ただし、子音字は異なる。
const float MAX_FREQ = 1400.0f;
const float MIN_VOLUME = 1.0f;
const float BUFFER_TIME = 1.6f; //話した後の余韻の時間 or 言葉に詰まった時の時間。
// Start is called before the first frame update
void Start()
{
#if UNITY_EDITOR
gain = 2000.0f;
#endif
GetMic();
}
void Update()
{
Waiting();
}
private void GetMic()
{
while (Microphone.devices.Length< 1) { }
string device = Microphone.devices[0];
listenSource.loop = true;
listenSource.clip = Microphone.Start(device, true, RECORD_SECONDS, SAMPLING_FREQUENCY);
while (!(Microphone.GetPosition(device) > 0)) { }
listenSource.Play();
}
async private void Waiting()
{
CalculateVowel();
if (MIN_FREQ < frequency && frequency < MAX_FREQ && MIN_VOLUME < volume) //しゃべり始めの時間
{
isSpeaking = true;
bufferTimer = 0.0f;
speakingTimer += Time.deltaTime;
//始めてレコーディングを開始したとき
if (!isRecording)
{
isRecording = true;
bufferTimer = 0.0f;
speakingTimer = 0.0f;
}
}
else if (isSpeaking && volume > MIN_VOLUME)
{
//子音字をしゃべっていると判定
bufferTimer = 0.0f;
speakingTimer += Time.deltaTime;
}
else if (isSpeaking && bufferTimer < BUFFER_TIME) // 余韻の時間
{
bufferTimer += Time.deltaTime;
speakingTimer += Time.deltaTime;
}
else
{
bufferTimer = 0.0f; // 後で、speakingTimerの条件処理あり!
isSpeaking = false;
if (isRecording)
{
isRecording = false;
AizuchiAnimation(speakingTimer);
}
speakingTimer = 0.0f;
}
}
private void CalculateVowel()
{
//ここが処理の重さ的にやばいかも?
var max_volume = 0.0f;
var max_index = 0;
var total_volume = 0.0f;
//録音時間*サンプリング周波数の個数のデータがほしい!
float[] temp = new float[FFTSAMPLES];
listenSource.GetSpectrumData(temp, 0, FFTWindow.Blackman);
for (int i = 0; i < temp.Length; i++)
{
if (max_volume < temp[i])
{
max_index = i;
max_volume = temp[i];
}
total_volume += Mathf.Abs(temp[i]);
}
if (temp.Length > 0)
{
frequency = max_index * AudioSettings.outputSampleRate / 2 / temp.Length;
volume = total_volume / temp.Length * gain;
}
}
private void AizuchiAnimation(float speakingTime)
{
if (2.0f < speakingTime && speakingTime < 5.0f)
{
animator.SetInteger("aizuchi", 1);
}
else if (5.0f < speakingTime && speakingTime < 8.0f)
{
animator.SetInteger("aizuchi", 2);
}
else if (8.0f < speakingTime)
{
animator.SetInteger("aizuchi", 3);
}
}
}
スクリプトをアタッチ後に、AudioSourceにMICをいれて、AnimatorにはVRMのModelを入れてください。
以下に、各機能についての説明です。
- gain
取得した音声のVolumeを計算後に何倍するかを示しています。
PCでのテスト時には2000倍、Oculus Quest 2 での実行時には8000倍となるようにしています。
- Waiting
音声を実際に取っているかを判定するところです。
ある一定の音量以上で、第一周波数(f0)が100Hz以上、1400Hz以下の時にしゃべっていると認識しています。
このf0の基準は日本語の母音の周波数帯が100Hz以上、1400Hz以下だからです。
- AizuchiAnimation
実際に相槌や頷きのアニメーションを行う部分です。
秒数 | 動作 |
---|---|
2s ~ 5s | 「うん」と頷く |
5s = 8s | 「へー、そうなんだ。」と相槌をうつ |
8s ~ | 「なるほどね」と相槌をうつ |
###キャラクターの声だけを出力しよう!
上記の設定で、ご自身の声が入力できるようになったと思います。
しかし、同時にキャラクターの声と混じって、自分の声が出力されてしまいます。
なので、AudioMixerを使って、キャラクターの声だけが出力されるようにします。
まずProjectから、右クリックして Create > AudioMixer を作ってください。
つぎに、Audio Mixerを開いて、Groupsの**+**ボタンを押し、2つのグループのMicとVoiceを追加してください。
そうしましたら、写真のようにAudioMixerを設定してください。
Micの-80dBにより、自分の声が出力されないようになります。
最後にモデルにアタッチされている Audio Source の Output に Voiceを指定し、MICにアタッチされている Audio Source の Output に Micを指定します。
これで、設定が完了しました。
###Animatorを準備しよう!
Oculus Quest 2でいちゃいちゃしてみよう!Part4でのやりかたと、同一のやり方でアニメーションを作成していきます。
まず、AnimatorのParametersから、Int型のパラメータを追加し、名前をaizuchiにします。
次に、AnimationClipを3つつくり、AnimatorにDrag&Dropします。
Drag&DropしたそれぞれのAnimationClipとWaiting(待機モーション)をそれぞれに双方向につなぎます。
Waiting -> AnimationClipの方向の矢印を右クリックして、InspectorからConditionsを追加します。
Conditionsの内容は aizuchi Equals 1~3までの数字です。
つぎに、AninmationClipをクリックして、Add Behaviourからスクリプトを追加します。
前回使用した、ExitAnimation.csに以下の行を追加して、アタッチしてください。
animator.SetInteger("aizuchi", 0)
###Animation Clipを編集しよう!
Oculus Quest 2でいちゃいちゃしてみよう!Part4でのAnimationClipを作成したときとやり方は同じです。
TimeLineで音声付きのAnimationClipを編集してください。
###Animation Clipに音声をつけよう!
Oculus Quest 2でいちゃいちゃしてみよう!Part4で使用した、Voice_Controller.csに以下のスクリプトを追加します。
[SerializeField]
public AudioClip[] aizuchiClips;
\\\
void AizuchiPlayBack(int aizuchiIndex)
{
audioSource.PlayOneShot(aizuchiClips[aizuchiIndex-1], 1.0f);
}
AnimationClipをダブルクリックして、AnimationWindowを開き、AddEventをクリックして、イベントを追加してください。
追加したEventについて、FunctionをAizuchiPlayBackとして、IntをVoiceController中のIndex+1の値に設定してください。
最後にアプリを起動させて、試してみてください。
お疲れ様でした。