Esempio n. 1
0
 //テストログ
 static void DataTestLog(HeaderChunkData h)
 {
     Console.WriteLine(
         "チャンクID:" + (char)h.chunkID[0] + (char)h.chunkID[1] + (char)h.chunkID[2] + (char)h.chunkID[3] + "\n" +
         "データ長:" + h.dataLength + "\n" +
         "フォーマット:" + h.format + "\n" +
         "トラック数:" + h.tracks + "\n" +
         "分解能:" + h.timeBase + "\n");
 }
Esempio n. 2
0
        //main--------------------
        public static void ReadMidi(string filePath, int _baseScale, float magniSpeed /*速度倍率*/)
        {
            //リスト生成
            int baseScale = _baseScale; //四分音符の長さ

            var headerData = new HeaderChunkData();

            TrackChunkData[] trackChunks;
            var b_noteDataList = new List <Bfr_NoteData>();
            var b_tempDataList = new List <Bfr_TempData>();

            //ファイル読み込み 読み込み終わるまで出ない!
            using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                using (var reader = new BinaryReader(file))
                {
                    //-------- ヘッダ解析 -------
                    HeaderDataAnaly(ref headerData, reader);

                    //-------- トラック解析 -------
                    trackChunks = new TrackChunkData[headerData.tracks]; //ヘッダからトラック数を参照

                    for (int i = 0; i < trackChunks.Length; i++)         //トラック数分回す
                    {
                        TrackDataAnaly(ref trackChunks, reader, i);

                        //演奏データ解析へ
                        TrackMusicAnaly(trackChunks[i].data, headerData, ref b_noteDataList, ref b_tempDataList);
                    }
                }

            MstimeFix(ref b_noteDataList, ref b_tempDataList); //曲中のBPM変更に対応

            //欲しいデータに変換
            a_noteDataList = new List <Aftr_NoteData>();
            AftrNoteCreate(b_noteDataList, headerData.timeBase, baseScale);

            a_tempDataList = new List <Aftr_TempData>();
            AftrTempCreate(b_tempDataList, baseScale, magniSpeed);

            //以下ログ
            DataTestLog(headerData);
            DataTestLog(trackChunks);
            DataTestLog(b_tempDataList);
            DataTestLog(b_noteDataList);
            DataTestLog(a_noteDataList);
            DataTestLog(a_tempDataList);
        }
Esempio n. 3
0
        //ヘッダー解析
        static void HeaderDataAnaly(ref HeaderChunkData header, BinaryReader reader)
        {
            //チャンクID
            header.chunkID = reader.ReadBytes(4);
            //リトルエンディアンなら逆に
            if (BitConverter.IsLittleEndian)
            {
                //データ長
                var bytePick = reader.ReadBytes(4);
                Array.Reverse(bytePick);
                header.dataLength = BitConverter.ToInt32(bytePick, 0);

                //フォーマット
                bytePick = reader.ReadBytes(2);
                Array.Reverse(bytePick);
                header.format = BitConverter.ToInt16(bytePick, 0);

                //トラック数
                bytePick = reader.ReadBytes(2);
                Array.Reverse(bytePick);
                header.tracks = BitConverter.ToInt16(bytePick, 0);

                //分解能
                bytePick = reader.ReadBytes(2);
                Array.Reverse(bytePick);
                header.timeBase = BitConverter.ToInt16(bytePick, 0);
            }
            else
            {
                //データ長
                header.dataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                //フォーマット
                header.format = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                //トラック数
                header.tracks = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                //分解能
                header.timeBase = BitConverter.ToInt16(reader.ReadBytes(2), 0);
            }
        }
Esempio n. 4
0
        //トラック演奏データ解析
        static void TrackMusicAnaly(byte[] data, HeaderChunkData header, ref List <Bfr_NoteData> b_noteL, ref List <Bfr_TempData> b_tmpL)
        {
            //トラック内で引き継ぎたいもの
            uint tickTime   = 0; //開始からのTick数
            byte statusByte = 0; //FFとか入る
            uint Instrument = 0; //楽器

            //データ分
            for (int i = 0; i < data.Length;)
            {
                //---デルタタイム---
                tickTime += DeltaMath(data, ref i);

                //---ランニングステータス---
                if (data[i] < 0x80)
                {
                    //***
                }
                else
                {
                    statusByte = data[i++];
                }

                //---ステータスバイト---

                //ステバ分岐 この辺はもう筋肉
                //--Midiイベント--
                if (statusByte >= 0x80 & statusByte <= 0xef)
                {
                    switch (statusByte & 0xf0)
                    {
                    case 0x90:                     //ノートオン
                    {
                        byte leanNum  = data[i++]; //音階
                        byte velocity = data[i++]; //音の強さ

                        //ノート情報まとめる
                        Bfr_NoteData noteData = new Bfr_NoteData();
                        noteData.tickTime = (int)tickTime;
                        noteData.msTime   = 0;                 //後でやる
                        noteData.leanNum  = (int)leanNum;
                        noteData.ch       = statusByte & 0x0f; //下4を取得

                        //ベロ値でオンオフを送ってくる奴に対応
                        if (velocity > 0)         //音が鳴っていたらオン
                        {
                            noteData.type = NoteType.ON;
                        }
                        else
                        {
                            noteData.type = NoteType.OFF;
                        }

                        b_noteL.Add(noteData);
                    }
                    break;

                    case 0x80:     //ノートオフ
                    {
                        byte leanNum  = data[i++];
                        byte velocity = data[i++];

                        Bfr_NoteData noteData = new Bfr_NoteData();
                        noteData.tickTime = (int)tickTime;
                        noteData.msTime   = 0;
                        noteData.leanNum  = (int)leanNum;
                        noteData.ch       = statusByte & 0x0f; //下4を取得
                        noteData.type     = NoteType.OFF;      //オフしか来ない

                        b_noteL.Add(noteData);
                    }
                    break;

                    case 0xc0:     //プログラムチェンジ 音色 楽器を変える
                        Instrument = data[i++];
                        break;

                    case 0xa0:     //キープッシャー
                        i += 2;
                        break;

                    case 0xb0:     //コンチェ
                        i += 2;
                        break;

                    case 0xd0:     //チェンネルプレッシャー
                        i += 1;
                        break;

                    case 0xe0:     //ピッチベンド
                        i += 2;
                        break;
                    }
                }

                //--システムエクスクルーシブイベント--
                else if (statusByte == 0x70 || statusByte == 0x7f)
                {
                    byte dataLen = data[i++];
                    i += dataLen;
                }

                //--メタイベ--
                else if (statusByte == 0xff)
                {
                    byte eveNum  = data[i++];
                    byte dataLen = (byte)DeltaMath(data, ref i); //可変長

                    switch (eveNum)
                    {
                    case 0x51:
                    {
                        Bfr_TempData tempData = new Bfr_TempData();
                        tempData.tickTime = (int)tickTime;
                        tempData.msTime   = 0;      //後でやる

                        //3byte固定 4分音符の長さをマイクロ秒で
                        uint temp = 0;
                        temp  |= data[i++];
                        temp <<= 8;
                        temp  |= data[i++];
                        temp <<= 8;
                        temp  |= data[i++];

                        //BPM計算 = 60秒のマクロ秒/4分音符のマイクロ秒
                        tempData.bpm = SECOND_BASE * 1000000 / (float)temp;
                        //小数点第1位切り捨て
                        tempData.bpm = (float)Math.Floor(tempData.bpm * 10) / 10;
                        //tick値=60/分解能*1000
                        tempData.tick = (SECOND_BASE / tempData.bpm / header.timeBase * 1000);
                        b_tmpL.Add(tempData);
                    }
                    break;

                    default:
                        i += dataLen;     //メタはデータ長で全てとばせる 書くの面倒だった
                        break;
                    }
                }
            }
        }
Esempio n. 5
0
    /// <summary>
    /// MIDIファイルをロードし必要な情報を返す
    /// </summary>
    /// <returns>ロードに成功したらそのデータ、失敗したらnull</returns>
    public MIDILoadData Load(string fileName)
    {
        var ret = new MIDILoadData();

        try
        {
            using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                using (var reader = new BinaryReader(stream))
                {
                    /* ヘッダチャンク侵入 */
                    var headerChunk = new HeaderChunkData();

                    // チャンクID読み込み
                    headerChunk.ChunkType = reader.ReadBytes(4);
                    // お前は本当にヘッダチャンクか?
                    if (
                        headerChunk.ChunkType[0] != 'M' ||
                        headerChunk.ChunkType[1] != 'T' ||
                        headerChunk.ChunkType[2] != 'h' ||
                        headerChunk.ChunkType[3] != 'd')
                    {
                        throw new FormatException("head chunk != MThd.");
                    }
                    // 自分のPCがリトルエンディアンなら変換する
                    if (BitConverter.IsLittleEndian)
                    {
                        // ヘッダ部のデータ長(値は6固定)
                        var byteArray = reader.ReadBytes(4);
                        Array.Reverse(byteArray);
                        headerChunk.DataLength = BitConverter.ToInt32(byteArray, 0);
                        // フォーマット(2byte)
                        byteArray = reader.ReadBytes(2);
                        Array.Reverse(byteArray);
                        headerChunk.Format = BitConverter.ToInt16(byteArray, 0);
                        // トラック数(2byte)
                        byteArray = reader.ReadBytes(2);
                        Array.Reverse(byteArray);
                        headerChunk.Tracks = BitConverter.ToInt16(byteArray, 0);
                        // タイムベース(2byte)
                        byteArray = reader.ReadBytes(2);
                        Array.Reverse(byteArray);
                        headerChunk.Division = BitConverter.ToInt16(byteArray, 0);
                    }
                    else
                    {
                        // ヘッダ部のデータ長(値は6固定)
                        headerChunk.DataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                        // フォーマット(2byte)
                        headerChunk.Format = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                        // トラック数(2byte)
                        headerChunk.Tracks = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                        // タイムベース(2byte)
                        headerChunk.Division = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                    }

                    // 分能値保存
                    ret.Division = headerChunk.Division;

                    // トラックが何もなかったら出ていけぇ!
                    if (headerChunk.Tracks <= 0)
                    {
                        throw new Exception("not exsist tracks.");
                    }

                    /* トラックチャンク侵入 */
                    var trackChunks = new TrackChunkData[headerChunk.Tracks];

                    // トラック数ぶん回す
                    for (int i = 0; i < headerChunk.Tracks; i++)
                    {
                        // チャンクID読み込み
                        trackChunks[i].ChunkType = reader.ReadBytes(4);
                        // お前は本当にトラックチャンクか?
                        if (
                            trackChunks[i].ChunkType[0] != 'M' ||
                            trackChunks[i].ChunkType[1] != 'T' ||
                            trackChunks[i].ChunkType[2] != 'r' ||
                            trackChunks[i].ChunkType[3] != 'k')
                        {
                            throw new FormatException("track chunk != MTrk.");
                        }

                        // 自分のPCがリトルエンディアンなら変換する
                        if (BitConverter.IsLittleEndian)
                        {
                            // トラックのデータ長読み込み(値は6固定)
                            var byteArray = reader.ReadBytes(4);
                            Array.Reverse(byteArray);
                            trackChunks[i].DataLength = BitConverter.ToInt32(byteArray, 0);
                        }
                        else
                        {
                            trackChunks[i].DataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                        }

                        // データ部読み込み
                        trackChunks[i].Data = reader.ReadBytes(trackChunks[i].DataLength);

                        // データ部解析
                        TrackDataAnalysis(
                            trackChunks[i].Data,
                            headerChunk.Division,
                            (n, s, b) =>
                        {
                            ret.NoteArray   = n;
                            ret.SoflanArray = s;
                            ret.BeatArray   = b;
                        },
                            () =>
                        {
                            throw new Exception("track data analysis failure.");
                        });
                    }
                }
        }
        catch (Exception e)
        {
            // エラーメッセージ処理
            Debug.LogError("LoadMIDI Error: " + e);
            return(null);
        }

        return(ret);
    }
Esempio n. 6
0
    public void LoadMSF(byte[] file)
    {
        noteList.Clear();
        tempoList.Clear();

        // using(var stream = new FileStream(fileAddres, FileMode.Open, FileAccess.Read))
        // using(var reader = new BinaryReader.ReadBytes(file)){

        var reader = file;

        var headerCH = new HeaderChunkData();

        // チャンクID
        headerCH.chunkID = SubArraybyte(file, 4);


        // 自分のPCがリトルエンディアンならバイト順を逆に
        if (BitConverter.IsLittleEndian)
        {
            // ヘッダ部のデータ長(値は6固定)
            var byteArray = SubArraybyte(file, 4);
            Array.Reverse(byteArray);
            headerCH.dataLength = BitConverter.ToInt32(byteArray, 0);
            // フォーマット(2byte)
            byteArray = SubArraybyte(file, 2);
            Array.Reverse(byteArray);
            headerCH.format = BitConverter.ToInt16(byteArray, 0);
            // トラック数(2byte)
            byteArray = SubArraybyte(file, 2);
            Array.Reverse(byteArray);
            headerCH.tracks = BitConverter.ToInt16(byteArray, 0);
            // タイムベース(2byte)
            byteArray = SubArraybyte(file, 2);
            Array.Reverse(byteArray);
            headerCH.division = BitConverter.ToInt16(byteArray, 0);
        }
        else
        {
            // ヘッダ部のデータ長(値は6固定)
            headerCH.dataLength = BitConverter.ToInt32(SubArraybyte(file, 4), 0);
            // フォーマット(2byte)
            headerCH.format = BitConverter.ToInt16(SubArraybyte(file, 2), 0);
            // トラック数(2byte)
            headerCH.tracks = BitConverter.ToInt16(SubArraybyte(file, 2), 0);
            // タイムベース(2byte)
            headerCH.division = BitConverter.ToInt16(SubArraybyte(file, 2), 0);
        }

        // トラックチャンク侵入
        var trackCH = new TrackChunkData[headerCH.tracks];

        // トラック数ぶん
        for (int i = 0; i < headerCH.tracks; i++)
        {
            // チャンクID
            trackCH[i].chunkID = SubArraybyte(file, 4);

            // 自分のPCがリトルエンディアンなら変換する
            if (BitConverter.IsLittleEndian)
            {
                // トラックのデータ長読み込み(値は6固定)
                var byteArray = SubArraybyte(file, 4);
                Array.Reverse(byteArray);
                trackCH[i].dataLength = BitConverter.ToInt32(byteArray, 0);
            }
            else
            {
                trackCH[i].dataLength = BitConverter.ToInt32(SubArraybyte(file, 4), 0);
            }

            // データ部読み込み
            trackCH[i].data = SubArraybyte(file, trackCH[i].dataLength);

            // トラックデータ解析に回す
            TrackDataAnalys(trackCH[i].data, headerCH);
        }



        // using(var stream = new FileStream(fileAddres, FileMode.Open, FileAccess.Read))
        // using(var reader = new BinaryReader.ReadBytes(file)){

        // var reader=file;

        // var headerCH =new HeaderChunkData();

        // // チャンクID
        // headerCH.chunkID = SubArraybyte(4);


        // // 自分のPCがリトルエンディアンならバイト順を逆に
        // if (BitConverter.IsLittleEndian)
        // {
        //     // ヘッダ部のデータ長(値は6固定)
        //     var byteArray = reader.ReadBytes(4);
        //     Array.Reverse(byteArray);
        //     headerCH.dataLength = BitConverter.ToInt32(byteArray, 0);
        //     // フォーマット(2byte)
        //     byteArray = reader.ReadBytes(2);
        //     Array.Reverse(byteArray);
        //     headerCH.format = BitConverter.ToInt16(byteArray, 0);
        //     // トラック数(2byte)
        //     byteArray = reader.ReadBytes(2);
        //     Array.Reverse(byteArray);
        //     headerCH.tracks = BitConverter.ToInt16(byteArray, 0);
        //     // タイムベース(2byte)
        //     byteArray = reader.ReadBytes(2);
        //     Array.Reverse(byteArray);
        //     headerCH.division = BitConverter.ToInt16(byteArray, 0);
        // }
        // else
        // {
        //     // ヘッダ部のデータ長(値は6固定)
        //     headerCH.dataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
        //     // フォーマット(2byte)
        //     headerCH.format = BitConverter.ToInt16(reader.ReadBytes(2), 0);
        //     // トラック数(2byte)
        //     headerCH.tracks = BitConverter.ToInt16(reader.ReadBytes(2), 0);
        //     // タイムベース(2byte)
        //     headerCH.division = BitConverter.ToInt16(reader.ReadBytes(2), 0);
        // }

        // // トラックチャンク侵入
        // var trackCH = new TrackChunkData[headerCH.tracks];

        // // トラック数ぶん
        // for(int i=0;i<headerCH.tracks;i++){

        //     // チャンクID
        //     trackCH[i].chunkID = reader.ReadBytes(4);

        //     // 自分のPCがリトルエンディアンなら変換する
        //     if (BitConverter.IsLittleEndian)
        //     {
        //         // トラックのデータ長読み込み(値は6固定)
        //         var byteArray = reader.ReadBytes(4);
        //         Array.Reverse(byteArray);
        //         trackCH[i].dataLength = BitConverter.ToInt32(byteArray, 0);
        //     }
        //     else
        //     {
        //         trackCH[i].dataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
        //     }

        //     // データ部読み込み
        //     trackCH[i].data=reader.ReadBytes(trackCH[i].dataLength);

        //     // トラックデータ解析に回す
        //     TrackDataAnalys(trackCH[i].data,headerCH);
        // }
        // }
        // }
    }
Esempio n. 7
0
    void TrackDataAnalys(byte[] data, HeaderChunkData headerCH)
    {
        // Debug.Log(data.Length);
        uint CrTime     = 0;               // 現在の時間 [ ms ]
        byte statusByte = 0;               // ステータスバイト

        bool[] longFlags = new bool[128];  // ロングノーツ用フラグ

        for (int i = 0; i < data.Length;)
        {
            // Debug.Log(i);

            bool Fintrack  = false;
            uint deltaTime = 0;

            while (true)
            {
                var tmp = data[i++];

                // 下位7bitを格納
                deltaTime |= (tmp & (uint)0x7f);

                // 最上位1bitが0ならデータ終了
                if ((tmp & 0x80) == 0)
                {
                    break;
                }

                // 次の下位7bit用にビット移動
                deltaTime = deltaTime << 7;
            }

            // 現在の時間にデルタタイムを足す
            CrTime += deltaTime;

            /* ランニングステータスチェック */
            if (data[i] >= 0x80)
            {
                statusByte = data[i++]; // ステータスバイト保存
            }
            //else:ランニングステータス適応(前回のステータスバイトを使いまわす)

            // ステータスバイト後のデータ保存用
            byte dataByte0, dataByte1;
            // byte dataByte2, dataByte3;
            if (statusByte >= 0x80 && statusByte <= 0xef)
            {
                switch (statusByte & 0xf0)
                {
                /* チャンネルメッセージ */
                // ノートオフ
                case 0x80:
                    dataByte0 = data[i++];    // どのキーが離されたか
                    // ベロシティ値
                    dataByte1 = data[i++];

                    // 前のレーンがロングノーツなら
                    if (longFlags[dataByte0])
                    {
                        // ロング終点ノート情報生成
                        var note = new NoteData();
                        note.eventTime = (int)CrTime;
                        note.laneIndex = (int)dataByte0;
                        note.type      = NoteType.LongEnd;

                        // リストにつっこむ
                        noteList.Add(note);

                        // ロングノーツフラグ解除
                        longFlags[note.laneIndex] = false;
                    }
                    break;

                case 0x90:      // ノートオン(ノートオフが呼ばれるまでは押しっぱなし扱い)
                    // どのキーが押されたか
                    dataByte0 = data[i++];
                    // ベロシティ値という名の音の強さ。ノートオフメッセージの代わりにここで0を送ってくるタイプもある
                    dataByte1 = data[i++];

                    {
                        // ノート情報生成
                        var note = new NoteData();
                        note.eventTime = (int)CrTime;
                        note.laneIndex = (int)dataByte0;
                        note.type      = NoteType.Normal;
                        // 独自でやっている。ベロシティ値が最大のときのみロングの始点とする
                        if (dataByte1 == 127)
                        {
                            note.type = NoteType.LongStart;
                            // ロングノーツフラグセット
                            longFlags[note.laneIndex] = true;
                        }
                        // ノートオフイベントではなく、ベロシティ値0をノートオフとして保存する形式もあるので対応
                        if (dataByte1 == 0)
                        {
                            // 同じレーンで前回がロングノーツ始点なら
                            if (longFlags[note.laneIndex])
                            {
                                note.type = NoteType.LongEnd;
                                // ロングノーツフラグ解除
                                longFlags[note.laneIndex] = false;
                            }
                        }

                        // リストにつっこむ
                        noteList.Add(note);
                    }
                    break;

                case 0xa0:          // ポリフォニック キープレッシャー(鍵盤楽器で、キーを押した状態でさらに押し込んだ際に、その圧力に応じて送信される)
                    i += 2;         // 使わないのでスルー
                    break;

                case 0xb0:          // コントロールチェンジ(音量、音質など様々な要素を制御するための命令)
                    // コントロールする番号
                    dataByte0 = data[i++];
                    // 設定する値
                    dataByte1 = data[i++];

                    // ※0x00-0x77までがコントロールチェンジで、それ以上はチャンネルモードメッセージとして処理する
                    if (dataByte0 < 0x78)
                    {
                        // コントロールチェンジ
                    }
                    else
                    {
                        // チャンネルモードメッセージは一律データバイトを2つ使用している
                        // チャンネルモードメッセージ
                        switch (dataByte0)
                        {
                        case 0x78:              // オールサウンドオフ
                            // 該当するチャンネルの発音中の音を直ちに消音する。後述のオールノートオフより強制力が強い。
                            break;

                        case 0x79:              // リセットオールコントローラ
                            // 該当するチャンネルの全種類のコントロール値を初期化する。
                            break;

                        case 0x7a:              // ローカルコントロール
                            // オフ:鍵盤を弾くとMIDIメッセージは送信されるがピアノ自体から音は出ない
                            // オン:鍵盤を弾くと音源から音が出る(基本こっち)
                            break;

                        case 0x7b:              // オールノートオフ
                            // 該当するチャンネルの発音中の音すべてに対してノートオフ命令を出す
                            break;

                        /* MIDIモード設定 */
                        // オムニのオン・オフとモノ・ポリモードを組み合わせて4種類のモードがある
                        case 0x7c:              // オムニモードオフ
                            break;

                        case 0x7d:              // オムニモードオン
                            break;

                        case 0x7e:              // モノモードオン
                            break;

                        case 0x7f:              // モノモードオン
                            break;
                        }
                    }
                    break;

                case 0xc0:          // プログラムチェンジ(音色を変える命令)
                    i += 1;
                    break;

                case 0xd0:          // チャンネルプレッシャー(概ねポリフォニック キープレッシャーと同じだが、違いはそのチャンネルの全ノートナンバーに対して有効となる)
                    i += 1;
                    break;

                case 0xe0:          // ピッチベンド(ウォェーンウェューンの表現で使う)
                    i += 2;
                    // ボルテのつまみみたいなのを実装する場合、ここの値が役立つかも
                    break;
                }
            }

            /* システムエクスクルーシブ (SysEx) イベント*/
            else if (statusByte == 0x70 || statusByte == 0x7f)
            {
                byte dataLength = data[i++];
                i += dataLength;
            }

            /* メタイベント*/
            else if (statusByte == 0xff)
            {
                // メタイベントの番号
                byte metaEventID = data[i++];
                // データ長
                byte dataLength = data[i++];

                switch (metaEventID)
                {
                case 0x00:          // シーケンスメッセージ
                    i += dataLength;
                    break;

                case 0x01:          // テキストイベント
                    i += dataLength;
                    break;

                case 0x02:          // 著作権表示
                    i += dataLength;
                    break;

                case 0x03:          // シーケンス/トラック名
                    i += dataLength;
                    break;

                case 0x04:          // 楽器名
                    i += dataLength;
                    break;

                case 0x05:          // 歌詞
                    i += dataLength;
                    break;

                case 0x06:          // マーカー
                    i += dataLength;
                    break;

                case 0x07:          // キューポイント
                    i += dataLength;
                    break;

                case 0x20:          // MIDIチャンネルプリフィクス
                    i += dataLength;
                    break;

                case 0x21:          // MIDIポートプリフィックス
                    i += dataLength;
                    break;

                case 0x2f:          // トラック終了
                    i       += dataLength;
                    Fintrack = true;
                    // ここでループを抜けても良い
                    break;

                case 0x51:          // テンポ変更
                {
                    // テンポ変更情報リストに格納する
                    var tempoData = new TempoData();
                    tempoData.eventTime = (int)CrTime;

                    // 4分音符の長さをマイクロ秒単位で格納されている
                    uint tempo = 0;
                    tempo  |= data[i++];
                    tempo <<= 8;
                    tempo  |= data[i++];
                    tempo <<= 8;
                    tempo  |= data[i++];

                    // BPM割り出し
                    tempoData.bpm = 60000000 / (float)tempo;

                    // 小数点第1で切り捨て処理(10にすると第一位、100にすると第2位まで切り捨てられる)
                    tempoData.bpm = Mathf.Floor(tempoData.bpm * 10) / 10;

                    // tick値割り出し
                    tempoData.tick = (60 / tempoData.bpm / headerCH.division * 1000);

                    // リストにつっこむ
                    tempoList.Add(tempoData);
                }
                break;

                case 0x54:          // SMTPEオフセット
                    i += dataLength;
                    break;

                case 0x58:          // 拍子
                    i += dataLength;
                    // 小節線を表示させるなら使えるかも
                    break;

                case 0x59:          // 調号
                    i += dataLength;
                    break;

                case 0x7f:          // シーケンサ固有メタイベント
                    i += dataLength;
                    break;
                }
            }

            if (Fintrack)
            {
                break;
            }
        }
        // Debug.Log("FinAnalyz");
    }
Esempio n. 8
0
    public void LoadMSF(string fileAddres)
    {
        noteList.Clear();
        tempoList.Clear();

        using (var stream = new FileStream(fileAddres, FileMode.Open, FileAccess.Read))
            using (var reader = new BinaryReader(stream)){
                var headerCH = new HeaderChunkData();

                // チャンクID
                headerCH.chunkID = reader.ReadBytes(4);


                // 自分のPCがリトルエンディアンならバイト順を逆に
                if (BitConverter.IsLittleEndian)
                {
                    // ヘッダ部のデータ長
                    var byteArray = reader.ReadBytes(4);
                    Array.Reverse(byteArray);
                    headerCH.dataLength = BitConverter.ToInt32(byteArray, 0);
                    // フォーマット(2byte)
                    byteArray = reader.ReadBytes(2);
                    Array.Reverse(byteArray);
                    headerCH.format = BitConverter.ToInt16(byteArray, 0);
                    // トラック数(2byte)
                    byteArray = reader.ReadBytes(2);
                    Array.Reverse(byteArray);
                    headerCH.tracks = BitConverter.ToInt16(byteArray, 0);
                    // タイムベース(2byte)
                    byteArray = reader.ReadBytes(2);
                    Array.Reverse(byteArray);
                    headerCH.division = BitConverter.ToInt16(byteArray, 0);
                }
                else
                {
                    // ヘッダ部のデータ長
                    headerCH.dataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                    // フォーマット(2byte)
                    headerCH.format = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                    // トラック数(2byte)
                    headerCH.tracks = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                    // タイムベース(2byte)
                    headerCH.division = BitConverter.ToInt16(reader.ReadBytes(2), 0);
                }

                // トラックチャンク
                var trackCH = new TrackChunkData[headerCH.tracks];

                // トラック数
                for (int i = 0; i < headerCH.tracks; i++)
                {
                    // チャンクID
                    trackCH[i].chunkID = reader.ReadBytes(4);

                    // 自分のPCがリトルエンディアンなら変換する
                    if (BitConverter.IsLittleEndian)
                    {
                        // トラックのデータ長読み込み
                        var byteArray = reader.ReadBytes(4);
                        Array.Reverse(byteArray);
                        trackCH[i].dataLength = BitConverter.ToInt32(byteArray, 0);
                    }
                    else
                    {
                        trackCH[i].dataLength = BitConverter.ToInt32(reader.ReadBytes(4), 0);
                    }

                    // データ部読み込み
                    trackCH[i].data = reader.ReadBytes(trackCH[i].dataLength);

                    // トラックデータ解析へ
                    TrackDataAnalys(trackCH[i].data, headerCH);
                }
            }
    }
Esempio n. 9
0
    void TrackDataAnalys(byte[] data, HeaderChunkData headerCH)
    {
        // Debug.Log(data.Length);
        uint CrTime     = 0;
        byte statusByte = 0;

        bool[] longFlags = new bool[128];

        for (int i = 0; i < data.Length;)
        {
            // Debug.Log(i);

            bool Fintrack  = false;
            uint deltaTime = 0;

            while (true)
            {
                var tmp = data[i++];
                deltaTime |= (tmp & (uint)0x7f);
                if ((tmp & 0x80) == 0)
                {
                    break;
                }
                deltaTime = deltaTime << 7;
            }

            // 現在の時間にデルタタイムを足す
            CrTime += deltaTime;
            if (data[i] >= 0x80)
            {
                statusByte = data[i++];
            }
            //else:ランニングステータス適応(前回のステータスバイトを使いまわす)

            // ステータスバイト後のデータ保存用
            byte dataByte0, dataByte1;
            // byte dataByte2, dataByte3;
            if (statusByte >= 0x80 && statusByte <= 0xef)
            {
                switch (statusByte & 0xf0)
                {
                /* チャンネルメッセージ */
                // ノートオフ
                case 0x80:
                    dataByte0 = data[i++];
                    dataByte1 = data[i++];
                    if (longFlags[dataByte0])
                    {
                        var note = new NoteData();
                        note.eventTime = (int)CrTime;
                        note.laneIndex = (int)dataByte0;
                        note.type      = NoteType.LongEnd;

                        // リストにつっこむ
                        noteList.Add(note);

                        longFlags[note.laneIndex] = false;
                    }
                    break;

                // ノートオン(ノートオフが呼ばれるまでは押しっぱなし扱い)
                case 0x90:
                    // どのキーが押されたか
                    dataByte0 = data[i++];
                    dataByte1 = data[i++];

                    {
                        // ノート情報生成
                        var note = new NoteData();
                        note.eventTime = (int)CrTime;
                        note.laneIndex = (int)dataByte0;
                        note.type      = NoteType.Normal;
                        if (dataByte1 == 127)
                        {
                            note.type = NoteType.LongStart;
                            longFlags[note.laneIndex] = true;
                        }
                        // ノートオフイベントではなく、ベロシティ値0をノートオフとして保存する形式もあるので対応
                        if (dataByte1 == 0)
                        {
                            // 同じレーンで前回がロングノーツ始点なら
                            if (longFlags[note.laneIndex])
                            {
                                note.type = NoteType.LongEnd;
                                longFlags[note.laneIndex] = false;
                            }
                        }

                        // リストにつっこむ
                        noteList.Add(note);
                    }
                    break;

                case 0xa0:
                    i += 2;         // 使わないのでスルー
                    break;

                case 0xb0:
                    dataByte0 = data[i++];
                    dataByte1 = data[i++];

                    // ※0x00-0x77までがコントロールチェンジで、それ以上はチャンネルモードメッセージとして処理する
                    if (dataByte0 < 0x78)
                    {
                        // コントロールチェンジ
                    }
                    else
                    {
                        switch (dataByte0)
                        {
                        case 0x78:
                            break;

                        case 0x79:
                            break;

                        case 0x7a:
                            break;

                        case 0x7b:
                            break;

                        // オムニモードオフ
                        case 0x7c:
                            break;

                        // オムニモードオン
                        case 0x7d:
                            break;

                        // モノモードオン
                        case 0x7e:
                            break;

                        // モノモードオン
                        case 0x7f:
                            break;
                        }
                    }
                    break;

                case 0xc0:
                    i += 1;
                    break;

                case 0xd0:
                    i += 1;
                    break;

                case 0xe0:
                    i += 2;
                    break;
                }
            }

            else if (statusByte == 0x70 || statusByte == 0x7f)
            {
                byte dataLength = data[i++];
                i += dataLength;
            }

            else if (statusByte == 0xff)
            {
                           // メタイベント
                byte metaEventID = data[i++];
                byte dataLength  = data[i++];

                switch (metaEventID)
                {
                // シーケンスメッセージ
                case 0x00:
                    i += dataLength;
                    break;

                // テキストイベント
                case 0x01:
                    i += dataLength;
                    break;

                // 著作権表示
                case 0x02:
                    i += dataLength;
                    break;

                // シーケンス/トラック名
                case 0x03:
                    i += dataLength;
                    break;

                // 楽器名
                case 0x04:
                    i += dataLength;
                    break;

                // 歌詞
                case 0x05:
                    i += dataLength;
                    break;

                // マーカー
                case 0x06:
                    i += dataLength;
                    break;

                // キューポイント
                case 0x07:
                    i += dataLength;
                    break;

                // MIDIチャンネルプリフィクス
                case 0x20:
                    i += dataLength;
                    break;

                // MIDIポートプリフィックス
                case 0x21:
                    i += dataLength;
                    break;

                // トラック終了
                case 0x2f:
                    i       += dataLength;
                    Fintrack = true;
                    // ここでループを抜ける
                    break;

                // テンポ変更
                case 0x51:
                {
                    // テンポ変更情報リストに格納する
                    var tempoData = new TempoData();
                    tempoData.eventTime = (int)CrTime;

                    // 4分音符の長さをマイクロ秒単位で格納されている
                    uint tempo = 0;
                    tempo  |= data[i++];
                    tempo <<= 8;
                    tempo  |= data[i++];
                    tempo <<= 8;
                    tempo  |= data[i++];

                    // BPM割り出し
                    tempoData.bpm = 60000000 / (float)tempo;

                    // 小数点第1で切り捨て処理(10にすると第一位、100にすると第2位まで切り捨てられる)
                    tempoData.bpm = Mathf.Floor(tempoData.bpm * 10) / 10;

                    // tick値割り出し
                    tempoData.tick = (60 / tempoData.bpm / headerCH.division * 1000);

                    // リストにつっこむ
                    tempoList.Add(tempoData);
                }
                break;

                // SMTPEオフセット
                case 0x54:
                    i += dataLength;
                    break;

                // 拍子
                case 0x58:
                          // 小節線を表示させるなら使えるかも
                        i += dataLength;
                    break;

                // 調号
                case 0x59:
                    i += dataLength;
                    break;

                // シーケンサ固有メタイベント
                case 0x7f:
                    i += dataLength;
                    break;
                }
            }

            if (Fintrack)
            {
                break;
            }
        }
        // Debug.Log("FinAnalyz");
    }