見出し画像

【Unity】オーディオビジュアライザーをつくる

前回に続き音シリーズです。今回はマイク入力の周波数成分を視覚化してUnityで表示してみたいと思います。いわゆるオーディオビジュアライザーです。

環境

Unity 2019.4.5f1

1. スクリプト

以下のスクリプトを適当なオブジェクトにアタッチし、同オブジェクトにAudioSourceもアタッチします。アタッチ後、AudioSource設定のLoopにチェックを入れます。AudioSourceには音声信号をスペクトラム解析してくれるGetSpectrumDataメソッドが用意されているので、今回はこれを使用しています。

using UnityEngine;

class MicAudioSource : MonoBehaviour {
    [SerializeField] private string m_DeviceName;
    private const int SAMPLE_RATE = 48000;
    private const int RESOLUTION = 1024;
    private AudioSource m_MicAudioSource;

    [SerializeField] private LineRenderer m_LineRenderer;
    private readonly Vector3[] m_Positions = new Vector3[1024];
    [SerializeField, Range(1, 300)] private float m_AmpGain = 300;
    
    private void Awake() {
        m_MicAudioSource = GetComponent<AudioSource>();
    }

    void Start() {
        string targetDevice = "";
        
        foreach (var device in Microphone.devices) {
            Debug.Log($"Device Name: {device}");
            if (device.Equals(m_DeviceName)) {
                targetDevice = device;
            }
        }
        
        Debug.Log($"=== Device Set: {targetDevice} ===");
        MicStart(targetDevice);
        
        // LineRenderer初期化
        for (int i = 0; i < RESOLUTION; i++) {
            var x = 10 * (i / 512f - 1);
            m_Positions[i] = new Vector3(x, 0, 0);
        }
        
        m_LineRenderer.SetPositions(m_Positions);
        
        Debug.Log($"Sample rate: {AudioSettings.outputSampleRate}");
    }

    void Update() {
        DrawSpectrum();
    }

    private void DrawSpectrum() {
        if (!m_MicAudioSource.isPlaying) return;
        
        float[] spectrum = new float[RESOLUTION];
        m_MicAudioSource.GetSpectrumData(spectrum, 0, FFTWindow.BlackmanHarris);

        for (int i = 0; i < RESOLUTION / 2; i++) {
            m_Positions[i].y = spectrum[i] * m_AmpGain;
        }
        
        m_LineRenderer.SetPositions(m_Positions);
    }
    
    private void MicStart(string device) {
        if (device.Equals("")) return;
        
        m_MicAudioSource.clip = Microphone.Start(device, true, 1, SAMPLE_RATE);

        //マイクデバイスの準備ができるまで待つ
        while (Microphone.GetPosition("") <= 0) { }

        m_MicAudioSource.Play();
    }
}

GetSpectrumDataの第一引数(spectrum)には2のべき乗のfloat配列を渡します。GetSpectrumDataが実行されると、spectrum配列には要素数をサンプリングレートで割った各周波数成分が格納されます。

spectrumデータと周波数成分の関係

ここでのサンプリングレートは、Microphoneで設定した値ではなくUnityプロジェクトに設定されているサンプリングレートが使用されます。

プロジェクトのサンプリングレートはメニューの[Edit] > [ProjectSettings]のAudio項目の「System Sample Rate」で設定します。

プロジェクトのサンプリングレート

初めて確認する場合は「0」が入っていますが、設定的にはデフォルトで前回の値または、44100/48000が設定されているため、改めてこのウィンドウで任意の値を設定することができます。

デフォルトでどの値が設定されているかどうかは、以下のスクリプトで確認することができます。

AudioSettings.outputSampleRate

2. LineRendererの設定

スクリプトの設定が完了したら、次はビジュアル要素となるLineRendererの設定を行います。ヒエラルキー上に空のオブジェクトを配置し、LineRendererコンポーネントをアタッチします。

LineRendererの設定

LineRendererの設定として、「Width」を0.05、「Materials」にDefault-Lineをアタッチします。色も適宜変えてみてください。この辺りはお好みで構いません。

重要な要素としては、LineRendererのPositionsの値をスクリプトのRESOLUTIONで設定した値と同じ1024にします。これで得られた周波数成分ごとにpointの位置を設定して視覚化させます。最後にLineRendererがアタッチされているオブジェクトのTransformが原点(0, 0, 0)にあることを確認して設定は完了です。

LineRendererのPositionsの設定

3. 実行

最初に実行するとコンソールログにMicrophoneデバイスの一覧が表示されるので、マイクとつながっているデバイス名をコピーし、作成したスクリプトのインスペクタの「m_DeviceName」に貼り付けます。

Microphoneデバイス一覧

デバイス名を設定した状態で、一度実行を止めて再び実行してマイクに話しかけるとLineRendererで周波数成分を確認することができます。

Microphoneデバイスの指定を間違えると、デバイス準備完了待機のループでロックしてしまいUnity(エディタ)が固まってしまうので注意してください。
今回は解説用のためエラー処理は省いています。

スクリプトの項でも触れましたが、GetSpectrumDataで得られるデータは0Hz~指定サンプリングレートまでの値が入っているため、半分以上は可聴域やマイクの収音帯域を超えるデータとなっており、そのほとんどがゼロまたはとても小さい値になっています。

そのため、音のスペクトラムを視覚化する場合は、サンプリングレートの半分以上の配列データは切り捨てるなどして調整すると良いでしょう。(上記の動画では範囲を調整しています)

4. おわりに

音声信号のスペクトラム解析となると、フーリエ変換をする必要がありますが、AudioSourceのGetSpectrumDataを使うと簡単に周波数成分を得られることが分かりました。

音量の大小だけでなく周波数成分に応じた処理をさせることで、より高度なUnityと音の連携を行うことができるようになるので、インタラクティブな作品作りに活用してみたいですね。🌱

5. 参考

凹みさんの記事はいつもお世話になっています。

https://qiita.com/niusounds/items/b8858a2b043676185a54


この記事が気に入ったらサポートをしてみませんか?