ここで必要になる構造体の最後は、ov_callbacksに相当するOggVorbisCallbacksです。これをどう定義すればいいかを知る為に、include\vorbis\vorbisfile.hを見てみると
typedef struct { size_t ( *read_func )( void* ptr, size_t size, size_t nmemb, void* datasource ); int ( *seek_func )( void* datasource, ogg_int64_t offset, int whence ); int ( *close_func )( void* datasource ); long ( *tell_func )( void* datasource ); } ov_callbacks;
とあります。関数ポインタです。C/C++の関数ポインタに相当するC#の言語機構はデリゲートですから、これらに対応するデリゲートを定義します:
[StructLayout( LayoutKind.Sequential )] public struct OggVorbisCallbacks { public ReadFunction readFunction; public SeekFunction seekFunction; public CloseFunction closeFunctrion; public TellFunction tellFunction; } } public delegate int ReadFunction( IntPtr destination, int size, int count, IntPtr dataSource ); public delegate int SeekFunction( IntPtr dataSource, long offset, int whence ); public delegate int CloseFunction( IntPtr dataSource ); public delegate int TellFunction( IntPtr dataSource );
C/C++のsize_tは常に4バイトではありませんが、64ビット環境で8バイトになる基本型がポインタ系しか無いので、とりあえずintで代用しています。64ビット環境の場合はReadFunctionの戻り値はlongに変えて下さい。或いは、気にならないのであればIntPtrを使えば放っておけます。
デリゲートは関数ポインタに似た機構ですが、インスタンスメソッド(this参照を必要とするメソッド)も使えたり、一回のデリゲート呼び出しで複数のメソッドを順番に呼ぶことも可能です。しかし、OggVorbis.dll内部ではこれを__stdcallの関数として扱う為、そのようなデリゲート特有の機能は使えません。staticなメソッドを一つだけ入れて渡す必要があります。
さて、これで外部関数を可視化するのに必要なものは集まりました。SetError、ov_clear以外の関数のプロトタイプを見てみます。
extern int ov_open_callbacks( void* datasource, OggVorbis_File* vf, char* initial, long ibytes, ov_callbacks callbacks); extern ogg_int64_t ov_pcm_total( OggVorbis_File* vf, int i ); extern int ov_pcm_seek( OggVorbis_File* vf, ogg_int64_t pos ); extern ogg_int64_t ov_pcm_tell( OggVorbis_File* vf ); extern vorbis_info* ov_info( OggVorbis_File* vf, int link ); extern long ov_read( OggVorbis_File* vf, char* buffer, int length, int bigendianp, int word, int sgned, int* bitstream );
これを、C#のOggvorbis.Functionsの中で可視化します。前述のSetErrorやov_clearも含めて書くと、Functionsクラスは次のようになります。
public class Functions { [DllImport( "OggVorbis.dll", EntryPoint = "ov_open_callbacks" )] public static extern int OpenCallbacks( IntPtr dataSource, [In, Out] OggVorbisFile oggVorbisFile, IntPtr initial, int initialBytes, OggVorbisCallbacks callbacks ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_clear" )] public static extern int Clear( [In, Out] OggVorbisFile oggVorbisFile ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_pcm_total" )] public static extern long PCMTotal( [In, Out] OggVorbisFile oggVorbisFile, int bitStream ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_pcm_seek" )] public static extern int PCMSeek( [In, Out] OggVorbisFile oggVorbisFile, long position ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_pcm_tell" )] public static extern long PCMTell( [In, Out] OggVorbisFile oggVorbisFile ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_info" )] public static extern IntPtr GetInformation( [In, Out] OggVorbisFile oggVorbisFile, int bitStream ); [DllImport( "OggVorbis.dll", EntryPoint = "ov_read" )] public static extern int Decode( [In, Out] OggVorbisFile oggVorbisFile, IntPtr destination, int length, int bigEndian, int wordSize, int signed, ref int bitStream ); [DllImport( "OggVorbis.dll", EntryPoint = "SetError" )] public static extern void SetError( int errorOccurred ); }
大体はそのまんまです。ov_infoに相当するGetInformationのみ戻り値の型がIntPtrに変わっていますが、これはクラスへの参照を戻り値として取る関数が定義できなかったからです(知らないだけかも)。勿論、IntPtrを使ってアンマネージドデータの内容を読み取ることは可能ですので、心配する必要はありません。
これでようやく下準備は終わりです。結構長かったですね。次回はこれらの関数を実際に呼び出して使ってみます。
コメントする