UnityでAudioLiveCoding(2)

前回の続きです。

livecoding-audio

Unity Web Player | livecoding-audio

波形を即時編集する

前回下調べした通り、JavaScriptのevalを用いて実装していきます。

先日の動画と似たようなインターフェースを目指します


function Calc( t : float, w : float, s : float, f : float ) : float
{
  var y : float = 0;
  y = Mathf.sin( w * t );
  return y;
}

この関数の内部を編集していくイメージです。
一度関数が確定した後、同じ関数を何度も呼ぶことになるので
無名関数を作成し、それを変数に代入して保持しておくことにします。


public var innerCalc = function(t:float,w:float,s:float,f:float):float{return 0;};
public var LastError : String = null;
public var IsValid : boolean = false;

public function Check( code : String ) : boolean
{
  var y : float = 0;
  var t : float = 0;
  var w : float = 0;
  var s : float = 48000;
  var f : float = 440;

try
  {
    eval( code );
    eval( "innerCalc = function(t:float,w:float,s:float,f:float):float{\n" + code + ";\nreturn y;};" );
    IsValid = true;
    return true;

} catch( e : System.Exception )
  {
    IsValid = false;
    if( e as UnityScript.Scripting.CompilationErrorsException )
    {
      LastError = ( e as UnityScript.Scripting.CompilationErrorsException ).Errors.ToString();
      return false;
    }
    LastError = e.GetType().ToString() + " : " + e.Message;
  }
  return false;
}

public function Calc( t : float, w : float, s : float, f : float ) : float
{
  return innerCalc( t, w, s, f );
}

Check関数でコードの実行チェックと無名関数の生成を行っています
evalを二回呼んでますが、一回目はコードチェック用、二回目は無名関数生成用です。
無名関数生成と同時にチェックも行うことはできますが、
ここではユーザーが入力したコードに手を入れているので
エラー表記がユーザー入力時点のコードと一致しなくなってしまいます。
そのため、チェック用と無名関数生成用の二つにわけて実行しています。

実行に成功したら innerCalc に代入しておきます。
失敗した場合はエラー内容を LastError に突っ込んでおきます。
文法エラーは UnityScript.Scripting.CompilationErrorsException が返ってくるので、
そのときは読みやすいようにいい感じにしときます。
他のエラーは手抜きです。

Check関数に成功した場合はコード実行ができる状態なので、
Calc関数を呼んでユーザーが編集したコードを実行します。

上記のコードを UserCode.js としてAssets/Plugins においておきます。

呼び出し側のC#のコードは下記のような感じになりました。


public float mTime = 0.0f;
public float mFrequency = 261.62f;
public float mSampling = 48000.0f;
public UserCode mUserCode = null;

public string mCode;

void Start()
{
  mUserCode = this.GetComponent<usercode>();
  mCode = "y = Mathf.sin( w * t ) * Mathf.exp( -2 * t );";
  bool result = mUserCode.Check( mCode );
}

void OnAudioFilterRead( float[] data, int channels )
{
  for( int i = 0; i &lt; data.Length; i += channels )
  {
    float t = (mTime+i) / mSampling;
    float w = Mathf.PI * 2.0f * mFrequency;
    float s = mSampling;
    float f = mFrequency;
    data[i] = mUserCode.Calc( t, w, s, f );
    for( int k = 1; k &lt; channels; k++ )
    {
      data[i+k] = data[i];
    }
  }
  mTime += data.Length;
}

まず、Start の中で先ほどのJavaScriptのコンポーネントを取得して保持しておきます。
ついでに今回はここでコード生成、チェックも行っちゃいます。
あとは data[i] に突っ込む計算の部分を mUserCode.Calc( t, w, s, f ) に置き換えてあります。
これでランタイム上で任意のコードから波形生成できる仕組みができました。
あとはGUIでコード編集、任意のタイミングでコードチェックを行えば
リアルタイム変数ができますが、今回GUIについては割愛します。

音に同期してオブジェクトを動かす

さて、これでリアルタイム波形編集はできるようになったのですが、
ただ音が鳴らせるだけってのもツマランので、
音に同期させてオブジェクトを動かしてみようと思います。

音に同期させて動かすために、現在鳴っている音の周波数成分を取得します。
それぞれの周波数の成分を取得するのにはフーリエ変換を用いるらしい。

周波数スペクトル – Wikipedia

フーリエ変換 – DSP の基礎・トレーニング – TI

ですが、すでに周波数スペクトルを取得するライブラリを
作ってくださった方がいますので、そちらを利用させていただくことにします。

ビジュアライザ向けスペクトル分析 (Unity, C#) –Keijiro Takahashi

こちらのAudioSpectrum.csをAssets/以下の任意のディレクトリに、
AudioSpectrumInspector.csをAssets/Editor/以下に置きます。
あとはAudioSpectrumを任意のGameObjectに追加すればOK

あとは下記のような感じで、各周波数成分を取得し、同期させることができます


public float Scale;
public float Torque;

private AudioSpectrum mSpectrum;
private int mIndex;
private Vector3 mBaseScale;
private Vector3 mTorqueDir;

void Start()
{
  GameObject camera = GameObject.Find( "Main Camera" );
  mSpectrum = camera.GetComponent<audiospectrum>();
  mBaseScale = transform.localScale;
  mTorqueDir = Random.onUnitSphere;
  mIndex = Random.Range( 0, mSpectrum.Levels.Length );
}

void Update
{
  transform.localScale = mBaseScale * ( 1.0f * mSpectrum.levels[mIndex] * Scale );
  rigidbody.AddTorque( mSpectrum.Levels[mIndex] * Torque * mTorqueDir );
}

Start で Main Camera に追加した AudioSpectrum を取得し、保持しておきます。
あとは基準となるオブジェクトの大きさを保持し、トルクをかける方向を決めておきます。
AudioSpectrum.Levels が現在の周波数成分をあらわしているので、
どの周波数成分に同期するかをランダムに決めておきます。

あとは Update で周波数のレベルを取得し
オブジェクトの大きさを変えたり、トルクをかけたりしています。

おわりに

細かい調整は入っていますが、大体上記の要素で先のアプリはできています。
コード編集をユーザーが行えるとなると、Unityでゲームツクールをツクールこともできそうですね。
あとはユーザー編集Modや、Ruby Warriorみたいなこともできそうです。


コメントを残す

メールアドレスが公開されることはありません。