前回、Ogg Vorbisファイル(ストリームになっていれば、ファイルじゃなくてもいいけれど)を開いて、正しく閉じるところまで実装しました。今回は、デコードの前に、他のインフラを整えます。つまり、フォーマットを取得したり、位置を取得・設定できるようにします。基本的な部分は前回実装してしまったので、今回は楽です。書く方にとっても。
まずはフォーマットを取得しましょう。ここでいうフォーマットというのはストリームのデータがOgg Vorbisかどうか、ということではなくて、ウェーブフォーマットのことです。Managed DirectXでは、Microsoft.DirectX.DirectSound.WaveFormat構造体に当たります。この構造体はセカンダリバッファを作るときに必要になります。また、データの長さも必要になるでしょう。
using Microsoft.DirectX.DirectSound;
public class OggVorbisDecoder : IDisposable {
// ……
private WaveFormat format;
public WaveFormat Format {
get {
return this.format;
}
}
private long dataLength;
public long Length {
get {
return this.dataLength;
}
}
// ……
}
と、外部から見えるようにしておきます。実際にthis.formatやthis.dataLengthの内容を書き換えるのは、OggVorbisDecoder.OpenOggVorbisの中にします。
private void OpenOggVorbis() {
// ……Functions.OpenCallbacksでファイルを開く処理(前回説明)
IntPtr information;
information = Functions.GetInformation( this.oggVorbisFile, -1 );
if ( information == IntPtr.Zero )
throw new InvalidOperationException();
this.format.FormatTag = WaveFormatTag.Pcm;
this.format.Channels = (short)Marshal.ReadInt32( information, 4 );
this.format.BitsPerSample = 16;
this.format.BlockAlign = (short)( 2 * this.format.Channels );
this.format.SamplesPerSecond = Marshal.ReadInt32( information, 8 );
this.format.AverageBytesPerSecond =
this.format.BlockAlign * this.format.SamplesPerSecond;
this.dataLength = Functions.PCMTotal( this.oggVorbisFile, -1 );
if ( this.dataLength < 0 )
throw new InvalidOperationException();
return;
}
フォーマットはov_infoに相当するFunctions.GetInformationで取得できます。ここで失敗するとNULL(=0)を返すので一応例外を投げるようにしておきます。
実は、WaveFormat構造体にセットする必要がある値のうち、vorbis_info構造体には2つしか含まれていません。他の値は自分で適宜設定しなければなりません。
- FormatTag
- PCMデータであることを示す
WaveFormatTag.Pcmを代入します。ov_readもPCMデータを返しますし、DirectSoundではPCM以外扱えません。 - Channels
- チャネル数を代入します。大抵1(モノラル)か2(ステレオ)です。これは、vorbis_info構造体の2番目のメンバ(int)に入っています。これは先頭から4バイトの位置にあるので、
Marshal.ReadInt32( information, 4 )で値を取得してshortにキャストして代入します。 - BitsPerSample
- サンプリングサイズをビット単位で設定します。Ogg Vorbisの場合、デコード時に決められるので、16にしておきます。
- BlockAlign
- 全てのチャネルを合わせた1サンプル(ブロック)のバイト数を代入します。16ビット(2バイト)にチャネル数をかければいいでしょう。
- SamplesPerSecond
- サンプリング周波数です。これはvorbis_info構造体の3番目のメンバ(int)に入っているので、informationから8バイトの所にある値を代入します。
- AverageBytesPerSecond
- PCMの場合、単に一秒当たりのバイト数なので、BlockAlign * SamplesPerSecondと等価です。
フォーマットの設定が終わったら、データの長さも取得しておきます。Functions.PCMTotalはサンプル単位でデータ長を返してくれるので、それをそのままthis.dataLengthに入れます。負の値が返されたときは例によって例外を。
フォーマットとデータ長は用意ができたので、最後に位置の取得と設定を行うプロパティを作ります。
public long Position {
get {
long currentSampleIndex;
currentSampleIndex = OggVorbis.PCMTell( this.oggVorbisFile );
if ( currentSampleIndex < 0 )
throw new InvalidOperationException();
return currentSampleIndex;
}
set {
if ( value < 0 )
value = 0;
else if ( value > this.dataLength )
value = this.dataLength;
if ( OggVorbis.PCMSeek( this.oggVorbisFile, value ) < 0 )
throw new InvalidOperationException();
return;
}
}
データ長もサンプル単位で行ったので、ここでもPCMデータのサンプル単位で処理を行います。エラーが起きると何れも負の値を返すので、例外を投げましょう。
これで準備は終わりです。次は実際にデコードする関数を定義します。

コメントする