public static MidiFileAsset Load(byte [] data)
        {
            var reader = new MidiDataStreamReader(data);;

            // Chunk type
            if (reader.ReadChars(4) != "MThd")
            {
                throw new FormatException("Can't find header chunk.");
            }

            // Chunk length
            if (reader.ReadBEUInt32() != 6u)
            {
                throw new FormatException("Length of header chunk must be 6.");
            }

            // Format (unused)
            reader.Advance(2);

            // Number of tracks
            var trackCount = reader.ReadBEUInt16();

            // Ticks per quarter note
            var tpqn = reader.ReadBEUInt16();

            if ((tpqn & 0x8000u) != 0)
            {
                throw new FormatException("SMPTE time code is not supported.");
            }

            // Tracks
            var tracks = new MidiAnimationAsset [trackCount];

            for (var i = 0; i < trackCount; i++)
            {
                tracks[i] = ReadTrack(reader, tpqn);
            }

            // Asset instantiation
            var asset = ScriptableObject.CreateInstance <MidiFileAsset>();

            asset.tracks = tracks;
            return(asset);
        }
        static MidiAnimationAsset ReadTrack(MidiDataStreamReader reader, uint tpqn)
        {
            // Chunk type
            if (reader.ReadChars(4) != "MTrk")
            {
                throw new FormatException("Can't find track chunk.");
            }

            // Chunk length
            var chunkEnd = reader.ReadBEUInt32();

            chunkEnd += reader.Position;

            // MIDI event sequence
            var events = new List <MidiEvent>();
            var ticks  = 0u;
            var stat   = (byte)0;

            while (reader.Position < chunkEnd)
            {
                // Delta time
                ticks += reader.ReadMultiByteValue();

                // Status byte
                if ((reader.PeekByte() & 0x80u) != 0)
                {
                    stat = reader.ReadByte();
                }

                if (stat == 0xffu)//255,FFのこと
                {
                    // 0xff: Meta event (unused) メタイベントにテンポ情報とか入っている

                    //reader.Advance(1);//オリジナルのスクリプト:メタデータの種類
                    var meta = reader.ReadByte();

                    if (meta == 0x51u)
                    {
                        var dataLengh = reader.ReadMultiByteValue(); //データ長の情報。ここでは03が入るはず。(テンポの情報は3バイトであらわされる)
                        var data      = reader.ReadBEUInt24();       //テンポのデータ。単位は[ms/qn]。通常表記のテンポ[qn/min]に直すには、逆数に60000000をかければ良い。

                        var tmp = Math.Round(60000000 / (float)data);
                        //出力するとき、MidiEventのインスタンスで返すが、小数の情報が落ちてしまうのでRoundにしておく。複雑なテンポはないと仮定する。

                        events.Add(new MidiEvent
                        {
                            time   = ticks,
                            status = stat,
                            data1  = meta,
                            data2  = (byte)tmp
                        });

                        Debug.Log(ticks + ":: Tempo info:" + stat + ", " + meta + ", " + dataLengh + ", " + tmp);
                    }
                    else
                    {
                        reader.Advance(reader.ReadMultiByteValue());//reader.ReadMultiByteValue()で続くデータ長さを取得。その分Advanceで送っている。
                    }
                }
                else if (stat == 0xf0u)
                {
                    // 0xf0: SysEx (unused)
                    while (reader.ReadByte() != 0xf7u)
                    {
                    }
                }
                else
                {
                    // MIDI event
                    var b1 = reader.ReadByte();
                    var b2 = (stat & 0xe0u) == 0xc0u ? (byte)0 : reader.ReadByte();
                    events.Add(new MidiEvent {
                        time = ticks, status = stat, data1 = b1, data2 = b2
                    });
                    //Debug.Log(ticks + "," + stat + "," + b1 + "," + b2);
                }
            }

            //Debug.Log(events);

            // Quantize duration with bars.
            var bars = (ticks + tpqn * 4 - 1) / (tpqn * 4);
            //Debug.Log(bars + "bars");


            // Asset instantiation
            var asset = ScriptableObject.CreateInstance <MidiAnimationAsset>();

            asset.template.tempo               = 120;
            asset.template.duration            = bars * tpqn * 4;
            asset.template.ticksPerQuarterNote = tpqn;
            asset.template.events              = events.ToArray();
            return(asset);
        }
Esempio n. 3
0
        static MidiAnimationAsset ReadTrack(MidiDataStreamReader reader, uint tpqn)
        {
            // Chunk type
            if (reader.ReadChars(4) != "MTrk")
            {
                throw new FormatException("Can't find track chunk.");
            }

            // Chunk length
            var chunkEnd = reader.ReadBEUInt32();

            chunkEnd += reader.Position;

            // MIDI event sequence
            var events       = new List <MidiEvent>();
            var tick         = 0u;
            var time         = 0f;
            var tempo        = 120f;
            var stat         = (byte)0;
            var ticksStr     = "";
            var hitPointsCnt = 0;

            while (reader.Position < chunkEnd)
            {
                // Delta time
                var delta = reader.ReadMultiByteValue();
                tick += delta;
                // Time with tempo
                var secondsPerBeat = 60f / tempo;
                var deltaSeconds   = secondsPerBeat * ((float)delta / (float)tpqn);
                time += deltaSeconds;

                // Status byte
                if ((reader.PeekByte() & 0x80u) != 0)
                {
                    stat = reader.ReadByte();
                }

                if (stat == 0xffu)
                {
                    // 0xff: Meta event
                    var meta = reader.ReadByte();

                    if (meta == 0x51u)   // tempo set
                    {
                        var dataLength = reader.ReadMultiByteValue();
                        var data       = reader.ReadBEUInt24();
                        var tmp        = Math.Round(60000000 / (float)data);
                        Debug.LogFormat("tick: {0}, tempo: {1}", tick, tmp);
                        tempo = (float)tmp;
                        events.Add(new MidiEvent {
                            time   = time,
                            tick   = tick,
                            status = stat,
                            data1  = meta,
                            data2  = new byte(), // dummy
                            tempo  = tempo
                        });
                    }
                    else
                    {
                        reader.Advance(reader.ReadMultiByteValue());
                        events.Add(new MidiEvent {
                            time   = time,
                            tick   = tick,
                            status = stat,
                            data1  = meta,
                            data2  = new byte() // dummy
                        });
                    }
                }
                else if (stat == 0xf0u)
                {
                    // 0xf0: SysEx (unused)
                    while (reader.ReadByte() != 0xf7u)
                    {
                    }
                    events.Add(new MidiEvent {
                        time   = time,
                        tick   = tick,
                        status = stat,
                        data1  = new byte(), // dummy
                        data2  = new byte()  // dummy
                    });
                }
                else
                {
                    // MIDI event
                    var b1 = reader.ReadByte();
                    var b2 = (stat & 0xe0u) == 0xc0u ? (byte)0 : reader.ReadByte();
                    if (!Contains(events, tick, stat))
                    {
                        events.Add(new MidiEvent {
                            time = time, tick = tick, status = stat, data1 = b1, data2 = b2
                        });

                        if ((stat & 0xf0) == 0x90)
                        {
                            Debug.LogFormat("tick: {0}, time: {1}, frame: {2}, note: {3}", (float)tick, time, Math.Round(time * 60), b1);
                            ticksStr += tick + "\n";

                            if (b1 == 0x26 || b1 == 0x23)
                            {
                                hitPointsCnt++;
                            }
                        }
                    }
                }
            }
            // Debug.Log(ticksStr);
            Debug.LogFormat("HitPoints {0}", hitPointsCnt);

            // Quantize duration with bars.
            var bars = (tick + tpqn * 4 - 1) / (tpqn * 4);

            // Asset instantiation
            var asset = ScriptableObject.CreateInstance <MidiAnimationAsset>();

            asset.template.tempo               = 120;
            asset.template.duration            = bars * tpqn * 4;
            asset.template.ticksPerQuarterNote = tpqn;
            asset.template.events              = events.ToArray();
            return(asset);
        }
        static MidiAnimationAsset ReadTrack(MidiDataStreamReader reader, uint tpqn)
        {
            // Chunk type
            if (reader.ReadChars(4) != "MTrk")
            {
                throw new FormatException("Can't find track chunk.");
            }

            // Chunk length
            var chunkEnd = reader.ReadBEUInt32();

            chunkEnd += reader.Position;

            // MIDI event sequence
            var events = new List <MidiEvent>();
            var ticks  = 0u;
            var stat   = (byte)0;

            while (reader.Position < chunkEnd)
            {
                // Delta time
                ticks += reader.ReadMultiByteValue();

                // Status byte
                if ((reader.PeekByte() & 0x80u) != 0)
                {
                    stat = reader.ReadByte();
                }

                if (stat == 0xffu)
                {
                    // 0xff: Meta event (unused)
                    reader.Advance(1);
                    reader.Advance(reader.ReadMultiByteValue());
                }
                else if (stat == 0xf0u)
                {
                    // 0xf0: SysEx (unused)
                    while (reader.ReadByte() != 0xf7u)
                    {
                    }
                }
                else
                {
                    // MIDI event
                    var b1 = reader.ReadByte();
                    var b2 = (stat & 0xe0u) == 0xc0u ? (byte)0 : reader.ReadByte();
                    events.Add(new MidiEvent {
                        time = ticks, status = stat, data1 = b1, data2 = b2
                    });
                }
            }

            // Quantize duration with bars.
            var bars = (ticks + tpqn * 4 - 1) / (tpqn * 4);

            // Asset instantiation
            var asset = ScriptableObject.CreateInstance <MidiAnimationAsset>();

            asset.template.tempo               = 120;
            asset.template.duration            = bars * tpqn * 4;
            asset.template.ticksPerQuarterNote = tpqn;
            asset.template.events              = events.ToArray();
            return(asset);
        }