[Unity] オーディオレベルメーターを作ってみる [なんか違う?]

音に反応して上下するメーターを作ってみます。こちらの記事 Unity のオーディオの再生・エフェクト・解析周りについてまとめてみた(凹みTips) をそのままやってみる感じです。音楽から映像を作る 「Unity でシェーダを使って 20,000 人が音楽に合わせてサイリウム振ってる様子を作ってみた」 も素敵。

音に関する知識も無いのですべて他の方の記事をそのまま作ってみます。

※作ってみたはいいですが思った結果になりませんでした。(作成したメーターと、他のソフトウェアで表示されるメーターとなんか違う・・・。何かが足りない・・・。)

 

 

 

環境

Unity 5.0.0f4

 

uGUIでメーターのパーツを作る

参考サイトではキューブでメーターを作成していたので、今回はuGUIで作成してみます。

初期バージョン。Imageコンポーネントをそのままメーターとして使う。グラデーションテクスチャーを指定するだけでそれっぽくなる。ImageTypeをFilledにし、FillAmountを変更させることでメーターとして使えるようになります。

audiometer_bar

もうちょっと頑張ったバージョン。テクスチャーは使用せず、UIコンポーネントを並べて作成。ぽちぽちと作るのが大変。絵を作れないだけなので、テクスチャーを作って貼った方がもちろん楽だし実行時の負荷は少ないと思われる。BarにはImageとMaskを指定してあります。上と同じようにImageTypeをFilledにしてFill Amountを操作することでメーターを表現します。

audiometer_bar2

 

バー制御用スクリプトを作成

値(0~1)を渡すことで内部のMaskサイズを制御するスクリプトです。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class AudioMeterBar : MonoBehaviour 
{
  /// <summary>
  /// 操作するイメージ。
  /// </summary>
  public Image MeterImage;

  /// <summary>
  /// メーター下のテキスト。
  /// </summary>
  public Text MeterText;

  /// <summary>
  /// メーター値。0~1。
  /// </summary>
  public float MeterValue
  {
    set
    {
      if( MeterImage.fillAmount < value ) {
        MeterImage.fillAmount = value;
      }
    }
  }

  /// <summary>
  /// メーターテキスト。
  /// </summary>
  public string MeterString
  {
    set { MeterText.text = value; }
  }

  /// <summary>
  /// 
  /// </summary>
  void Awake()
  {
    for( int n = 0; n < transform.childCount; n++ ) {
      var child = transform.GetChild(n);
      if( child.name == "Bar" ) {
        MeterImage = child.GetComponent<Image>();
      }
      if( child.name == "HzText" ) {
        MeterText = child.GetComponent<Text>();
      }
    }
  }

  /// <summary>
  /// 
  /// </summary>
  void Update()
  {
    // 自動で0に近づくように。
    var newValue = MeterImage.fillAmount - (Time.deltaTime * 2);
    MeterImage.fillAmount = System.Math.Max(0, (float)newValue);
  }
}

 

必要な本数を並べる

先ほど作成したバーを並べます。ここでは20本並べました。

audiometer_meter2

 

オーディオメーター制御用のスクリプトを作成

表示する周波数の最小値、最大値、配置したバーのスクリプト配列などをプロパティに準備し、マウスでぽちぽちと設定していきます。データ取得先はAudioListenerとしました。

using UnityEngine;
using System.Collections;

public class AudioMeterControl : MonoBehaviour 
{
  /// <summary>
  /// バー配列。
  /// </summary>
  public AudioMeterBar[] Bars;

  /// <summary>
  /// 対応周波数。
  /// </summary>
  public int[] Hertz;

  /// <summary>
  /// メーター感度?
  /// </summary>
  public float MeterFactor = 1;

  float[] _spectrum = new float[1024 * 8];

  /// <summary>
  /// 更新
  /// </summary>
  void Update()
  {
    var deltaFreq = AudioSettings.outputSampleRate / _spectrum.Length;

    AudioListener.GetSpectrumData(_spectrum, 0, FFTWindow.BlackmanHarris);

    float[] values = new float[Bars.Length];
    for( int n = 0; n < _spectrum.Length; n++ ) {
      var freq = ((AudioSettings.outputSampleRate * 0.5) / _spectrum.Length) * n;
      var idx = SpectrumToIndex((int)freq);
      if( idx >= 0 ) {
        values[idx] = Mathf.Max(_spectrum[n], values[idx]);
      }
    }

    for( int n = 0; n < values.Length; n++ ) {
      float meterValue = System.Math.Min(1.0f, values[n] * this.MeterFactor);
      Bars[n].MeterValue = meterValue;
    }
  }

  /// <summary>
  /// 周波数からインデックス番号を取得します。
  /// </summary>
  /// <param name="freq"></param>
  /// <returns></returns>
  int SpectrumToIndex( int freq )
  {
    for( int n = 0; n < Hertz.Length; n++ ) {
      if( Hertz[n] > freq ) {
        return n;
      }
    }
    return -1;
  }
}

 

さて本当にこれで良いのか不明です。参考にした場所はこちら。

http://www.kaappine.fi/tutorials/fundamental-frequencies-and-detecting-notes/

http://answers.unity3d.com/questions/720179/getspectrumdata-cant-recognize-correct-note.html

http://answers.unity3d.com/questions/175173/audio-analysis-pass-filter.html

 

デモ

BGM : 魔王魂

 

※オーディオメーターも満足に作れないよー・・・。今後の課題としてひっそり更新しておきます。


希木小鳥

Diablo1でハクスラの世界に。今はBorderlands2をプレイ中。ぬるゲーマー。

あわせて読みたい