MATLABでMIDI音源を鳴らす

# はじめに

WindowsのMIDI APIを使うと、c/c++言語プログラムでMIDI音源を制御することができます。また、MATLABのMEX機能を用いることで、C/C++言語で記述されたプログラムを、MATLAB環境に取り込むことができます。ここでは、Visual Studio環境でMIDI出力をテストし、MATLAB環境へ移植する手順を概説します。

# Visual Studio環境でテスト
## 使用コマンド、用語など

以下のプログラムをVisual Studio環境でコンパイルし、動作を確認します。以下は、プログラム内のコマンド、用語のポイント。

- **mmsystem.h**: MIDIコマンドに関する情報の格納されたヘッダファイル。
- **argv[]** : コマンドライン入力字に文字列が格納される配列。
- **HMIDIOUT**: MIDIハンドルの定義。
- **midiOutOpen**: MIDIデバイスのオープン。第一引数はMIDIハンドルのポインタ、第二引数はデバイスID。MIDI_MAPPERは、"デフォルトのデバイス"の意味。
- **midiOutClose**: MIDIデバイスのクローズ。引数はMIDIハンドル。
- **midiOutShortMsg**: MIDIメッセージの送信。第一引数はMIDIハンドル、第二引数は**MIDIメッセージ**
- **MIDIメッセージ**: 4バイト信号を16進数で表現。最下位（一番右）1バイトが**ステータスバイト**。
- **ステータスバイト**: メッセージの種別を示す。ここでは以下の三種類を使用
    - **0x90**: ノートオン（発音の開始）。上位バイトで音程、さらに上位バイトで音量情報を与える。
    - **0x80**: ノートオフ（発音の終了）。上位バイトで、該当する音程情報を与える。
    - **0xC0**: プログラムチェンジ。上位バイトで、音色番号を与える。

##Cプログラム

	#include <stdio.h>
	#include "stdafx.h"
	#include <Windows.h>
	#include <mmsystem.h>
	#pragma comment(lib, "winmm.lib")

 	int main(int argc, char *argv[])
 	{
		HMIDIOUT hmo;	// MIDI out handle

		int pitch;	// 音程(0～127: C-1～G9)
		int duration;	// 音の長さ(msec)
		int velocity;	// ベロシティ（音量）
		int timbre;	// 音色

		pitch	 = atoi(argv[1]); //コマンドライン入力の文字列をinteger型へ変換
		duration = atoi(argv[2]);
		velocity = atoi(argv[3]);
		timbre   = atoi(argv[4]);

		midiOutOpen(&hmo, MIDI_MAPPER, 0, 0, 0); // MIDIデバイスオープン
		midiOutShortMsg(hmo, 0xC0 | (timbre << 8)); // 0xC*: プログラムチェンジ

		midiOutShortMsg(hmo, 0x90 | (velocity << 16) | (pitch << 8)); // 0x90: ノートオン
		Sleep(duration);
		midiOutShortMsg(hmo, 0x80 | (pitch << 8));     // 0x80: ノートオフ
	
		midiOutClose(hmo);                             // MIDIデバイスクローズ
 	}

# テスト
Visual Studio内でコマンドライン引数を与えて評価する場合は、
「Solution Explorerペインでプロジェクト名を右クリック」
　　⇒ Propertiesを選択
　　⇒ Debugging
　　⇒ Command Argumentsに、引数を記入（複数ある場合は、スペースで区切る）
ここでは、以下の引数をテストしています。

- 第一引数：60（音程：C4、ド）
- 第二引数：1000（長さ：1000[msec]発音）
- 第三引数：100（音量：maxは127）
- 第四引数：1（音色：GM音源では1番はグランドピアノ）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/196112/1e153050-5003-1b47-f1f1-b998af697fe0.png)

Visual Studio環境でテスト後、DOS窓等で実行する場合は、実行ファイル名の後、スペース区切りで引数を羅列して入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/196112/0a0e49f6-0706-2e98-6dac-2d66ff017d6c.png)

## MATLAB環境へ移植
## コマンド・用語等

以下のプログラム例の様に、mexFunction（ゲートウェイルーチン）を介すことで、C/C++言語で記述されたプログラムを、MATLABで実行できる形式にコンパイルすることができます。以下は、プログラム内のコマンド、用語のポイント

- **miditest()**: 実際の処理部（オリジナルのプログラムに該当）。コマンドライン引数の受け渡しに関連する部分は削除し、MATLAB環境で受け取る形式に修正。それ以外は変更なし。
- **mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])**: MATLABと上記"実際の処理部"との間を取り持つゲートウェイルーチン。お約束なので、このまま使用。
- **mxGetScalar(prhs[n])**: n番目の右辺入力引数を取得。 

# MATLAB環境でコンパイルするために修正したプログラム

	#include "mex.h"
	#include <stdio.h>
	#include <Windows.h>
	#include <mmsystem.h>

	#pragma comment(lib, "winmm.lib")

	int miditest(int pitch, int duration, int velocity, int timbre)
	{
		HMIDIOUT hmo;     // MIDI out handle

		midiOutOpen(&hmo, MIDI_MAPPER, 0, 0, 0);    // open MIDI out device
		midiOutShortMsg(hmo, 0xC0 | (timbre << 8)); // 0xC*: program change

		midiOutShortMsg(hmo, 0x90 | (velocity << 16 | pitch << 8)); // 0x90: note on
		Sleep(duration);                                                         
		midiOutShortMsg(hmo, 0x80 | (pitch << 8));   // 0x80: note off
		midiOutClose(hmo);                           // close MIDI out device

		return 0;
	}

	void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
	{
    	int pitch, duration, velocity, timbre;
    
    	pitch		= mxGetScalar(prhs[0]); //右辺入力取得（0番目）
    	duration	= mxGetScalar(prhs[1]); //右辺入力取得（1番目）
    	velocity	= mxGetScalar(prhs[2]); //右辺入力取得（2番目）
    	timbre		= mxGetScalar(prhs[3]); //右辺入力取得（3番目） 
    
    	miditest(pitch, duration, velocity, timbre);
	}
#テスト
MATLABのコマンドウィンドウで、以下の様にmexコマンドを実行し、上記プログラムをコンパイルします。

　>>mex filename.cpp

ビルドに成功すると、"MEXは正常に完了しました"というメッセージが表示され、Cコードと同じディレクトリに、"mexw64"という拡張子を持つファイル（一般に、"MEXファイル"と呼ばれる）が現れます。後は、MATLABのコマンドウィンドウで、以下の様にMEXファイルに必要な引数を与えることで、MATLAB環境からMIDI音源を鳴らすことができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/196112/b0198c2c-3e14-6167-c2dc-cc7999a2afeb.png)

##まとめ

Windows環境において、非常に短い記述でMIDI音源を鳴らすことができました。またMATLABへの移植も大きな修正は不要です。ここでの例は非常にシンプルですが、同様の手順で、複数音源の同時再生や、MIDIファイルを読み込んで再生することも可能でしょう。