サウンドフォントからADX2LE変換するSF2ADX2を作ってみた


sf2からADX2LEデータへのコンバーターを作ってみることにした。

#はじめに
- もっと気軽に高品質でいろんな楽器音を出してみたいと思った。
- サウンドフォントというフォーマットがあり、いろいろ音色が公開されている。
- ADX2LEなら大量サンプルの扱いや圧縮が得意そう。

Unityパッケージ: [SF2ADX2LE.unitypackage](http://chahix.ddo.jp/~chahi/unity/20160504SF2ADX2/SF2ADX2LE.unitypackage)
https://github.com/tatmos/SoundFont2ADX2LE
改変、改造、解析など自由にどうぞ。

動画：

[![ ](http://img.youtube.com/vi/S0xBpIwbDpI/0.jpg)](http://www.youtube.com/watch?v=S0xBpIwbDpI)

#SoundFontの仕様とか参考にしたもの
gocha氏のQiita記事[SoundFont 2.04 仕様の概要まとめ](http://qiita.com/gocha/items/f3e7f216a071808e9e2d)
仕様書[SoundFont®Technical Specification ](https://www.linuxsampler.org/libgig/)
SoundFontエディター[Viena SoundFont Editor](http://www.synthfont.com/Downloads.html)


#環境は
Unity5.3.4f1、UnityC#スクリプト

#変換ルール

変換元：.sf2ファイル
生成物：ADX2LEワークユニットファイル

1サウンドフォント＝1ワークユニット、1音色プリセット＝1キューシート、キュー名（キューID）はNoteNo 0~127。
とかで、プログラムから扱いやすくなるような感じがした。

#ターゲットのSoundFont
すべてのサウンドフォントが読めるのが理想ですが、ちょっとわからない点もあり、
とりあえず、波形を取り出して、ピッチのマップができればいいかという軽い気持ちで始めてみた。

DMG-CPU1.5.SF2
4OPFM.SF2
Famicom.sf2

とかチップチューンぽいものを選んでみた。

#必要そうなチャンクを読んでいく
チャンク構造やバイナリを読むなんて、久しぶり。今時でもエンディアンとか気にしながら読まないといけないフォーマットだなぁと思いつつ、
とりあえず情報とれればいいのでstringやintとかで読めるようにしていった。

手元にあるシンプルなSoundFont(Famicom.sf2、4OPFM.sf2など）を読み込み、解析のためにログを出して内容を見ていく。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/1f8ebf06-8c52-1bb8-0dff-d8bdc1e57ad1.png)


#波形データを分割する
どうやらSoundFontは波形が一つだけどーんと'smpl'チャンクに入っていて、これを’shdr'チャンクの内容をもとに分割することにした。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/766cb50b-93b8-85cc-bbf2-53bb89b6d141.png)
ここら辺がADX2LEではマテリアルとかウェーブフォームの設定になる。

#音色を取りまとめる
プリセット、instrumentなどの情報からキューの設定を考える。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/e73af55f-cc4d-d543-c011-41597472e753.png)

音色に関しての様々な情報が詰まっている。すごい。
genOperというのに色々な情報がある。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/23a70b53-1035-d783-30ce-3ec6fc2531a4.png)
とりあえず、サンプラーで言う所の、ゾーンの情報とキーレンジがとれればといったところ。

'keyRange' 'sampleID'が大事そう。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/3a1c6dbd-7553-52ec-09d4-46068e79f299.png)

'ibag'で値の範囲（ゾーン）が決まっているっぽい。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/8eae6925-fed4-f923-69db-93bff2571534.png)

#構造解析
phdrが複数のpbagが複数のpgenがinstが複数のibagが複数のigen
を参照している構造っぽい。

#ループの短さに注意

SoundFontはおそらくメモリー上のサンプラーなので、波形データは極力短いものとなっている。このループを処理可能なハードウェアがあればいいのだが、現在のコンピュータは持ち合わせておらず、CPUを使って処理する。

ADX2LEはメモリ再生であっても、基本ストリームしながら再生が行われる都合でとても短いループは処理として難しい。
（ループ部分をバッファ分メモリ展開するために、何かしらシーク位置が変化するが、それが毎フレーム内に複数起こるような状況は負荷変動的に好ましくない。）

ピッチを上げた場合などにサンプル数が枯渇してそれを埋める特別な処理も負荷がかかる。
そのまま変換すると再生時にエラーになる。

リアルタイムでがんばるよりデータ側で工夫をして処理負荷を下げる方向として、
ループ範囲が少ないものは適当に32回とかループを展開してみた。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/c24e7aa3-bed5-3440-b46e-0eee0f882404.png)

#ゾーン情報生成

キーの範囲、ループかどうか、基準のキー、どの波形を参照するかといった情報。
キースプリットで波形が変わる感じ。
これらをうりゃっと取り出す。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/53b49d7d-f591-92f9-8bd6-ef7cd37ff07e.png)

#ワークユニット作成

ワークユニットとキューシート、マテリアル情報ファイルも
うりゃっと作成

#だいたいできた。

これ以上やるとエターナる気もしてきたので、中途半端だけど公開しておきます。


#やり残していること

エンベロープ、VelocityRange、フィルター
とかもう少し手を加えると変換できそうな気がする。

#使い方

Assetsの一つ上の階層にSFというフォルダを用意し、
変換元のSF2ファイルを置いておく。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/aa9439c6-efa5-c647-03f6-1fc20b668905.png)

カメラに[SF2ADX2LE]コンポーネントをつけて
input SF Path に対象の名前を入れて、実行。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/fee5673d-4344-bd72-4188-6eb696da8400.png)

しばらくすると[Make Atom Craft Data Finish!]とログにでてくれば成功。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/c9fc761d-148e-951f-bc92-bd9310a65b3a.png)

SFフォルダにワークユニットフォルダが出来上がる。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/02f9dfcc-d233-aee4-dae7-d5185ec291f1.png)

ワークユニットフォルダを「ワークユニット」へドロップ
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/e8bc4ad8-2ccb-1cfb-022d-69387d940102.png)


#さいごに
Unityでバイナリのチャンク構造を解析したコンバータを作ってみたい人
Unityでループ波形ファイルを生成してみたい人
UnityでADX2LEファイルを生成してみたい人
SoundFontを解析したい人
の何かしらの参考になれば幸い