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