AVRマイコン(ATiny85)でformat0のMIDIデータで曲を演奏させたときのメモ。
MIDI formata0
MIDIファイルの形式で演奏データをトラックごとに分けずに全てTrack0に入っている。
読み出しポインタが1つで良いので時間管理が容易でデルタタイムのサイズも小さいのでマイコンでの処理に向く、DOMINOなどのシーケンサソフトのsmfファイル書き出し時にオプションで指定、演奏に不要なデータ(トラック名とか著作権、サポートしていないMIDIイベント)はあらかじめ取り除いておけばサイズが小さくなる。
デルタタイム=前イベントからの時間。
ヘッダ (ヘッダチャンク14byte,トラックチャンク8byte)
最初の14byte+8byte、format0で演奏に使うのはヘッダチャンクの最後の2byteの時間単位(分解能)
ヘッダチャンク
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
タイプ(4byte) | データ長(4byte) | フォーマットの種類 (2byte) | トラック数(2byte) | 分解能(2byte) |
トラックチャンク
1 | 2 |
---|---|
タイプ(4byte) | データ長(4byte) |
分解能は時間の最小単位(1デルタタイム)を4分音符をこの値で割った値で表す、4分音符の長さはメタデータのSetTempoで設定。 |
分解能以外は使わない。
データ
1 | 2 |
---|---|
デルタタイム(可変長) | データ(メタデータ、システムエクルシーブ、MIDIデータ) |
デルタタイム
前のイベントからの待機時間、データ長可変
可変長データ
有効下位7bit,MSBが立っている場合は次データ有り
0x7Fでマスクして7bit左シフトしてMSBが0のデータが来るまで繰り返す。
delta_time = 0;
if( -1 == (sd_c = SMFDATA.smf_getChar())) break;
while(sd_c & 0x80){ //次データあり
delta_time = delta_time|(sd_c & 0x7f);
delta_time <<= 7;
if( -1 == (sd_c = SMFDATA.smf_getChar())) break;
}
delta_time = delta_time | sd_c;
MIDI データ
システムエクルシーブ
0xf0から始まって0xf7で終わる、サポートしないなら元データで消す。
メタイベント
1 | 2 | 3 | 4 |
---|---|---|---|
メタイベント | イベントtype | 後続データ数 | データ |
0xFF | 1byte | 可変 (後続データを127byte以内にすれば1byte) | 後続データ数で示されたデータが続く |
曲を演奏するのに最低必要なのはSetTempo,後は無視しても取りあえずは鳴らせる。
SetTempo (0xFF,0x51,0x03, data1,data2,data3)
4分音符の長さ(uS)、1デルタタイムはこの値を分解能で割った時間。
dat1,dat2,dat3は8bitの値
long Tempo_val;
Tempo_val = dat1;
Tempo_val <<= 8;
Tempo_val |= dat2;
Tempo_val <<= 8;
Tempo_val |= dat3;
基準時間 = (四分音符の長さμS)/(分解能)
基準時間 = Tempo_val/midi_division; (midi_division:分解能)
この基準時間を実際のタイマで何クロック待てば良いかを計算して待つ。
delta_count = delta_time × (Tempo_val/midi_division) / ( 1/Clock)
例)
分解能=240 Tempo=193.5 で Tempo_val が 0x4BB3D(310077)
基準時間は 310077/240 = 1291.9875 us
Clock 32KHz
1clock = 1/32000000 32.25 us
1291/32 ≒ 40
これぐらいの値になるClockにする。
イベント発生時にタイマをクリアして次のイベントはdelta_countまで待てば良い。