C#でOgg Vorbis - その他のインフラ整備

| コメント(0) | トラックバック(0)

概要: 前回、Ogg Vorbisファイル(ストリームになっていれば、ファイルじゃなくてもいいけれど)を開いて、正しく閉じるところまで実装しました。今回は、デコードの前に、他のインフラを整えます。つまり、フォ...

前回、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データのサンプル単位で処理を行います。エラーが起きると何れも負の値を返すので、例外を投げましょう。

これで準備は終わりです。次は実際にデコードする関数を定義します。

トラックバック(0)

トラックバックURL: http://w4ard.s26.xrea.com/program/mt/mt-tb.cgi/9

コメントする

このブログ記事について

このページは、E+Xが2006年10月12日 15:03に書いたブログ記事です。

ひとつ前のブログ記事は「C#でOgg Vorbis - Ogg Vorbisファイルのオープン」です。

次のブログ記事は「C#でOgg Vorbis - デコード」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。