DirectX: DirectX Music で小節番号を演奏位置に変換する方法
ここでは、演奏する曲の開始位置を小節番号と拍で指定してしまうコードを紹介します。
DirectX Music には演奏開始位置などに MUSIC_TIME という単位を使用してます。この定義は四分音符一つ分を「DMUS_PPQ (現在 768 に定義されている)」の長さにしているため、テンポ(Tempo)に依存しない時間単位となります。曲データが読み込まれると、曲の先頭の位置を 0 として、音符、調号、拍子記号などの位置は MUSIC_TIME 上に合うように計算されます。したがって、小節位置や拍子数も MUSIC_TIME で表すことが可能となります。
例: 拍子が 4/4 の曲で 2 小節目の 3 拍目(右図の赤い色の位置)は、MUSIC_TIME で表すと ((2 - 1) * 4 + 2) * DMUS_PPQ = 4608 となります。 |
|
複雑な曲になると拍子記号が途中で変わったりします。そこで曲データから拍子記号を取得し、IDirectMusicPerformance(または IDirectMusicPerformance8)のメソッド TimeToRhythm と RhythmToTime を使って位置を計算します。
実際のコードは以下の通りです。
[C++]
// wMeasure: 小節番号(最初を0とする) // bBeat: 拍子数(各小節の頭を0とする) MUSIC_TIME CalcMusicTime(IDirectMusicPerformance* pPerf, IDirectMusicSegment* pSegment, WORD wMeasure, BYTE bBeat) { DMUS_TIMESIGNATURE ts; // 変数 mtTime は曲の頭から数えて現在計算中の位置が // どの辺に当たるかを保持する MUSIC_TIME mtTime, mtNext; HRESULT hr; WORD wM; BYTE bB, bG; short nO; mtTime = 0; // 最初の拍子記号を取得する hr = pSegment->GetParam(GUID_TimeSignature, 0xFFFFFFFF, 0, mtTime, &mtNext, (void*) &ts); if (SUCCEEDED(hr)) { do { // GUID_TimeSignature は ts.mtTime を 0 以下に設定する // (これは GetParam の mtTime パラメータの位置における拍子記号を取得し、 // その拍子記号がどの位置から始まっているかを ts.mtTime に // オフセットとして設定するため) // そこで ts.mtTime を「曲全体における拍子記号の開始位置」とするため // mtTime を値に加える // // ※ 今回は ts.mtTime はほぼ 0 になる ts.mtTime += mtTime; // ts には○分の○拍子の情報が既に入っているため、 // ts.mtTime にある「拍子記号の開始位置」を基点として、 // 「次の拍子記号の位置」が何小節目の何拍目にあるかを取得する // (bG, nO は使用しない) // // ※ mtNext が 0 のときは wM などには 0 が返る hr = m_pDXMusicPerformance->TimeToRhythm(mtTime + mtNext, &ts, &wM, &bB, &bG, &nO); if (FAILED(hr)) break; // wMeasure の値が次の拍子変更位置よりも手前の場合 // → mtTime ~ mtTime + mtNext の間に欲しい値がある if (wMeasure < wM) { OnSetMeasure: // ts.mtTime の位置から wMeasure, bBeat 経過したときの // 位置を、位置 0 を基準にして計算する // (相対位置は mtNext - ts.mtTime になる) hr = m_pDXMusicPerformance->RhythmToTime(wMeasure, bBeat, 0, 0, &ts, &mtNext); if (FAILED(hr)) break; return mtNext; } // 位置が見つかるまでの残り小節数を設定する wMeasure -= wM; // mtNext が 0 のときは「拍子記号がもう存在しない」 // → ts を使って残り小節数が経過したときの位置を計算する if (!mtNext) goto OnSetMeasure; // 計算する位置を次の拍子記号の位置に更新する mtTime += mtNext; // 次の拍子記号を取得する hr = pSegment->GetParam(GUID_TimeSignature, 0xFFFFFFFF, 0, mtTime, &mtNext, (void*) &ts); } while (SUCCEEDED(hr)); } // どこかで処理に失敗 return 0; }
ここで注意したいメソッドは今回のコードの主役でもある TimeToRhythm、そして RhythmToTime です。これらのメソッドでは、DMUS_TIMESIGNATURE 構造体内の mtTime の値を使用して計算するのですが、TimeToRhythm で指定する MUSIC_TIME の値と、RhythmToTime で返される MUSIC_TIME の値はいずれも DMUS_TIMESIGNATURE 構造体の mtTime の値をベースとはせず、その mtTime の値における基準点をそのまま流用しています。つまり、TimeToRhythm で拍子記号の位置を 200、引数として渡す位置を 600 とすると、この位置は拍子記号を基準に取ると 400 という位置になります。
また、ここで扱われる小節番号は拍子記号のある位置を 0 としています。すなわち、曲の先頭から小節番号を(0 をスタートとして)数えていくとき、拍子記号が出るたびに 0 にリセットされる、ということです。
最終更新日: 2007/01/10