Platform SDK: DirectX |
ここでは、C++ でのアプリケーション開発について説明する。Visual Basic については、「DirectMusic Visual Basic チュートリアル」を参照すること。
このツールの実際の動作は、IDirectMusicTool::ProcessPMsg から派生したメソッド内で実行される。IDirectMusicTool::GetMediaTypes が返す配列内に列挙されたタイプのあらゆるメッセージは、このメソッドを要求する。
CEchoTool::ProcessPMsg メソッドは、最初にローカル変数の何らかの初期化を行う。
HRESULT STDMETHODCALLTYPE CEchoTool::ProcessPMsg( IDirectMusicPerformance* pPerf, DMUS_PMSG* pMsg) { DMUS_NOTE_PMSG* pNote; DWORD dwCount; DWORD dwEchoNum; MUSIC_TIME mtDelay; // SetEchoNum()と SetDelay()はこれらのメンバ変数を使うので、 // クリティカルセクションを利用してこれらをスレッドセーフにする。 EnterCriticalSection(&m_CrSec); dwEchoNum = m_dwEchoNum; mtDelay = m_mtDelay; LeaveCriticalSection(&m_CrSec);
次にこのメソッドは、メッセージに対して IDirectMusicGraph::StampPMsg メソッドを呼び出す。処理が終わった後でこのメッセージを他のツールに転送しなければならない場合、StampPMsg は成功する (DirectMusic が最終出力ツールを用意しているため、メッセージの転送先となるアプリケーション固有のツールが存在しない場合でも、StampPMsg は成功するはずである)。失敗した場合、メソッドは S_FREE を返し、メッセージは自動的に無視される。
if ((NULL == pMsg->pGraph) || FAILED(pMsg->pGraph->StampPMsg(pMsg))) { return DMUS_S_FREE; }
ここでは CEchoTool がメッセージを処理する。DMUS_PMSGT_NOTE、DMUS_PMSGT_MIDI、または DMUS_PMSGT_PATCH のタイプのメッセージのみを受け取るよう設定されている点に注意すること (これらのタイプは DMUS_PMSGT_TYPES 列挙型の一部である)。
連続するエコーを作成するたびに、ツールは以下のことを行う。
MIDI の音符を扱うサンプルコードを示す。
if( pPMsg->dwType == DMUS_PMSGT_MIDI ) { // MIDI メッセージをエコー チャンネルにコピーする。 for( dwCount = 1; dwCount <= dwEchoNum; dwCount++ ) { DMUS_MIDI_PMSG* pMidi; if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_MIDI_PMSG), (DMUS_PMSG**)&pMidi ))) { // オリジナル メッセージをこのメッセージにコピーする。 memcpy( pMidi, pPMsg, sizeof(DMUS_MIDI_PMSG) ); // オブジェクトへのポインタを保持または保持している // 可能性のあるフィールドの Addref か消去を実行する。 if( pMidi->pTool ) pMidi->pTool->AddRef(); if( pMidi->pGraph ) pMidi->pGraph->AddRef(); pMidi->punkUser = NULL; // メッセージが次の上位グループへ移動するよう、 // P チャンネルを設定する。 pMidi->dwPChannel = pMidi->dwPChannel + (16*dwCount); // エコーされるメッセージの時間への追加を行う。 pMidi->mtTime += (dwCount * mtDelay); // メッセージを追加するので、MUSIC_TIME だけが有効になる。 // REFERENCE_TIME は、SendPMsg()の内部で // 再計算される。 pMidi->dwFlags = DMUS_PMSGF_MUSICTIME; // メッセージを送信する。 pPerf->SendPMsg( (DMUS_PMSG*)pMidi ); } } }
パッチ チェンジもコピーされ、現在使われていないものも含め他のすべてのエコー チャンネルに送信される。そのため、後でエコーを追加した場合でも、正しい音色による演奏が行われる。サンプルの EchoTool アプリケーションでは、Echotool.h で MAX_ECHOES が定義されている点に注意すること。
else if( pPMsg->dwType == DMUS_PMSGT_PATCH ) { for( dwCount = 1; dwCount <= MAX_ECHOES; dwCount++ ) { DMUS_PATCH_PMSG* pPatch; if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_PATCH_PMSG), (DMUS_PMSG**)&pPatch ))) { // オリジナルメッセージをこのメッセージにコピーする。 memcpy( pPatch, pPMsg, sizeof(DMUS_PATCH_PMSG) ); // オブジェクトへのポインタを保持または保持している // 可能性のあるフィールドの Addref か消去を実行する。 if( pPatch->pTool ) pPatch->pTool->AddRef(); if( pPatch->pGraph ) pPatch->pGraph->AddRef(); pPatch->punkUser = NULL; // メッセージが次の上位グループへ移動するよう、 // P チャンネルを設定する。 pPatch->dwPChannel = pPatch->dwPChannel + (16*dwCount); // エコーされるメッセージの時間への追加を行う。 pPatch->mtTime += (dwCount * mtDelay); // メッセージを追加するので、MUSIC_TIME だけが有効になる。 // REFERENCE_TIME は、SendPMsg()の内部で // 再計算される。 pPatch->dwFlags = DMUS_PMSGF_MUSICTIME; // メッセージを送信する。 pPerf->SendPMsg( (DMUS_PMSG*)pPatch ); } } }
このメソッドは、MIDI の音符と同様の方法で音符を扱うが、ボリュームを下げるためのステップを追加する。
else if( pPMsg->dwType == DMUS_PMSGT_NOTE ) { // 次の音符のベロシティを追跡するための変数を作成する。 BYTE bVelocity; pNote = (DMUS_NOTE_PMSG*)pPMsg; bVelocity = pNote->bVelocity; for( dwCount = 1; dwCount <= dwEchoNum; dwCount++ ) { if( SUCCEEDED( pPerf->AllocPMsg( sizeof(DMUS_NOTE_PMSG), (DMUS_PMSG**)&pNote ))) { // オリジナルの音符をこのメッセージにコピーする。 memcpy( pNote, pPMsg, sizeof(DMUS_NOTE_PMSG) ); // オブジェクトへのポインタを保持または保持している // 可能性のあるフィールドの Addref か消去を実行する。 if( pNote->pTool ) pNote->pTool->AddRef(); if( pNote->pGraph ) pNote->pGraph->AddRef(); pNote->punkUser = NULL; // エコーされるメッセージの時間への追加を行う。 pNote->mtTime += (dwCount * mtDelay); // エコーされた音符のボリュームを下げる。 bVelocity = (BYTE) (bVelocity - ((bVelocity * (dwCount * 15))/100)); pNote->bVelocity = bVelocity; // メッセージを追加するので、MUSIC_TIME だけが有効になる。 // REFERENCE_TIME は、SendPMsg()の内部で // 再計算される。 pNote->dwFlags = DMUS_PMSGF_MUSICTIME; pNote->dwPChannel = pNote->dwPChannel + (16*dwCount); // メッセージを送信する。 pPerf->SendPMsg( (DMUS_PMSG*)pNote ); } } }
最後に、ProcessPMsg メソッドは DMUS_S_REQUEUE を返し、オリジナル メッセージはパイプラインへ戻される。
return DMUS_S_REQUEUE; } // CEchoTool::ProcessPMsg()の終わり
次項 : ステップ 5 : クラス メソッドの定義