West Hill 開発メモ

タグ:Tips

Unity上で楽曲をテンポ解析してBPMを推定する機能を作ってみました。

使い方
public AudioClip targetClip;

private void Start()
{
    int bpm = BpmAnalyzer.AnalyzeBpm(targetClip);
    Debug.Log("BPMは多分 " + bpm + " くらい");
}
AnalyzeBpmの中ではAudioClip.GetData()で取ってきたオーディオのサンプルデータを解析しています。

BpmAnalyzer.cs内で
サンプルデータをある程度で区切ってまとめるCreateVolumeArrayメソッドと、
実際にBPMを推定しているSearchBpmメソッドは、
処理時間が結構かかるので別スレッドから呼ぶように変更したほうが良いと思います。

※コルーチンみたいに使えるThread Ninja(Unity Asset Store)はおすすめ。
UnityEngine名前空間でもMathfなら別スレッドからでも呼べます。

また、例のシーンではメトロノームを使っていますが、実際の楽曲はテンポが動的に変わったりするので、万能ではないです。
楽曲を数秒おきに区切ってその範囲でBPMを推定する等が良いかもしれません。


参考
テンポ解析のアルゴリズム:
C/C++言語で音声ファイルのテンポ解析を行うサンプルプログラム 

AudioClip.GetDataで取れる値について:
このエントリーをはてなブックマークに追加 Clip to Evernote

Unity5.3で追加されたJsonUtilityを少し使ってみました。
http://docs.unity3d.com/ScriptReference/JsonUtility.html

JsonUtility.ToJsonJsonUtility.FromJsonで変換する。

・内部でUnityのシリアライザを使っているので、自前のクラスを含めたい場合、
MonoBehaviourとScriptableObjectを継承していればそのまま変換される。
そうでないクラスや構造体には[Serializable]属性を付けないと無視される。

・DictinaryやHashtableはシリアライズされないため、これも無視される。
回避策として、Json化したいクラスでISerializationCallbackReceiverを継承し、
OnBeforeSerializeOnAfterDeserializeでシリアライズ前、デシリアライズ後のタイミングをフックできるので、List等の別の型に置き換える。

以下、テスト用のコンポーネント。


このエントリーをはてなブックマークに追加 Clip to Evernote

UnityでオーディオをAudioClipから事前に解析する場合についてのメモ。
ss_audio_01
オーディオのサンプルデータはAudioClip.GetData
引数に入れた配列にサイズ分だけ取ってこれる。
※オーディオファイルのインポート設定がDecompress on Loadになっていないと取得できず、値が0の配列になる。

全データが必要な場合はサンプル数*チャンネル数分の配列を用意する。
//
// クリップの全サンプルデータ取得
float[] allSamples = new float[clip.samples * clip.channels];
clip.GetData(allSamples, 0);
第2引数はとってくるデータ開始位置までのオフセット。
秒数でオフセットを指定したい場合は秒数*サンプルレート*チャンネル数になる。
解析する際に数秒分のデータをスキップしたい時にもこの式が使える。
//
// オフセット2秒
float offset = 2f * clip.frequency * clip.channels;
AudioClip.GetDataで取得したデータは2chの場合、
LRLRLR…の順で-1~0と0~1の範囲に収まっていて、無音だと0になる。

このデータをゴニョゴニョした後、再生中にデータとの同期を取りたい場合、
再生中のオーディオソースからAudioSource.timeSamplesで現在の再生サンプル時間を取得できるので、これを使う。

このtimeSamplesでクリップからAudioClip.GetDataで取得した配列を参照する場合は、
timeSamples*チャンネル数が当該インデックスになる。
//
// 今ここ
float sample = samples[audioSource.timeSamples * clip.channels];

このエントリーをはてなブックマークに追加 Clip to Evernote

Unity4.5.3からResourcesディレクトリ配下のアセットの読込を非同期で行うResources.LoadAsync()メソッドが追加されたので試してみました。


戻り値であるResourceRequestで進捗の取得や優先度の設定が行えます。

このエントリーをはてなブックマークに追加 Clip to Evernote

Unity4.3以降でビルドしたAndroidアプリで上部のStatus Barを隠さない設定で、ソフトキーボードを表示すると以下のように画面全体のサイズが小さくなってしまう現象があります。

この画面が
unity_android_softkey_01
こうなってしまう。
unity_android_softkey_02
Activity全体がリサイズされてしまいます。
AndroidManifest上でwindowSoftInputModeを変更したりしても防げませんでした。

参考:UnityForums Android Manifest Not Working Properly

上記フォーラムでは最後にUnityPlayerActivityをオーバーライドしたActivity内で
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
を呼び出しWindowの設定を変更すれば解決するとなっているのですが、 
前回の「UnityからAndroidのUIスレッドで処理を行う」を使用してUnity側のC#コードから設定してみます。


このコンポーネントを付けたGameObjectを最初のシーンに配置します。
これでキーボードを出しても画面サイズが小さくならなくなりました。
unity_android_softkey_03

このエントリーをはてなブックマークに追加 Clip to Evernote

このページのトップヘ