EternalWindows
MIDI / ランニングステータス

前節では、ReadDeltaという自作関数でデルタタイムを取得しました。 今回は、この後に格納されているイベントを取得することにします。 まず、イベントにはMIDIイベント、SysExイベント、メタイベントの3種類あります。 格納されているイベントがどの種類を表すかどうかは、 デルタタイムの後に存在する1バイトを読み取ることで確認でき、 これが0x8n〜0xEnの値ならMIDIイベント、 0xF0ならSysExイベント、0xFFならMIDIイベントになります。 次に、前節で示したReadTrackを簡略化して表示します。

for (;;) {
	ReadDelta(hmmio, &lpEvent->dwDelta); // デルタタイムを読み込む
	
	mmioRead(hmmio, (HPSTR)&lpEvent->state, sizeof(BYTE)); // ステータスバイトを読み込む
	if (!(lpEvent->state & 0x80)) { // ランニングステータスか
		lpEvent->state = statePrev; // 一つ前のイベントのステータスバイトを代入
		mmioSeek(hmmio, -1, SEEK_CUR); // ファイルポインタを一つ戻す
	}
	
	switch (lpEvent->state & 0xF0) { // ステータスバイトを基にどのイベントか判別

	case 0x80:
	case 0x90:
	case 0xA0:
	case 0xB0:
	case 0xE0:
		// MIDIイベント固有の処理を行う
		break;
	case 0xC0:
	case 0xD0:
		// MIDIイベント固有の処理を行う
		break;
	
	case 0xF0:
		if (lpEvent->state == 0xF0) {
			 // SysExイベント固有の処理を行う
		}
		else if (lpEvent->state == 0xFF) {
			 // メタイベント固有の処理を行う
		}
		else
			;

		break;

	default:
		MessageBox(NULL, TEXT("ステータスバイトが不正です。"), NULL, MB_ICONWARNING);
		return FALSE;

	}
	
	statePrev = lpEvent->state; // 次のイベントが前のイベントのステータスバイトを確認できるように保存
	
	// 次のイベントのメモリを確保する処理を行う
}

ReadDeltaの後のmmioReadでイベントの第1バイトを取得します。 ここでは、EVENT構造体のstateで値を受け取っていますが、 このstateというのはステータスバイトを意味しています。 ステータスバイトとは、イベントの第1バイトのことを意味します。 その次のif文は後述するとして、先にswitch文に注目してください。 ここでは、イベントの第1バイトと0xF0の論理積をとることによって下位4ビットを除去し、 イベントがどの種類のものかを確認しようとしています。 既に述べたように、0x8n〜0xEnの値ならMIDIイベント、 0xF0ならSysExイベント、0xFFならMIDIイベントの処理を行うことになります。

イベントの第1バイトはステータスバイトが格納されると述べましたが、 MIDIイベントの場合はこれが省略されることがあります。 これをランニングステータスと呼びます。 この場合、stateにはステータスバイトの後に存在するデータバイトが格納されることになるため、 取得した値がステータスバイトであるかを確認し、 そうでない場合は適切な処理を行う必要があります。

if (!(lpEvent->state & 0x80)) { // ランニングステータスか
	lpEvent->state = statePrev; // 一つ前のイベントのステータスバイトを代入
	mmioSeek(hmmio, -1, SEEK_CUR); // ファイルポインタを一つ戻す
}

ステータスバイトに0x8n〜0xEnの値が格納されるという関係上、 ステータスバイトのMSB(最上位ビット)は必ず1になります。 一方、データバイトのMSBは必ず0になります。 よって、0x80と論理積をとり、その結果が0であるならば、 ランニングステータスによってstateにデータバイトが格納されたことになります。 ランニングステータスのイベントのステータスバイトは、 前のイベントのステータスバイトと同じになるという決まりがあるため、 予め保存しておいたステータスバイトを代入します。 そして、mmioSeekでファイルポインタを戻すことで、 データバイトの読み取りをなかったことにします。 これにより、後続の処理は問題なく成功することになります。


戻る