OSDN Git Service

DTXManiaソリューション、DTXManiaプロジェクト、DTXCreatorプロジェクト、FDKプロジェクトについて英語化。
[dtxmania/dtxmania.git] / DTXCreator / コード / 07.MIDIインポート / CMIDIトラック.cs
diff --git a/DTXCreator/コード/07.MIDIインポート/CMIDIトラック.cs b/DTXCreator/コード/07.MIDIインポート/CMIDIトラック.cs
new file mode 100644 (file)
index 0000000..19253ef
--- /dev/null
@@ -0,0 +1,269 @@
+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 );
+                                       }
+                               }
+                       }
+               }
+    }
+    
+}