MIDI IN/OUT コンソールアプリ

今さら感がとっても強いですが、MIDIデータを送受信するコンソールアプリを作成します。WINDOWSのコンソールで動作します。環境はCのコンパイラとしてMinGWを使います。MinGWはGNUツールチェーンのWindows移植版です。(wiki参照)MinGWのインストールは後述します。

MIDI送受信のAPIはmmsystem.hに定義があります。また、ライブラリはlibwinmm.aを使います。

MIDIを受信するプログラム

以下MIDIを受信するプログラム、midiintest.cppプログラムを紹介します。

/* *************************************************************************
	midiintest.cpp : MIDI入力テスト用のコンソールアプリケーション

	This software is released under the MIT License, see LICENSE.txt.
	Copyright (c) 2013 Mikata Hara
*************************************************************************	*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <mmsystem.h>

/*	MIDIを受信した時のコールバック関数			*/
/*	HMIDIIN inHandle	; MIDI受信のハンドル		*/
/*	UINT uMsg		: メッセージ			*/
/*	DWORD dwParam1	: MIDI Data				*/
/*	DWORD dwParam2	: Time Stamp				*/

HMIDIIN      inHandle; 	//MIDI入力を受信するためのMIDIデバイスインターフェイス

void CALLBACK midiCallback(HMIDIIN handle, UINT uMsg, DWORD dwInstance,
	DWORD dwParam1, DWORD dwParam2)
{
	switch ( uMsg ) {
		case MM_MIM_DATA:	/* MIDI Short Message */
			fprintf(stderr,"-----APPARENTLY THERE IS DATA.-----\n");

		/* MIDIチャンネルメッセージの処理プログラムはここに記載する	*/
			if( dwParam1!=0xF8 && dwParam1!= 0xFE ){
				fprintf(stderr,"dwInstance is %08lx\n", dwInstance);  
				fprintf(stderr,"Handle is %08lx\n", handle);
				fprintf(stderr,"uMsg is %08lx\n", uMsg);
				fprintf(stderr,"dwParam1 is %08x\n",dwParam1);
				fprintf(stderr,"dwParam2 is %08x\n",dwParam2);
	 		}
			break;


		case MM_MIM_OPEN:
			fprintf(stderr,"-----OPENED.-----\n");
			break;

		case MM_MIM_CLOSE:
			fprintf(stderr,"-----EVERYTHING IS CLOSING.-----\n");
			break;
		
		case MM_MIM_LONGDATA:
			fprintf(stderr,"-----LONGDATA'D.-----\n");
			break;

		case MM_MIM_ERROR:
			fprintf(stderr,"-----ERROR.-----\n");
			break;

		case MM_MIM_LONGERROR:
			fprintf(stderr,"-----LONGERROR.  EVEN WORSE.-----\n");
			break;
    }
}

void MidiInInit(){

	MIDIINCAPS     mic;       
	unsigned long result;
	int ckey;	// 現在押されているキーボードキーのストレージ
	unsigned int iNumDevs= midiInGetNumDevs();  // MIDI入力デバイスの数
	unsigned int iDevNum=0; 

    /* 入力デバイスの名前を表示する */
	for (int i = 0; i < iNumDevs; i++)
	{
		/* 次のデバイスに関する情報を取得する */
		if (!midiInGetDevCaps(i, &mic, sizeof(MIDIINCAPS)))
		{
			/* デバイスIDと名前を表示します */
			fprintf(stderr,"Device ID #%u: %s\r\n", i, mic.szPname);
		}
	}
 
	/* 入力デバイスの選択 */
	fprintf(stderr,"These are the only available devices...?\n"); 
	fprintf(stderr,"Device No.=? ");
	scanf("%u",&iDevNum);
 
    /* 入力デバイスを開く 		*/
	/* コールバック関数を登録 	*/
	result = midiInOpen(&inHandle, iDevNum, (DWORD)midiCallback, 0, CALLBACK_FUNCTION);
     
	if (result)
	{
		fprintf(stderr,"There was an error opening the default MIDI In device!\r\n");
	} else {
		fprintf(stderr,"midiInStart has been called.\n"); 
		midiInStart(inHandle);	//MIDI入力開始
	}
}

void MIDIInClose()
{
	midiInStop(inHandle);
//	midiInReset(inHandle);
	midiInClose(inHandle);
 
	fprintf(stderr,"Lines are done twice because midiCallback\n");
	fprintf(stderr,"is called when midiInClose is called...?\n");
	fprintf(stderr,"%d was the MIDIIN handle.\n",inHandle); 
	fprintf(stderr,"Stuff's closed now.\n");     
}

int main(int argc, char *argv[])
{
	MidiInInit();
	while(!kbhit());	//何かキーが押されるまで続ける
	MIDIInClose();
	return EXIT_SUCCESS;
}

アプリケーションをコンパイルします。MinGWはCドライブの直下にインストールしたとします。

> gcc -o midiintest midiintest.c c:\MinGW\lib\libwinmm.a

midiintestをコマンドラインから起動すると接続されているMIDIデバイスが表示されます。いずれかの番号を選択して下さい。dwParam1にMIDIデータが逆順に表示されます。

C:\Users\user\Documents\midiwork>midiintest
Device ID #0: nanoKEY2
Device ID #1: loopMIDI Port
Device ID #2: loopMIDI Port 1
These are the only available devices...?
Device No.=? 0
-----OPENED.-----
midiInStart has been called.
-----APPARENTLY THERE IS DATA.-----
dwInstance is 00000000
Handle is 0010b398
uMsg is 000003c3
dwParam1 is 00483090 < 90H 30H 48H
dwParam2 is 00000cef
-----APPARENTLY THERE IS DATA.-----
dwInstance is 00000000
Handle is 0010b398
uMsg is 000003c3
dwParam1 is 00403080 < 80H 30H 40H
dwParam2 is 00000d9d

MIDIを送信するプログラム

以下MIDIを送信するプログラム、midiouttest.cppプログラムを紹介します。

/* *************************************************************************
	midiouttest.cpp : MIDI出力テスト用のコンソールアプリケーション

	This software is released under the MIT License, see LICENSE.txt.
	Copyright (c) 2013 Mikata Hara
*************************************************************************	*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <mmsystem.h>

HMIDIOUT outHandle;    // MIDI出力を送信するためのMIDIデバイスインターフェイス

int MidiOutInit(){

	MIDIOUTCAPS     mic;       
	int flag;		// 関数の戻り値 
	int iNumDevs = midiOutGetNumDevs();  //MIDI出力デバイスの数
	unsigned int iDevNum=0;

	/* 出力デバイスの名前を表示する  */
	for (int i = 0; i < iNumDevs; i++)
	{
        /* 次のデバイスに関する情報を取得する */
		if (!midiOutGetDevCapsA(i, &mic, sizeof(MIDIINCAPS)))
		{
            /* デバイスIDと名前を表示します */
			fprintf(stderr,"outHandle ID #%u: %s\r\n", i, mic.szPname);
		}
	}


	/* 出力デバイスの選択 */
	fprintf(stderr,"These are the only available devices...?\n"); 
	fprintf(stderr,"Device No.=? ");
	scanf("%u",&iDevNum);

	// Open the MIDI output port
	flag = midiOutOpen(&outHandle, iDevNum, 0, 0, CALLBACK_NULL);

	if (flag != MMSYSERR_NOERROR) {
		fprintf(stderr,"Error opening MIDI Output.\n");
		return 1;
	}
	return 0;
}

int main(int argc, char** argv) {

	int ckey;           // 現在押されているキーボードキー
	int flag;           // エラーフラグ
	int notestate=0;	// 現在ONかOFFか?
	unsigned char notenum;	//Note Number
	unsigned char Note[8]={48,50,52,53,55,57,59,60};
	int i;

	// 送信用メッセージ
	union { unsigned long word; unsigned char data[4]; } message;
	// Note Onを例とすると以下のように設定する
	message.data[0] = 0x90;  // MIDI note-on message (requires to data bytes)
	message.data[1] = 60;    // MIDI note-on message: Key number (60 = middle C)
	message.data[2] = 100;   // MIDI note-on message: Key velocity (100 = loud)
	message.data[3] = 0;     // 使っていない

	if(MidiOutInit()) return EXIT_FAILURE;	// MIDI出力デバイス初期化

	// "q"が押されるまで続ける
	fprintf(stderr,"Press \"1\", \"2\", \"2\", ..., \"8\" to send MIDI\n");
	fprintf(stderr,"Press \"q\" to quit.\n");

	while (1) {
		if (kbhit()) {	// コンピューターのキーボードのキーが押された場合 
			ckey = getch();
			if (ckey == 'q') break;
			if(ckey<'1' || ckey>'8') continue;
			notenum = Note[ckey-'1'];

			if (notestate == 0) {
			// ノートは現在オフ。ノートをオンにする。
				message.data[2] = 100;	//velocity=100
				message.data[1] = notenum;
				notestate = 1;
				printf("Note turned ON.\r");
			} else {
			//  ノートは現在オン。ノートをオフにする。
				message.data[2] = 0;  //velocity = note off
				message.data[1] = notenum;
				notestate = 0;
				printf("Note turned OFF.\r");
			}

		//MIDIデータ送信
			flag = midiOutShortMsg(outHandle, message.word);
			if (flag != MMSYSERR_NOERROR) {
				printf("Warning: MIDI Output is not open.\n");
				break;
			}
		}
	}

	// 出力デバイスをリセットする
 	midiOutReset(outHandle);

	// MIDIデバイスのデータをすべて削除し、MIDI出力ポートを閉じる
	midiOutClose(outHandle);

	return EXIT_SUCCESS;
}

アプリケーションをコンパイルします。MinGWはCドライブの直下にインストールしたとします。

> gcc -o midiouttest midiouttest.c c:\MinGW\lib\libwinmm.a

midiouttestをコマンドラインから起動すると接続されているMIDIデバイスが表示されます。いずれかの番号を選択して下さい。パソコンのキーボードで"1", "2", ..., "8" を押すとドレミの順でMIDIが送信されます。送信デバイスとしてMicrosoft GS Wavetable Synthを選ぶとパソコンから音が出ます。ただし、このプログラムは和音の処理はやってないので、2以上のキーを押すと音が鳴りっぱなしになることがあります。"q"を押すとアプリケーションを終了します。

C:\Users\user\Documents\midiwork>midiouttest
outHandle ID #0: Microsoft GS Wavetable Synth
outHandle ID #1: loopMIDI Port
outHandle ID #2: loopMIDI Port 1
These are the only available devices...?
Device No.=? 0
Press "1", "2", "2", ..., "8" to send MIDI
Press "q" to quit.
Note turned ON.

MinGW 32ビットインストール

Windows 10にMinGW 32ビットインストールします。GUI first time setup tool (mingw-get-setup)を使ってインストールするのが良いかと思います。

C:\Users\user>gcc --version
gcc (MinGW.org GCC Build-2) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

C:\Users\user>gdb --version
GNU gdb (GDB) 7.6.1
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.