MusiCalorieの作成にはUnityを使っています。
この中で、マイク音量を検出してレベルメーターを表示する、という事を行っていますが、Unityではあまりやられないタイプの内容で、調べてもあまり見つからなかったので、誰かの参考になればと思い実装方法を共有します。
基本設計
基本設計の概念図です。

“MicAudioSource.cs”、”LevelMeter.cs”の2つのスクリプトを用意して、それぞれで以下の内容を実装することにします。
・AudioSourceにAudioClipとしてマイクデバイスのオーディオ信号を読み込ませる
・GetOutputData()でAudioSourceの信号を取得する
・取得した信号をdBに変換して、その値を保持
・MicAudioSourceのdB値を監視する
・dB値をuGUI Image のFillAmountの値に変換
・FillAmountの値をImageに反映し、レベルメーター画像の伸縮動作をさせる
MicAudioSource.csの実装
Unity側の準備
まずはProjectにAudioMixerを作成します。
AudioMixerを使わないと、のちのち不都合が出てしまいますので、最初に作成しておきます。

AudioMixerを開いて、”mic_input”グループを追加、ボリュームを-80dBに下げておきます。
ここが一番のポイントで、後ほどこのグループにAudioSourceを流し込むことになるのですが、
ここのボリュームを下げておかないと、マイクから入れた音声がスピーカーから出てきてしまいます。「出てきてもいいよ!」という状況であれば、そもそもAudioMixerすら使う必要もないのですが、「出てきたらマズい」という状況の場合は、これ以外の方法、例えば
・AudioSourceのVolumeを0にする
・AudioSourceをMuteにする
等で対応してしまうと、GetOutputData()に音が入ってこなくなりますので、そもそも音量が測定できなくなってしまいます。そこで行き着いたのが、一度AudioMixerまで音を流し込んでおき、AudioMixer側で音が出てこないようにする、という方法でした。
※もっと良い方法がありましたらすみません。

次に、HierarchyにAudioSourceを追加します。

追加したAudioSourceの設定は以下です。

Outputに先ほど作成した”mic_input”を選択しておくことが肝心です。
スクリプト
以下を先ほど作成したAudioSourceにアタッチします。
//MicAudioSource.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
class MicAudioSource : MonoBehaviour
{
//サンプリング周波数
static readonly int SAMPLE_RATE = 48000;
//この秒数の幅で振幅の平均値を取ったものでdB値を更新
static readonly float MOVING_AVE_TIME = 0.05f;
//MOVING_AVE_TIMEに相当するサンプル数
static readonly int MOVING_AVE_SAMPLE = (int)(SAMPLE_RATE * MOVING_AVE_TIME);
//マイクのClipをセットする為のAudioSource
AudioSource micAS = null;
//現在のdB値
private float _now_dB;
public float now_dB { get { return _now_dB; } }
private void Awake()
{
//AudioSourceコンポーネント取得
micAS = GetComponent<AudioSource>();
}
void Start()
{
//フレーム更新開始直後にマイクデバイスをスタートする
this.MicStart();
}
public void MicStart()
{
//AudioSourceのClipにマイクデバイスをセット
micAS.clip = Microphone.Start(null, true, 1, SAMPLE_RATE);
//マイクデバイスの準備ができるまで待つ
while (!(Microphone.GetPosition("") > 0)) { }
//AudioSouceからの出力を開始
micAS.Play();
}
void Update()
{
if (micAS.isPlaying)
{
//GetOutputData用のバッファを準備
float[] data = new float[MOVING_AVE_SAMPLE];
//AudioSourceから出力されているサンプルを取得
micAS.GetOutputData(data, 0);
//バッファ内の平均振幅を取得(絶対値を平均する)
float aveAmp = data.Average(s => Mathf.Abs(s));
//振幅をdB(デシベル)に変換
float dB = 20.0f * Mathf.Log10(aveAmp);
//現在値(now_dB)を更新
_now_dB = dB;
}
}
}
細かい説明はコード上のコメントをご確認ください。
なお、簡単のために省きましたが、AndroidやiOSだと、パーミッションの取得等を事前に行っておく必要がありますので、ご注意ください。
ポイントとしては、micAS.GetOutputData()で取得されるデータは、-1.0f~1.0fの範囲を取る、振幅値になっていますので、これをこのままレベルメーターに反映しても、よくあるdB単位のレベルメーターの動きにはなりませんので、きっちりdBに変換してあげることです。
LevelMeter.csの実装
Unity側の準備
まずはレベルメーター用のuGUI Imageを追加しましょう。

ImageのSource Imageに、Projectから画像を追加します。
画像の内容は、細長い奴でもいいですし、派手な色付きのやつ、太い奴でも、お好みで大丈夫です。

Imageの設定は以下です。Image TypeをFilledにしてください。
横長のレベルメーターを作成する場合は、「Fill Method」を「Horizontal」に、「Fill Orign」を「Left」に設定します。

スクリプト
以下のスクリプトを先ほど作成したImageにアタッチします。
//LevelMeter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
class LevelMeter : MonoBehaviour
{
//更新する対象のlevelMeter(uGUI Image)
Image levelMeterImage = null;
//このdBでlevelMeter表示の下限に到達する
[SerializeField]
private float dB_Min= -80.0f;
//このdBでlevelMeter表示の上限に到達する
[SerializeField]
private float dB_Max = -0.0f;
//dBを取得する対象のmicAudioSource
[SerializeField]
private MicAudioSource micAS = null;
void Awake()
{
//更新する対象のImageを取得
levelMeterImage = GetComponent<Image>();
}
void Update()
{
//dB値からlevelMeterImage用のfillAountの値に変換
float fillAmountValue = dB_ToFillAmountValue(micAS.now_dB);
//fillAmount値更新
this.levelMeterImage.fillAmount = fillAmountValue;
}
/// <summary>
/// dB_Minとdb_Maxに基づいてdBをfillAmount値に変換
/// </summary>
/// <param name="dB">dB値</param>
/// <returns>fillAmount値</returns>
float dB_ToFillAmountValue(float dB)
{
//入力されたdBをdB_MaxとdBMin値で切り捨て
float modified_dB = dB;
if (modified_dB > dB_Max) { modified_dB = dB_Max; }
else if (modified_dB < dB_Min) { modified_dB = dB_Min; }
//fillAmount値に変換(dB_Min=0.0f, dB_Max=1.0f)
float fillAountValue = 1.0f + (modified_dB / (dB_Max - dB_Min));
return fillAountValue;
}
}
MicAudioSourceを監視する必要があります。インスペクタ上のLevel MeterスクリプトのMicASに、MicAudioSourceをアタッチしたAudioSourceを設定します。

動作チェック
実行してみましょう。
マイクに向かってしゃべったりしてみて、こんな感じで動けば成功と思います。

なかなかにマニアックな需要だと思いますが、参考になったらうれしいです。
コメント