--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Diagnostics;
+
+namespace DTXCreator.MIDIインポート
+{
+ class CMIDIトラック
+ {
+ public string strトラック名;
+ public int nトラック数;
+ public int nデータ長;
+ public byte[] byMIDIトラックバイナリ;
+ public string str解析内容;
+ public CMIDI cMIDI;
+
+ public CMIDIトラック( CMIDI _cMIDI, int _dトラック数, byte[] _byMIDIトラックバイナリ )
+ {
+ this.strトラック名 = "";
+ this.nトラック数 = _dトラック数;
+ this.nデータ長 = 0;
+ this.byMIDIトラックバイナリ = _byMIDIトラックバイナリ;
+ this.cMIDI = _cMIDI;
+ this.str解析内容 = "";
+
+ this.nデータ長 = CMIDI.nBin2Int( this.byMIDIトラックバイナリ, 4, 4 );
+ }
+
+ public void tトラックチャンクを走査する()
+ {
+ int p = 8;
+ UInt32 nデルタタイム合計 = 0;
+ int nイベントbefore = 0;
+
+ while( true )
+ {
+ // デルタタイム計算
+ int nデルタタイムLen;
+ UInt32 deltatime;
+ GetVarLen( p, out nデルタタイムLen, out deltatime );
+ nデルタタイム合計 += deltatime;
+
+ // イベント
+ int nイベント = this.byMIDIトラックバイナリ[p+nデルタタイムLen];
+ int nイベントLen = 3;
+
+ // デルタタイムの直後がイベントじゃなかったら、前のイベントを継ぐ
+ if ( nイベント < 0x80 )
+ {
+ nイベント = nイベントbefore;
+ p -= 1;
+ }
+
+ // 8n - 9n ノートオフ・オン
+ if ( 0x80 <= nイベント && nイベント <= 0x9F )
+ {
+ int nData1 = this.byMIDIトラックバイナリ[p+nデルタタイムLen+1];
+ int nData2 = this.byMIDIトラックバイナリ[p+nデルタタイムLen+2];
+ // ノートオン(9n)の時の値を取得 (ノートオンでも、ベロシティ0の場合はノートオフの意味なので、無視する)
+ if ( nイベント >= 0x90 && nData2 > 0 )
+ {
+ int nチャンネル0to15 = nイベント - 0x90;
+ cMIDI.nチャンネル0to15毎のノート数[nチャンネル0to15] ++;
+ cMIDI.lMIDIイベント.Add( new CMIDINote( nデルタタイム合計, nData1, nData2, nチャンネル0to15 ) );
+
+ if ( (bool)cMIDI.dgvチャンネル一覧.Rows[nチャンネル0to15].Cells[ "Channel_Load" ].Value )
+ cMIDI.nドラムチャンネルのキー毎のノート数[nData1]++;
+
+ }
+ //this.str解析内容 += ((nイベント>=144)?"N-ON ":"N-OFF")+" "+p.ToString().PadLeft(6)+" "+nデルタタイム[0]+","+nData1.ToString("X2")+","+nData2.ToString("X2")+"\r\n";
+
+ nイベントLen = 3;
+ }
+ // A0 - EF コントロールチェンジ等
+ else if ( 0xA0 <= nイベント && nイベント <= 0xEF )
+ {
+ int nData1 = this.byMIDIトラックバイナリ[p+nデルタタイムLen+1];
+ int nData2 = this.byMIDIトラックバイナリ[p+nデルタタイムLen+2];
+
+ nイベントLen = 3;
+ if ( 0xC0 <= nイベント && nイベント < 0xDF ) nイベントLen = 2;
+
+ //this.str解析内容 += "CC / Tick: "+nデルタタイム合計.ToString().PadLeft(6)+" Type: "+nData1.ToString("X2")+"\r\n";
+
+ // 面倒なので、コントロールチェンジが3byteになる場合はとりあえず想定しない。相当レアだし。
+ }
+ // F0/F7 System Exclusive Message
+ else if ( nイベント == 0xF0 || nイベント == 0xF7)
+ {
+ UInt32 nF0F7データLen;
+ int nデータLenのLen;
+ int pt = p + nデルタタイムLen + 1; // F0/F7 の次のバイト(データ長が記載)
+ GetVarLen( p + nデルタタイムLen + 1, out nデータLenのLen, out nF0F7データLen );
+
+ pt += nデータLenのLen;
+
+ tドラムチャンネルかどうか推測する( pt, nデルタタイムLen );
+
+ nイベントLen = 1 + nデータLenのLen + (int)nF0F7データLen; // "F0orF7"(1byte) + 可変長のデータバイト数 + nF0F7データ長(F0の場合はF7込み)
+ string str = CMIDI.strBin2BinStr( this.byMIDIトラックバイナリ, p + nデルタタイムLen, nイベントLen );
+
+ //this.str解析内容 += "Sys / Tick: "+nデルタタイム合計.ToString().PadLeft(6)+" Val : "+str+"\r\n";
+ //Debug.WriteLine( this.str解析内容 += "Sys / Tick: " + nデルタタイム合計.ToString().PadLeft( 6 ) + " Val : " + str );
+
+ }
+ // FF メタイベント
+ else if ( nイベント == 0xFF )
+ {
+ int nType = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 1 ];
+ UInt32 nLen = 0;
+
+ switch ( nType )
+ {
+ // FF 01 - FF 07
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ {
+ int nLenのLen;
+ GetVarLen( p + nデルタタイムLen + 2 , out nLenのLen, out nLen );
+ //nLen = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 2 ];
+ string str1 = CMIDI.strBin2Str( this.byMIDIトラックバイナリ, p + nデルタタイムLen + 2 + nLenのLen, (int)nLen );
+ if ( nType == 0x03 && this.strトラック名 == "" ) this.strトラック名 = str1; // 最初のトラック名以外は捨てる
+ nイベントLen = 2 + nLenのLen + (int)nLen;
+ }
+ break;
+
+ // FF 20 - FF 21
+ case 0x20:
+ case 0x21:
+ nイベントLen = 4;
+ break;
+
+ // FF 2F EOT
+ case 0x2F:
+ nイベントLen = 0;
+ break;
+
+ // FF 51 BPM
+ case 0x51:
+ float fBPM = (float) ( Math.Round( (float) 60.0 * Math.Pow( 10, 6 ) / CMIDI.nBin2Int( this.byMIDIトラックバイナリ, p + nデルタタイムLen + 3, 3 ), 2 ) );
+ if ( nデルタタイム合計 == 0 ) cMIDI.f先頭BPM = fBPM;
+ nイベントLen = 6;
+ cMIDI.lMIDIイベント.Add( new CMIDIBPM( nデルタタイム合計, fBPM ) );
+ cMIDI.nドラムチャンネルのキー毎のノート数[ 128 ]++;
+ break;
+
+ // FF 54 SMPTEオフセット
+ //case 0x54:
+ // nイベントLen = 8;
+ // break;
+
+ // FF 58 拍設定
+ case 0x58:
+ int n分子 = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 3 ];
+ int n分母 = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 4 ];
+ n分母 = (int) Math.Pow( 2, n分母 );
+
+ int nメトロノームクリックtick = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 5 ];
+ int nメトロノームクリック数内32分音符数 = this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 6 ];
+
+ cMIDI.strTimeSignature = CMIDI.strBin2BinStr( this.byMIDIトラックバイナリ, p + nデルタタイムLen + 3, 4 );
+ nイベントLen = 7;
+
+ cMIDI.lMIDIイベント.Add( new CMIDIBARLen( nデルタタイム合計, n分子, n分母 ) );
+ cMIDI.nドラムチャンネルのキー毎のノート数[ 128 ]++;
+ break;
+
+ // FF 59
+ case 0x59:
+ nイベントLen = 5;
+ break;
+ default:
+ nイベントLen = 3 + this.byMIDIトラックバイナリ[ p + nデルタタイムLen + 3 ];
+ break;
+
+ }
+
+ //this.str解析内容 += "Event / Tick: "+nデルタタイム合計.ToString().PadLeft(6)+" Type: "+nType.ToString("X2")+"\r\n";
+ }
+ nイベントbefore = nイベント;
+
+ p += nデルタタイムLen + nイベントLen;
+
+ if ( nイベントLen == 0 ) // EOT
+ {
+ //this.str解析内容 += "End Of Track\r\n";
+ break;
+ }
+ if ( p >= this.nデータ長 + 8 )
+ {
+ //this.str解析内容 += "Pointer Over "+p+"\r\n";
+ break;
+ }
+ }
+ }
+
+ private void GetVarLen( int p, out int nデルタタイムLen, out UInt32 deltatime )
+ {
+ nデルタタイムLen = 0;
+ deltatime = 0;
+ for ( int i = 0; i < 4; i++ ) // デルタタイムは最大4byte
+ {
+ ++nデルタタイムLen;
+ UInt32 b = this.byMIDIトラックバイナリ[ p + i ];
+ deltatime <<= 7;
+ deltatime += ( b & 0x7F ); // 下位7bitのみ使用
+ if ( b < 0x80 ) break; // MSBが0になったらデルタタイム終了
+ }
+ }
+
+ /// <summary>
+ /// ドラムチャンネルを複数使用していると思わるデータについて、ドラムチャンネルと思われるチャンネルを識別する。
+ /// Roland GS音源を使用した曲データで典型的なパターンのみ対応。
+ /// </summary>
+ /// <param name="p"></param>
+ /// <param name="nデルタタイムLen"></param>
+ private void tドラムチャンネルかどうか推測する( int p, int nデルタタイムLen )
+ {
+ int pt = p;
+ if ( this.byMIDIトラックバイナリ[ pt + 0 ] == 0x41 && // Manufacturer ID == Roland
+ // this.byMIDIトラックバイナリ[ pt + 1 ] == 0xxx && // Dev ID == 通常0x10だが0x7F(broadcast)とかもあるかも
+ this.byMIDIトラックバイナリ[ pt + 2 ] == 0x42 && // Model ID == 0x42 (GS Format)
+ this.byMIDIトラックバイナリ[ pt + 3 ] == 0x12 ) // Command ID == 0x12 (Data Set 1)
+ {
+ if ( this.byMIDIトラックバイナリ[ pt + 4 ] == 0x40 && // USE FOR RHYTHM PART
+ ( this.byMIDIトラックバイナリ[ pt + 5 ] & 0xF0 ) == 0x10 && //
+ this.byMIDIトラックバイナリ[ pt + 6 ] == 0x15 ) //
+ {
+ if ( this.byMIDIトラックバイナリ[ pt + 7 ] == 0x01 || // 01=MAP1(Drum Part),
+ this.byMIDIトラックバイナリ[ pt + 7 ] == 0x02 ) // 02=MAP2(Drum Part)
+ {
+ int ch = this.byMIDIトラックバイナリ[ pt + 5 ] & 0x0F;
+ cMIDI.bドラムチャンネルと思われる[ ch ] = true;
+//Debug.WriteLine( "USE FOR RHYTHM PART: ch" + ch + "="+this.byMIDIトラックバイナリ[pt+7] );
+ }
+ else
+ if ( this.byMIDIトラックバイナリ[ pt + 7 ] == 0x00 ) // 00:OFF(Normal Part)
+ {
+ int ch = this.byMIDIトラックバイナリ[ pt + 5 ] & 0x0F;
+ cMIDI.bドラムチャンネルと思われる[ ch ] = false;
+//Debug.WriteLine( "USE FOR RHYTHM PART: ch" + ch + "="+this.byMIDIトラックバイナリ[pt+7] );
+ }
+ }
+ if ( this.byMIDIトラックバイナリ[ pt + 4 ] == 0x40 && // Rx CHANNEL
+ ( this.byMIDIトラックバイナリ[ pt + 5 ] & 0xF0 ) == 0x10 && //
+ this.byMIDIトラックバイナリ[ pt + 6 ] == 0x02 ) //
+ {
+ int org = this.byMIDIトラックバイナリ[ pt + 5 ] & 0x0F; //
+ int target = this.byMIDIトラックバイナリ[ pt + 7 ]; //
+ if (cMIDI.bドラムチャンネルと思われる[org] == true )
+ {
+ cMIDI.bドラムチャンネルと思われる[ target ] = true;
+Debug.WriteLine( "Rx CHANNEL: chorg" + org + ", chTarget=" + target );
+ }
+ }
+ }
+ }
+ }
+
+}