2013年4月19日金曜日

C++? / C#でoggファイルをロードするためのdllをビルド

C#で効果音としてoggファイルを使う方法について検索してみました。 「公式の方法」みたいなものは無いようですね。 自分の用途を考えて一番使い易そうなコードを探すと、こんなのがありました。

ストレージ上のoggファイルを読み込んで、メモリ上に展開します。 展開されたデータはwavファイルの形式になります。 その機能だけのシンプルなコードです。 このdll自体には再生機能はありません。 SoundPlayerクラスで再生します。 読み込んだデータを自前でメモリにキャッシュできるのがありがたいです。

とりあえず32bitでも64bitでも動かしてみたかったので自分でビルドすることにしました。 で、参考サイトにあるコードだとどのバージョンのoggライブラリを使っているのか分かりませんでした。 小さなコードなので参考サイトを元にすれば自分でビルドできるかな? ってことで現行のoggライブラリを使ったビルドをやってみました。

試した環境はコレです。

  • windows7 64bit home
  • Visual Studio 2012 Express

新しい開発環境(Visual Studio 2012 Express)でビルドできるか確認ってのも目的に追加で。

とりあえずプロジェクトのアーカイブを上げときます。

ライセンスとか著作権とかはdo not evilな感じでお願いします。

まずは参考サイトから「Ogg Vorbis Sample Source Code」をダウンロード。 残念ながら、ウェブサイトに載っているコードには数箇所、サイトに載せるときにできたと思われる不具合があります。 アーカイブの方を手に入れましょう。

Visual StudioでC++の「空のプロジェクト」を作成。 参考アーカイブから

  • OggVorbisSample\OggVorbis\OggVorbis\include\oggvorbis.h
  • OggVorbisSample\OggVorbis\OggVorbis\source\oggvorbis.cpp
  • OggVorbisSample\OggVorbis\OggVorbis\OggVorbis.def

をコピーして追加します。

そしてプロジェクトの構成プロパティを次のように設定します。

  • 全般
    • 構成の種類 : ダイナミックリンクライブラリ(.dll)
    • ターゲットの拡張子 : .dll
  • C/C++ : コード生成
    • セキュリティチェック : いいえ(/GS-)
  • リンカー : 入力
    • モジュール定義ファイル : OggLoader.def

そしてhttp://www.xiph.org/ から

  • libogg-1.3.0
  • libvorbis-1.3.3

を持ってきて、ソースコードをプロジェクトに追加します。 どのバージョンか分かるように解凍したフォルダをそのままプロジェクトフォルダにコピー。 ソースコード(.c と .h)のみソリューションエクスプローラから「追加 → 既存の項目」で追加します。

あとは参考サイトにある通り、#includeのパスを書き換えてビルド。 それでもエラーが出たらc言語のソースをコンパイル対象から外してビルドします。 (コンパイル対象から外すのはソリューションエクスプローラでソースを選択 → 「プロジェクトに含まれる」プロパティをFalse。) とりあえずx86構成でdllをビルドしました。

で、C#から呼び出してみると実行時エラーが発生。 「~スタックを不安定に~」とか言われます。 どうやらDllImportでCallingConvention.Cdeclを書かないとダメな模様。 これは.net framework 4.0からの仕様でしたっけ?

[DllImport("OggLoader.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int LoadOgg(string oggFilePath, [In][Out] ref IntPtr wave);

これで使用できました。 ちなみにx86構成じゃないとダメ。 x64とかAnyCpuでは動きませんでした。 (もちろんdll側とcs側の両方をx64にして試してます。) 64bitで動かさなくちゃならないツールなんて作る予定が無いんでどうでもいいことなんですが、ちょっとだけガッカリ感。

まぁ、自分で作った物じゃないから仕方ないですよね。 わがまま禁止です。 x86で使えるってだけで十分だし感謝です。

x64で動かない原因が何だったのかは確かめようともしていません。 コードをチラッと見た感じではoggvorbis.cppの方には64bitで不味くなりそうなところは見当たりませんでした。 いや、c++から離れて久しいので断言はできませんけどね。 調べようとしたらc++再入門とかですごい時間がかかりそうなので割愛。

---------

ちなみに、x64のdllをDllImportするときはextern Cを使わずに、

  • c++側で__declspec(dllexport)の追加。
  • モジュール定義ファイル.defは不要。
  • c#側のDllImportでエントリポイントを書く。

とかするといいとか小耳に挟んだ事はあります。 もちろん未確認。

---------

とりあえず、ちょっとの試行錯誤で動きはしたんですけど、環境とかで書くべきことが抜けている可能性もあります。 例えば、もしかしたらwindows sdk v7.1をインストール済みとかってのが何かに影響しているかも...?