public void HandleEvent(int track, MidiEvent e) { if (e.status == 0xff && e.type == 0x51) // meta tempo { int i = e.dataLoc; // 24-bit value specifying the tempo as the number of microseconds per beat int microsecondsPerBeat = BitBe.ReadInt24(file.bytes, ref i); beatsPerSecond = (1000000f / microsecondsPerBeat); Debug.LogFormat("meta: {0} tempo", microsecondsPerBeat); } byte channel = (byte)(e.status & 0xf); DispatchChannelEvent(channel, e.status, e.b1, e.b2); for (int i = 0, count = routes.Count; i < count; i += 1) { var r = routes[i]; if (r.trackIn == track && r.channelIn == channel) { DispatchChannelEvent(r.channelOut, e.status, e.b1, e.b2); } } }
public MidiFile(byte[] bytes) { this.bytes = bytes; int i = 0; // "MThd" 4 bytes i += 4; // <header_length> 4 bytes int length = BitBe.ReadInt32(bytes, ref i); int ii = i; // <format> 2 bytes format = BitBe.ReadInt16(bytes, ref i); // <n> 2 bytes trackCount = BitBe.ReadInt16(bytes, ref i); tracks = new MidiEvent[trackCount][]; trackLengths = new int[trackCount]; trackTicks = new int[trackCount]; // <division> 2 bytes ticksPerBeat = BitBe.ReadInt16(bytes, ref i); // end chunk i = ii + length; byte runingStatus = 0; for (short j = 0, count = trackCount; j < count; j += 1) { var trackEventList = new List <MidiEvent>(); // "MTrk" 4 bytes i += 4; // <length> 4 bytes length = BitBe.ReadInt32(bytes, ref i); ii = i; // <track_event> int k = 0; while (i < ii + length) { int delta = BitBe.ReadVlv(bytes, ref i); trackTicks[j] += delta; int trackTick = trackTicks[j]; byte statusByte = Bit.ReadByte(bytes, ref i); int dataLength; if (statusByte < 0x80) // running status { dataLength = GetMidiEventLength(runingStatus); trackEventList.Add(new MidiEvent(j, k, delta, trackTick, runingStatus, 0, i - 1, dataLength, bytes)); dataLength -= 1; } else if (statusByte < 0xf0) // midi events { runingStatus = statusByte; dataLength = GetMidiEventLength(statusByte); trackEventList.Add(new MidiEvent(j, k, delta, trackTick, statusByte, 0, i, dataLength, bytes)); } else if (statusByte == 0xf0 || statusByte == 0xf7) // sysex events | escape sequences { dataLength = BitBe.ReadVlv(bytes, ref i); trackEventList.Add(new MidiEvent(j, k, delta, trackTick, statusByte, 0, i, dataLength, bytes)); } else if (statusByte == 0xff) // meta events { byte type = Bit.ReadByte(bytes, ref i); dataLength = BitBe.ReadVlv(bytes, ref i); trackEventList.Add(new MidiEvent(j, k, delta, trackTick, statusByte, type, i, dataLength, bytes)); } else { return; } i += dataLength; k += 1; } tracks[j] = trackEventList.ToArray(); trackLengths[j] = trackEventList.Count; // end chunk i = ii + length; } var combinedEventList = new List <MidiEvent>(); for (int j = 0; j < trackCount; j++) { combinedEventList.AddRange(tracks[j]); } combinedEventList.Sort((a, b) => { if (a.tick == b.tick) { if (a.track == b.track) { return(a.index.CompareTo(b.index)); } return(a.track.CompareTo(b.track)); } return(a.tick.CompareTo(b.tick)); }); combinedTrack = combinedEventList.ToArray(); }
void Parse() { var seq = new Sequence(); // default tempo 120 bpm float beatsPerSecond = 2; int ticks = 0; float seconds = 0; byte[] channelPrograms = new byte[16]; for (int i = 0; i < file.combinedTrack.Length; i++) { var e = file.combinedTrack[i]; byte channel = (byte)(e.status & 0xf); seq = SwitchWorkingSequence(seq, e.track, channel, channelPrograms[channel]); int tickDiff = e.tick - ticks; ticks = e.tick; seconds += tickDiff / (beatsPerSecond * file.ticksPerBeat); switch (e.status >> 4) { case 0x8: // note off //UnityEngine.Debug.LogFormat("off {3} track {0} tick {1} seconds {2}", e.track, e.tick, seconds, e.b1); NoteOff(seq, e.tick, seconds, e.b1); break; case 0x9: // note on //UnityEngine.Debug.LogFormat("on {3} track {0} tick {1} seconds {2}", e.track, e.tick, seconds, e.b1); if (e.b2 == 0) { NoteOff(seq, e.tick, seconds, e.b1); } else { seq.notes.Add(new Note { track = e.track, channel = channel, note = e.b1, velocity = e.b2, start = e.tick, startSeconds = seconds }); } break; case 0xc: // program change //UnityEngine.Debug.LogFormat("prog track {0} tick {1} seconds {2}", e.track, e.tick, seconds); channelPrograms[channel] = e.b1; seq = SwitchWorkingSequence(seq, e.track, seq.channel, e.b1); break; case 0xff: // meta if (e.type == 0x51) // tempo //UnityEngine.Debug.LogFormat("temp track {0} tick {1} seconds {2}", e.track, e.tick, seconds); { int start = e.dataLoc; // 24-bit value specifying the tempo as the number of microseconds per beat int microsecondsPerBeat = BitBe.ReadInt24(file.bytes, ref start); beatsPerSecond = 1000000f / microsecondsPerBeat; } break; } } if (seq.notes.Count > 0 && !sequences.Contains(seq)) { sequences.Add(seq); } sequences.Sort((a, b) => { if (a.track == b.track) { if (a.channel == b.channel) { return(a.program.CompareTo(b.program)); } return(a.channel.CompareTo(b.channel)); } return(a.track.CompareTo(b.track)); }); int trackGroupIndex = 0; byte channelGroupIndex = 0; byte programGroupIndex = 0; var trackGroupDict = new Dictionary <int, int>(); var channelGroupDict = new Dictionary <byte, byte>(); var programGroupDict = new Dictionary <byte, byte>(); start = end = -1; for (int i = 0; i < sequences.Count; i++) { seq = sequences[i]; if (!trackGroupDict.ContainsKey(seq.track)) { trackGroupDict.Add(seq.track, trackGroupIndex); trackGroupIndex += 1; } if (!channelGroupDict.ContainsKey(seq.channel)) { channelGroupDict.Add(seq.channel, channelGroupIndex); channelGroupIndex += 1; } if (!programGroupDict.ContainsKey(seq.program)) { programGroupDict.Add(seq.program, programGroupIndex); programGroupIndex += 1; } seq.trackGroup = trackGroupDict[seq.track]; seq.channelGroup = channelGroupDict[seq.channel]; seq.programGroup = programGroupDict[seq.program]; noteCount += seq.notes.Count; seq.start = seq.notes[0].start; if (start == -1 || seq.start < start) { start = seq.start; } int seqEnd = seq.notes[0].end; for (int j = 0; j < seq.notes.Count; j++) { var n = seq.notes[j]; if (seqEnd < n.end) { seqEnd = n.end; } if (n.end == 0) { UnityEngine.Debug.LogErrorFormat("Note is never off: {0} tr {1} ch {2} start {3} seq {4} tr {5} ch {6} prog {7}", n.note, n.track, n.channel, n.start, seq.index, seq.track, seq.channel, seq.program); } } seq.end = seqEnd; if (end == -1 || seq.end > end) { end = seq.end; } } trackGroups = trackGroupDict.Keys.ToArray(); channelGroups = channelGroupDict.Keys.ToArray(); programGroups = programGroupDict.Keys.ToArray(); }