カクタスソフトウェア
カクタスソフトウェア
サウンド MIDI マルチメディア アプリケーション

バッファ更新のメカニズム

リングバッファの考え方については、セクション「アンマネージ型DirectSound」を参照して下さい。

リングバッファによる運用

以下にリングバッファによる運用の様子を示します。

リングバッファによる運用にあたっては、更新領域と保護された領域が重ならないようにしなければなりません。つまり以下の条件を満たす必要があります。

再生位置・書き込み位置の取得には、SecondaryBuffer.GetCurrentPosition()関数を用います。

public void GetCurrentPosition(
    out int currentPlayPosition,
    out int currentWritePosition);

currentPlayPositionにサウンドの再生位置が、currentWritePositionにサウンドカードへの書き込み位置が戻ります。これらの数値は、バッファのつなぎ目からのオフセットであり、単位はバイトです。

ここで次の変数と判定関数を用意します。

int WriteOffset;    // 更新開始位置
int NotifySize;     // 更新領域サイズ
int BufferSize;     // 全バッファサイズ
int playPos;
int writePos;
SoundBuffer.GetCurrentPosition(out playPos, out writePos);

bool check = CheckPositionSafe(
    playPos,
    (playPos <= writePos) ? writePos : writePos + BufferSize,
    WriteOffset,
    WriteOffset + NotifySize);

次の関数は、4つのオフセットが正しい位置関係にあるかどうかを判定します。領域が不正の場合、関数はfalseを戻します。

private bool CheckPositionSafe(int r1, int w1, int r2, int w2)
{
    return (w1 < r2) ^ (w2 < r1);
}

適当な時間間隔でCheckPositionSafe()関数をコールし、trueが戻ったらバッファの更新処理を行います。

サービス関数の呼び出し

セカンダリーバッファを更新するために、定期的にサービス関数を呼び出します。タイミングの作成はいくつかの方法があり、状況に応じて適当な方法を選びます。詳しい解説はセクション「タイミングを作る」を参照して下さい。

System.Windows.Forms.Timer

メインプログラムがウィンドウを持っている場合、タイマーイベントを用いて更新関数を呼び出します。

System.Threading.Timer

TimerCallbackデリゲートを用いて更新関数を定期的に呼び出します。

サブスレッドでSleep()を用いる

作業用スレッドを作成し、内部に永久ループを作成します。Sleep()関数で時間を調節しながら更新関数を呼び出します。

セカンダリーバッファの設計については、セクション「アンマネージ型DirectSound」を参照して下さい。

セカンダリーバッファの初期化

カレント位置の初期化

スタートコマンドを実行したとき、セカンダリーバッファのどの位置から再生を始めるのかを設定します。設定はバッファ先頭からのオフセットで、単位はバイトです。

SoundBuffer.SetCurrentPosition(0);

サウンドデータのロード

演奏に先立ってセカンダリーバッファにウェーブファイルの先頭部分をロードします。

WaveInstance.Seek(0, SeekOrigin.Begin);
SoundBuffer.Write(0, WaveInstance, BufferSize, LockFlag.None);

WaveInstanceは、System.IO.Streamクラスを継承したサウンドデータ供給用のサービスクラスです。Seekメソッドを用いて曲の先頭にシークしています。

セカンダリーバッファへのデータ供給は、Streamクラスを通して行います。Streamクラスの実装法については、セクション「ウェーブデータの供給」を参照して下さい。

サウンドデータの書き込みは、Writeメソッドを用います。データのやり取りは、Streamクラスが行うためこの場でコピーなどの操作は必要ありません。

public void Write(
    int         bufferStartingLocation,
    Stream      data,
    int         numberBytesToWrite,
    LockFlag    flag);

初期化が終わったら演奏を開始し、後は定期的にセカンダリーバッファの更新処理を行います。

セカンダリーバッファの更新

SoundBuffer.Write(WriteOffset, WaveInstance, NotifySize, LockFlag.None);

そして次の更新に備え書き込みポインターを進めます。書き込みポインターなどの詳細は「再生処理の流れ」を参照して下さい。

WriteOffset = (WriteOffset + NotifySize) % BufferSize;

セカンダリーバッファのリストア

セカンダリーバッファを再生する時は、SoundBuffer.Status.BufferLostフラグが立っていないかチェックしなければなりません。もしバッファが失われた状態になっているときは復帰処理を行います。以下のコードは、復帰処理のサンプルです。

    bool wasRestored = false;

    while (SoundBuffer.Status.BufferLost == true)
    {
        SoundBuffer.Restore();
        Thread.Sleep(10);
        wasRestored = true;
    }

リストアが終了したら、バッファにサウンドデータを読み込みます。

ドキュメントの先頭へ

カクタスソフトウェア 技術協力 資料室 資料室の広場 SourceForge.jp お問い合わせ