public MidiMetaEvent(uint time, int type, byte[] bytes, Midi midi, MidiTrack track) { Time = time; Type = type; Bytes = bytes; Midi = midi; Track = track; }
public MidiCommandEvent(uint time, int command, int channel, byte[] args, Midi midi, MidiTrack track) { Time = time; Command = command; Channel = channel; Args = args; Midi = midi; Track = track; }
MidiTrackMetaData(MidiTrack track) { mTrack = track; if (mTrack.Name != null) { var matches = sTrackRegex.Matches(mTrack.Name); for (var i = 0; i < matches.Count; ++i) { var name = matches[i].Groups["key"].Value.ToLowerInvariant(); var value = matches[i].Groups["value"].Value; if (sParserLUT.ContainsKey(name)) { sParserLUT[name](this, value); } } } }
public TrackDisplay(MidiTrack[] tracks, Color[] trackColors) { m_displayElementList = new ArrayList(); // store notes- ArrayList evts = new ArrayList(); for (int n = 0; n < tracks.Length; n++) //MidiTrack t in f.tracks) { MidiTrack t = tracks[n]; if (!t.enabled) { continue; } evts.Clear(); float width = 1; for (int i = 0; i < t.events.Count; i++) { if (t.get_e(i).IsMidi) { TimedMidiMsg m = (TimedMidiMsg)t.get_e(i); if (m.m.IsNoteOn) { evts.Add(m); } else if (m.m.IsNoteOff) { TimedMidiMsg e1 = null; TimedMidiMsg e2 = m; foreach (TimedMidiMsg mm in evts) { if (mm.m.Byte2 == e2.m.Byte2) { e1 = mm; evts.Remove(mm); break; } } if (e1 != null) { TrackDisplayElement element = new TrackDisplayElement(e1.Time, e2.Time, e2.m.Byte2, trackColors[Array.IndexOf(tracks, t)], width); AddElement(element, n == 0, 0); } } } else if (t.get_e(i).IsMeta) { MetaMsg m = (MetaMsg)t.get_e(i); if (m.Type == 6) // "Marker" { if (m.Txt.ToLower() == "bold") { width = 2; } else if (m.Txt.ToLower() == "stop") { width = 1; } } } } } }
/// <summary>Create the demo sequence.</summary> /// <returns>The created midi sequence.</returns> public MidiSequence CreateSequence() { MidiSequence sequence = new MidiSequence(0, 120); MidiTrack track = sequence.AddTrack(); track.Events.Add(new TimeSignature(0, 4, 2, 24, 8)); track.Events.Add(new KeySignature(0, Key.NoFlatsOrSharps, Tonality.Major)); track.Events.Add(new Tempo(0, 416667)); track.Events.Add(new ProgramChange(0, 0, GeneralMidiInstruments.AcousticGrand)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(11, 0, "E6", 60)); track.Events.Add(new Controller(56, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(24, 0, "D#6", 66)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(2, 0, "E6", 0)); track.Events.Add(new NoteOn(75, 0, "E6", 60)); track.Events.Add(new NoteOn(16, 0, "D#6", 0)); track.Events.Add(new NoteOn(72, 0, "D#6", 62)); track.Events.Add(new NoteOn(8, 0, "E6", 0)); track.Events.Add(new NoteOn(77, 0, "E6", 72)); track.Events.Add(new NoteOn(16, 0, "D#6", 0)); track.Events.Add(new NoteOn(64, 0, "B5", 71)); track.Events.Add(new NoteOn(12, 0, "E6", 0)); track.Events.Add(new NoteOn(64, 0, "D6", 85)); track.Events.Add(new NoteOn(19, 0, "B5", 0)); track.Events.Add(new NoteOn(60, 0, "C6", 80)); track.Events.Add(new NoteOn(24, 0, "D6", 0)); track.Events.Add(new NoteOn(51, 0, "A3", 66)); track.Events.Add(new NoteOn(5, 0, "A5", 73)); track.Events.Add(new NoteOn(13, 0, "C6", 0)); track.Events.Add(new Controller(24, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(35, 0, "E4", 70)); track.Events.Add(new NoteOn(51, 0, "A5", 0)); track.Events.Add(new NoteOn(23, 0, "A4", 75)); track.Events.Add(new NoteOn(72, 0, "A4", 0)); track.Events.Add(new NoteOn(0, 0, "C5", 78)); track.Events.Add(new NoteOn(73, 0, "E5", 86)); track.Events.Add(new NoteOn(13, 0, "A3", 0)); track.Events.Add(new NoteOn(42, 0, "E4", 0)); track.Events.Add(new NoteOn(17, 0, "A5", 87)); track.Events.Add(new NoteOn(19, 0, "C5", 0)); track.Events.Add(new NoteOn(14, 0, "E5", 0)); track.Events.Add(new NoteOn(40, 0, "E3", 72)); track.Events.Add(new NoteOn(1, 0, "B5", 84)); track.Events.Add(new Controller(4, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(10, 0, "A5", 0)); track.Events.Add(new Controller(36, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(19, 0, "E3", 0)); track.Events.Add(new NoteOn(5, 0, "E4", 70)); track.Events.Add(new NoteOn(47, 0, "E4", 0)); track.Events.Add(new NoteOn(0, 0, "B5", 0)); track.Events.Add(new NoteOn(9, 0, "G#4", 85)); track.Events.Add(new NoteOn(62, 0, "E5", 82)); track.Events.Add(new NoteOn(66, 0, "G#4", 0)); track.Events.Add(new NoteOn(5, 0, "G#5", 85)); track.Events.Add(new NoteOn(72, 0, "B5", 93)); track.Events.Add(new NoteOn(3, 0, "E5", 0)); track.Events.Add(new NoteOn(29, 0, "G#5", 0)); track.Events.Add(new NoteOn(38, 0, "C6", 78)); track.Events.Add(new NoteOn(4, 0, "A3", 66)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(3, 0, "B5", 0)); track.Events.Add(new Controller(38, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(27, 0, "E4", 72)); track.Events.Add(new NoteOn(76, 0, "A4", 70)); track.Events.Add(new NoteOn(68, 0, "E5", 75)); track.Events.Add(new NoteOn(5, 0, "C6", 0)); track.Events.Add(new NoteOn(46, 0, "A4", 0)); track.Events.Add(new NoteOn(20, 0, "A3", 0)); track.Events.Add(new NoteOn(5, 0, "E4", 0)); track.Events.Add(new NoteOn(3, 0, "E5", 0)); track.Events.Add(new NoteOn(1, 0, "E6", 74)); track.Events.Add(new NoteOn(70, 0, "E6", 0)); track.Events.Add(new NoteOn(2, 0, "D#6", 82)); track.Events.Add(new NoteOn(76, 0, "E6", 90)); track.Events.Add(new NoteOn(6, 0, "D#6", 0)); track.Events.Add(new Controller(4, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(59, 0, "D#6", 86)); track.Events.Add(new NoteOn(14, 0, "E6", 0)); track.Events.Add(new NoteOn(57, 0, "E6", 103)); track.Events.Add(new NoteOn(20, 0, "D#6", 0)); track.Events.Add(new NoteOn(56, 0, "B5", 90)); track.Events.Add(new NoteOn(11, 0, "E6", 0)); track.Events.Add(new NoteOn(66, 0, "D6", 86)); track.Events.Add(new NoteOn(11, 0, "B5", 0)); track.Events.Add(new NoteOn(62, 0, "C6", 85)); track.Events.Add(new NoteOn(8, 0, "D6", 0)); track.Events.Add(new NoteOn(72, 0, "A5", 77)); track.Events.Add(new NoteOn(3, 0, "C6", 0)); track.Events.Add(new NoteOn(1, 0, "A3", 55)); track.Events.Add(new Controller(16, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(52, 0, "E4", 60)); track.Events.Add(new NoteOn(28, 0, "A5", 0)); track.Events.Add(new NoteOn(50, 0, "A4", 77)); track.Events.Add(new NoteOn(63, 0, "C5", 90)); track.Events.Add(new NoteOn(3, 0, "A4", 0)); track.Events.Add(new NoteOn(57, 0, "A3", 0)); track.Events.Add(new NoteOn(14, 0, "E5", 86)); track.Events.Add(new NoteOn(66, 0, "E4", 0)); track.Events.Add(new NoteOn(3, 0, "A5", 82)); track.Events.Add(new NoteOn(3, 0, "C5", 0)); track.Events.Add(new NoteOn(12, 0, "E5", 0)); track.Events.Add(new NoteOn(54, 0, "B5", 89)); track.Events.Add(new NoteOn(3, 0, "E3", 79)); track.Events.Add(new NoteOn(7, 0, "A5", 0)); track.Events.Add(new Controller(2, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new Controller(45, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(14, 0, "E3", 0)); track.Events.Add(new NoteOn(7, 0, "E4", 68)); track.Events.Add(new NoteOn(66, 0, "E4", 0)); track.Events.Add(new NoteOn(11, 0, "G#4", 84)); track.Events.Add(new NoteOn(67, 0, "D5", 82)); track.Events.Add(new NoteOn(11, 0, "B5", 0)); track.Events.Add(new NoteOn(4, 0, "G#4", 0)); track.Events.Add(new NoteOn(68, 0, "C6", 73)); track.Events.Add(new NoteOn(37, 0, "D5", 0)); track.Events.Add(new NoteOn(35, 0, "B5", 69)); track.Events.Add(new NoteOn(69, 0, "B5", 0)); track.Events.Add(new NoteOn(3, 0, "C6", 0)); track.Events.Add(new NoteOn(6, 0, "A3", 60)); track.Events.Add(new NoteOn(2, 0, "A5", 68)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new Controller(49, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(29, 0, "E4", 66)); track.Events.Add(new NoteOn(90, 0, "A4", 71)); track.Events.Add(new NoteOn(23, 0, "A5", 0)); track.Events.Add(new NoteOn(5, 0, "A3", 0)); track.Events.Add(new NoteOn(16, 0, "E4", 0)); track.Events.Add(new NoteOn(0, 0, "A4", 0)); track.Events.Add(new NoteOn(130, 0, "E6", 80)); track.Events.Add(new EndOfTrack(130)); return(sequence); }
public override void ExecuteEvent(MidiTrack track) { }
/// <summary>Intializes the meta event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="metaEventID">The event ID for this meta event.</param> /// <param name="data">The data associated with the event.</param> public UnknownMetaMidiEvent(MidiTrack owner, long deltaTime, byte metaEventID, byte[] data) : base(owner, deltaTime, metaEventID) { Data = data; }
public override void Integrate(MidiTrack track) { Debug.Assert(time == 0); throw new System.NotImplementedException(); }
public void TestSplitTrack() { MidiTrack track = new MidiTrack(1); int start, number; /* Create notes between 70 and 80 */ for (int i = 0; i < 100; i++) { start = i * 10; number = 70 + (i % 10); MidiNote note = new MidiNote(start, 0, number, 10); track.AddNote(note); } /* Create notes between 65 and 75 */ for (int i = 0; i < 100; i++) { start = i * 10 + 1; number = 65 + (i % 10); MidiNote note = new MidiNote(start, 0, number, 10); track.AddNote(note); } /* Create notes between 50 and 60 */ for (int i = 0; i < 100; i++) { start = i * 10; number = 50 + (i % 10); MidiNote note = new MidiNote(start, 0, number, 10); track.AddNote(note); } /* Create notes between 55 and 65 */ for (int i = 0; i < 100; i++) { start = i * 10 + 1; number = 55 + (i % 10); MidiNote note = new MidiNote(start, 0, number, 10); track.AddNote(note); } track.Notes.Sort( track.Notes[0] ); List<MidiTrack> tracks = MidiFile.SplitTrack(track, 40); Assert.AreEqual(tracks[0].Notes.Count, 200); Assert.AreEqual(tracks[1].Notes.Count, 200); for (int i = 0; i < 100; i++) { MidiNote note1 = tracks[0].Notes[i*2]; MidiNote note2 = tracks[0].Notes[i*2 + 1]; Assert.AreEqual(note1.StartTime, i*10); Assert.AreEqual(note2.StartTime, i*10 + 1); Assert.AreEqual(note1.Number, 70 + (i % 10)); Assert.AreEqual(note2.Number, 65 + (i % 10)); } for (int i = 0; i < 100; i++) { MidiNote note1 = tracks[1].Notes[i*2]; MidiNote note2 = tracks[1].Notes[i*2 + 1]; Assert.AreEqual(note1.StartTime, i*10); Assert.AreEqual(note2.StartTime, i*10 + 1); Assert.AreEqual(note1.Number, 50 + (i % 10)); Assert.AreEqual(note2.Number, 55 + (i % 10)); } }
public void TestRoundStartTimes() { const byte notenum = 20; List<MidiTrack> tracks = new List<MidiTrack>(); MidiTrack track1 = new MidiTrack(0); track1.AddNote(new MidiNote(0, 0, notenum, 60)); track1.AddNote(new MidiNote(3, 0, notenum+1, 60)); track1.AddNote(new MidiNote(15, 0, notenum+2, 60)); track1.AddNote(new MidiNote(22, 0, notenum+3, 60)); track1.AddNote(new MidiNote(62, 0, notenum+4, 60)); MidiTrack track2 = new MidiTrack(1); track2.AddNote(new MidiNote(2, 0, notenum+10, 60)); track2.AddNote(new MidiNote(10, 0, notenum+11, 60)); track2.AddNote(new MidiNote(20, 0, notenum+12, 60)); track2.AddNote(new MidiNote(35, 0, notenum+13, 60)); track2.AddNote(new MidiNote(36, 0, notenum+14, 60)); tracks.Add(track1); tracks.Add(track2); int quarter = 130; int tempo = 500000; TimeSignature time = new TimeSignature(4, 4, quarter, tempo); /* quarternote * 60,000 / 500,000 = 15 pulses * So notes within 15 pulses should be grouped together. * 0, 2, 3, 10, 15 are grouped to starttime 0 * 20, 22, 35 are grouped to starttime 20 * 36 is still 36 * 62 is still 62 */ MidiFile.RoundStartTimes(tracks, 60, time); List<MidiNote> notes1 = tracks[0].Notes; List<MidiNote> notes2 = tracks[1].Notes; Assert.AreEqual(notes1.Count, 5); Assert.AreEqual(notes2.Count, 5); Assert.AreEqual(notes1[0].Number, notenum); Assert.AreEqual(notes1[1].Number, notenum+1); Assert.AreEqual(notes1[2].Number, notenum+2); Assert.AreEqual(notes1[3].Number, notenum+3); Assert.AreEqual(notes1[4].Number, notenum+4); Assert.AreEqual(notes2[0].Number, notenum+10); Assert.AreEqual(notes2[1].Number, notenum+11); Assert.AreEqual(notes2[2].Number, notenum+12); Assert.AreEqual(notes2[3].Number, notenum+13); Assert.AreEqual(notes2[4].Number, notenum+14); Assert.AreEqual(notes1[0].StartTime, 0); Assert.AreEqual(notes1[1].StartTime, 0); Assert.AreEqual(notes1[2].StartTime, 0); Assert.AreEqual(notes1[3].StartTime, 20); Assert.AreEqual(notes1[3].StartTime, 20); Assert.AreEqual(notes1[4].StartTime, 62); Assert.AreEqual(notes2[0].StartTime, 0); Assert.AreEqual(notes2[1].StartTime, 0); Assert.AreEqual(notes2[2].StartTime, 20); Assert.AreEqual(notes2[3].StartTime, 20); Assert.AreEqual(notes2[4].StartTime, 36); }
public void TestRoundDurations() { MidiTrack track = new MidiTrack(1); MidiNote note = new MidiNote(0, 0, 55, 45); track.AddNote(note); int[] starttimes = new int[] { 50, 90, 101, 123 }; foreach (int start in starttimes) { note = new MidiNote(start, 0, 55, 1); track.AddNote(note); } List<MidiTrack> tracks = new List<MidiTrack>(); tracks.Add(track); int quarternote = 40; MidiFile.RoundDurations(tracks, quarternote); Assert.AreEqual(track.Notes[0].Duration, 45); Assert.AreEqual(track.Notes[1].Duration, 40); Assert.AreEqual(track.Notes[2].Duration, 10); Assert.AreEqual(track.Notes[3].Duration, 20); Assert.AreEqual(track.Notes[4].Duration, 1); }
/// <summary> /// Parse's MTrk Chunk /// </summary> /// <param name="start"Index where the Chunk starts (excluding MTrk)</param> /// <param name="length">Length of the track</param> private void ParseTrack(int start, int length) { MidiTrack midiTrack = new MidiTrack(this); int ticks = 0; for (int i = start; i < start + length;) { //Update ticks int[] delta = GetDelta(i); ticks += MidiDecTime2normalTime(delta); //Move cursor i += delta.Length; if (_data[i] == 0x90 || _data[i] == 0x80) { ChannelVoice voice = ChannelVoiceFactory.Create(ticks, _data, i); midiTrack.Voice.Add(voice); i += 3; } else if (_data[i] == 0xFF) { i += ProcessMetaEvent(midiTrack, i); } } //Set total ticks midiTrack.Ticks = ticks; this._tracks.Add(midiTrack); }
/// <summary> /// Process meta events /// </summary> /// <param name="start">index of event</param> /// <returns>Length of event in bytes</returns> private int ProcessMetaEvent(MidiTrack midiTrack, int n) { byte flag = _data[n + 1]; byte len = 0; if (flag == 0x00) //FF 00 02 ss ss { len = 5; } else if(flag == 0x03) //Set Track name { len = _data[n + 2]; midiTrack.Name = Encoding.UTF8.GetString(_data, n+3, len); len += 3; } else if (flag >= 0x01 && flag <= 0x07) //FF nn len txt { len = _data[n + 2]; len += 3; } else if (flag == 0x20) //FF 20 01 cc { len = 5; } else if (flag == 0x2F) //FF 2F 00 (End of track) { len = 3; } else if (flag == 0x51) //FF 51 03 tt tt tt (Set tempo) { len = 6; byte[] tempo = { 0x00, _data[n + 3], _data[n + 4], _data[n + 5] }; Array.Reverse(tempo); this.Tempo = BitConverter.ToInt32(tempo, 0); } else if (flag == 0x54) //FF 54 05 hh mm ss fr ff { len = 8; } else if (flag == 0x58) //FF 58 04 nn dd cc bb { len = 7; } else if (flag == 0x59) //FF 58 04 nn dd cc bb { len = 7; } else if (flag == 0x7F) //FF 7F <len> <id> <data> { len = _data[n + 2]; len += 3; } return len; }
public static MidiTrackMetaData fromTrack(MidiTrack track) { if (track == null) { return null; } return new MidiTrackMetaData(track); }
static public void fixLoopCarryBack(MidiFile midiFile) { Debug.WriteLine("Fixing Loop Carryback Errors..."); // first of all we need to check if the MIDI actually loops // we only have to check the first track for the [ ] brackets in the marker Events bool hasLoopStart = false; long loopStartTick = 0; bool hasLoopEnd = false; long loopEndTick = 0; if (midiFile.midiTracks.Count == 0) { return; } MidiTrack metaTrack = midiFile.midiTracks[0]; for (int currentEvent = 0; currentEvent < metaTrack.midiEvents.Count; currentEvent++) { byte[] eventData = metaTrack.midiEvents[currentEvent].getEventData(); if (eventData[0] != 0xFF || eventData[1] != 0x6) // if event is META and marker type { continue; } if (eventData[2] == 0x1 && Encoding.ASCII.GetString(eventData, 3, 1) == "[") { hasLoopStart = true; loopStartTick = metaTrack.midiEvents[currentEvent].absoluteTicks; } else if (eventData[2] == 0x1 && Encoding.ASCII.GetString(eventData, 3, 1) == "]") { hasLoopEnd = true; loopEndTick = metaTrack.midiEvents[currentEvent].absoluteTicks; } } // we now got the loop points if there are any if (hasLoopStart == false || hasLoopEnd == false) { Debug.WriteLine("MIDI is not looped!"); return; } // now the carryback prevention is done for (int currentTrack = 0; currentTrack < midiFile.midiTracks.Count; currentTrack++) { MidiTrack trk = midiFile.midiTracks[currentTrack]; int midiChannel = getChannelNumberFromTrack(trk); // first of all we need to get the midi channel on the current track agbControllerState loopStartState = new agbControllerState(); if (trk.midiEvents.Count == 0) { continue; // skip the track if it has no events } if (midiChannel == -1 && currentTrack != 0) { continue; // skip track if it has no midi events and we don't need to consider the tempo track } int eventAtLoopStart = trk.midiEvents.Count - 1; #region recordStartState for (int currentEvent = 0; currentEvent < trk.midiEvents.Count; currentEvent++) { // now all events get recorded on continously update the loop start state if (trk.midiEvents[currentEvent].absoluteTicks > loopStartTick) { eventAtLoopStart = currentEvent; break; } byte[] eventData = trk.midiEvents[currentEvent].getEventData(); if (eventData[0] == 0xFF && eventData[1] == 0x51) // check if META tempo event occurs { loopStartState.Tempo[0] = eventData[3]; // set all tempo values loopStartState.Tempo[1] = eventData[4]; loopStartState.Tempo[2] = eventData[5]; } else if (eventData[0] >> 4 == 0xB) // is event a controller event? { if (eventData[1] == 0x1) // if MOD controller { loopStartState.Mod = eventData[2]; // save mod state } else if (eventData[1] == 0x7) { loopStartState.Volume = eventData[2]; // save volume state } else if (eventData[1] == 0xA) { loopStartState.Pan = eventData[2]; // save pan position } else if (eventData[1] == 0x14) { loopStartState.BendR = eventData[2]; // save pseudo AGB bendr } } else if (eventData[0] >> 4 == 0xC) // if voice change event { loopStartState.Voice = eventData[1]; } else if (eventData[0] >> 4 == 0xE) // if pitch bend { loopStartState.BendLSB = eventData[1]; loopStartState.BendMSB = eventData[2]; } } #endregion /* override all changes from the loop start state to the loop end state so we can * continue on with checking the trackdata for carryback errors we need to correct */ agbControllerState loopEndState = (agbControllerState)loopStartState.Clone(); // recorded loop start state, now record until int eventAtLoopEnd = trk.midiEvents.Count; #region recordLoopEndState for (int currentEvent = eventAtLoopStart; currentEvent < trk.midiEvents.Count; currentEvent++) { // now all events get recorded on continously update the loop start state if (trk.midiEvents[currentEvent].absoluteTicks >= loopEndTick) { // if the loop end occurs before the end of the event data set it's value manually eventAtLoopEnd = currentEvent; break; } byte[] eventData = trk.midiEvents[currentEvent].getEventData(); if (eventData[0] == 0xFF && eventData[1] == 0x51) // check if META tempo event occurs { loopEndState.Tempo[0] = eventData[3]; // set all tempo values loopEndState.Tempo[1] = eventData[4]; loopEndState.Tempo[2] = eventData[5]; } else if (eventData[0] >> 4 == 0xB) // is event a controller event? { if (eventData[1] == 0x1) { loopEndState.Mod = eventData[2]; // save mod state } else if (eventData[1] == 0x7) { loopEndState.Volume = eventData[2]; // save volume state } else if (eventData[1] == 0xA) { loopEndState.Pan = eventData[2]; // save pan position } else if (eventData[1] == 0x14) { loopEndState.BendR = eventData[2]; // save pseudo AGB bendr } } else if (eventData[0] >> 4 == 0xC) // if voice change event { loopEndState.Voice = eventData[1]; } else if (eventData[0] >> 4 == 0xE) // if pitch bend { loopEndState.BendLSB = eventData[1]; loopEndState.BendMSB = eventData[2]; } } #endregion // now we need to fill in the events at the loop end event slot "eventAtLoopEnd" // check if the values vary and set them accordingly if (!Enumerable.SequenceEqual(loopStartState.Tempo, loopEndState.Tempo) && loopStartState.Tempo[0] != 0 && loopStartState.Tempo[1] != 0 && loopStartState.Tempo[2] != 0) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MetaMidiEvent(loopStartTick, 0x51, loopStartState.Tempo)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MetaMidiEvent(loopStartTick, 0x51, loopStartState.Tempo)); } } // only do this fixing if the midi channel is actually defined (META only tracks are skipped here) if (midiChannel == -1) { continue; } // only fix if voice is a valid voice number (0-127) if (loopStartState.Voice != loopEndState.Voice && loopStartState.Voice != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Program, loopStartState.Voice, 0x0)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Program, loopStartState.Voice, 0x0)); } } // fix volume if (loopStartState.Volume != loopEndState.Volume && loopStartState.Volume != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0x7, loopStartState.Volume)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0x7, loopStartState.Volume)); } } // fix PAN if (loopStartState.Pan != loopEndState.Pan && loopStartState.Pan != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0xA, loopStartState.Pan)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0xA, loopStartState.Pan)); } } // fix BENDR if (loopStartState.BendR != loopEndState.BendR && loopStartState.BendR != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 20, loopStartState.BendR)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 20, loopStartState.BendR)); } } // fix MOD if (loopStartState.Mod != loopEndState.Mod && loopStartState.Mod != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0x1, loopStartState.Mod)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.Controller, 0x1, loopStartState.Mod)); } } // fix BEND if ((loopStartState.BendLSB != loopEndState.BendLSB || loopStartState.BendMSB != loopEndState.BendMSB) && loopStartState.BendLSB != 0xFF) { if (eventAtLoopStart >= trk.midiEvents.Count) { trk.midiEvents.Add(new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.PitchBend, loopStartState.BendLSB, loopStartState.BendMSB)); } else { trk.midiEvents.Insert(eventAtLoopStart, new MessageMidiEvent( loopStartTick, (byte)midiChannel, NormalType.PitchBend, loopStartState.BendLSB, loopStartState.BendMSB)); } } } }
/// <summary>Initialize the ProgramChange MIDI event message.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The delta-time since the previous message.</param> /// <param name="channel">The channel to which to write the message (0 through 15).</param> /// <param name="number">The instrument to which to change.</param> public ProgramChangeVoiceMidiEvent(MidiTrack owner, long deltaTime, byte channel, GeneralMidiInstrument number) : this(owner, deltaTime, channel, (byte)number) { }
private void loadStream(Stream stream, bool reloading = false) { byte[] array = new byte[4]; stream.Read(array, 0, 4); if (Encoding.UTF8.GetString(array, 0, array.Length) != "MThd") { return; } midiHeader = new MidiHeader(); stream.Read(array, 0, 4); Array.Reverse(array); BitConverter.ToInt32(array, 0); array = new byte[2]; stream.Read(array, 0, 2); Array.Reverse(array); midiHeader.setMidiFormat(BitConverter.ToInt16(array, 0)); stream.Read(array, 0, 2); Array.Reverse(array); int num = BitConverter.ToInt16(array, 0); stream.Read(array, 0, 2); Array.Reverse(array); int num2 = BitConverter.ToInt16(array, 0); midiHeader.DeltaTiming = (num2 & 0x7FFF); midiHeader.TimeFormat = (((num2 & 0x8000) > 0) ? MidiHelper.MidiTimeFormat.FamesPerSecond : MidiHelper.MidiTimeFormat.TicksPerBeat); bool flag = (!reloading || tracks.Count != num) ? true : false; if (flag) { tracks.Clear(); seqEvt.Events.Clear(); } int num3 = 0; while (true) { if (num3 >= num) { return; } MidiTrack midiTrack = null; MidiSequencerEvent midiSequencerEvent = null; if (flag) { midiTrack = new MidiTrack(); tracks.Add(midiTrack); midiSequencerEvent = new MidiSequencerEvent(); seqEvt.Events.Add(midiSequencerEvent); } else { midiTrack = tracks[num3]; midiTrack.TotalTime = 0uL; midiSequencerEvent = seqEvt.Events[num3]; } List <byte> list = new List <byte>(); List <byte> list2 = new List <byte>(); List <MidiEvent> list3 = new List <MidiEvent>(); list.Add(0); list2.Add(0); array = new byte[4]; stream.Read(array, 0, 4); if (Encoding.UTF8.GetString(array, 0, array.Length) != "MTrk") { break; } stream.Read(array, 0, 4); Array.Reverse(array); int num4 = BitConverter.ToInt32(array, 0); array = new byte[num4]; stream.Read(array, 0, num4); int i = 0; byte b = 0; int num5 = 0; MidiEvent midiEvent; for (; i < array.Length; list3.Add(midiEvent), tracks[num3].TotalTime = tracks[num3].TotalTime + midiEvent.deltaTime) { ushort numOfBytes = 0; uint data = BitConverter.ToUInt32(array, i); midiEvent = new MidiEvent(); midiEvent.deltaTime = GetTime(data, ref numOfBytes); i += 4 - (4 - numOfBytes); byte b2 = array[i]; int num6 = GetChannel(b2); if (b2 < 128) { b2 = b; num6 = num5; i--; } if (b2 != byte.MaxValue) { b2 = (byte)(b2 & 0xF0); } b = b2; num5 = num6; switch (b2) { case 128: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Off; i++; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; midiEvent.parameter1 = array[i++]; midiEvent.parameter2 = array[i++]; midiEvent.Parameters[1] = midiEvent.parameter1; midiEvent.Parameters[2] = midiEvent.parameter2; continue; case 144: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_On; i++; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; midiEvent.parameter1 = array[i++]; midiEvent.parameter2 = array[i++]; midiEvent.Parameters[1] = midiEvent.parameter1; midiEvent.Parameters[2] = midiEvent.parameter2; if (midiEvent.parameter2 == 0) { midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Off; } tracks[num3].NotesPlayed++; continue; case 160: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Aftertouch; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; i++; midiEvent.parameter1 = array[++i]; midiEvent.parameter2 = array[++i]; continue; case 176: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Controller; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; i++; midiEvent.parameter1 = array[i++]; midiEvent.parameter2 = array[i++]; midiEvent.Parameters[1] = midiEvent.parameter1; midiEvent.Parameters[2] = midiEvent.parameter2; continue; case 192: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Program_Change; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; i++; midiEvent.parameter1 = array[i++]; midiEvent.Parameters[1] = midiEvent.parameter1; if (midiEvent.channel != 9) { if (!list.Contains(midiEvent.parameter1)) { list.Add(midiEvent.parameter1); } } else if (!list2.Contains(midiEvent.parameter1)) { list2.Add(midiEvent.parameter1); } continue; case 208: midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Channel_Aftertouch; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; i++; midiEvent.parameter1 = array[++i]; continue; case 224: { midiEvent.midiChannelEvent = MidiHelper.MidiChannelEvent.Pitch_Bend; midiEvent.channel = (byte)num6; midiEvent.Parameters[0] = midiEvent.channel; i++; midiEvent.parameter1 = array[++i]; midiEvent.parameter2 = array[++i]; ushort parameter = midiEvent.parameter1; parameter = (ushort)(parameter << 7); parameter = (ushort)(parameter | midiEvent.parameter2); midiEvent.Parameters[1] = ((double)(int)parameter - 8192.0) / 8192.0; continue; } case byte.MaxValue: switch (array[++i]) { case 0: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequence_Number; i++; break; case 1: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Text_Event; i++; midiEvent.parameter1 = array[i++]; midiEvent.Parameters[0] = midiEvent.parameter1; midiEvent.Parameters[1] = Encoding.UTF8.GetString(array, i, array[i - 1]); i += array[i - 1]; break; case 2: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Copyright_Notice; i++; midiEvent.parameter1 = array[i++]; midiEvent.Parameters[0] = midiEvent.parameter1; midiEvent.Parameters[1] = Encoding.UTF8.GetString(array, i, array[i - 1]); i += array[i - 1]; break; case 3: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequence_Or_Track_Name; i++; midiEvent.parameter1 = array[i++]; midiEvent.Parameters[0] = midiEvent.parameter1; midiEvent.Parameters[1] = Encoding.UTF8.GetString(array, i, array[i - 1]); i += array[i - 1]; break; case 4: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Instrument_Name; i++; midiEvent.Parameters[0] = Encoding.UTF8.GetString(array, i + 1, array[i]); i += array[i] + 1; break; case 5: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Lyric_Text; i++; midiEvent.Parameters[0] = Encoding.UTF8.GetString(array, i + 1, array[i]); i += array[i] + 1; break; case 6: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Marker_Text; i++; midiEvent.Parameters[0] = Encoding.UTF8.GetString(array, i + 1, array[i]); i += array[i] + 1; break; case 7: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Cue_Point; i++; midiEvent.Parameters[0] = Encoding.UTF8.GetString(array, i + 1, array[i]); i += array[i] + 1; break; case 32: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Midi_Channel_Prefix_Assignment; i++; midiEvent.parameter1 = array[i++]; midiEvent.Parameters[0] = midiEvent.parameter1; midiEvent.Parameters[1] = array[i++]; break; case 47: midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.End_of_Track; i += 2; break; case 81: { midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Tempo; i++; midiEvent.Parameters[4] = array[i++]; byte[] array3 = new byte[4]; for (int num29 = 0; num29 < 3; num29++) { array3[num29 + 1] = array[num29 + i]; } i += 3; byte[] array4 = new byte[4]; for (int num30 = 0; num30 < 4; num30++) { array4[3 - num30] = array3[num30]; } uint num31 = BitConverter.ToUInt32(array4, 0); midiEvent.Parameters[0] = num31; break; } case 84: { midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Smpte_Offset; i++; int num16 = array[i++]; if (num16 >= 4) { for (int n = 0; n < 4; n++) { midiEvent.Parameters[n] = array[i++]; } } else { for (int num18 = 0; num18 < num16; num18++) { midiEvent.Parameters[num18] = array[i++]; } } for (int num20 = 4; num20 < num16; num20++) { i++; } break; } case 88: { midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Time_Signature; i++; int num22 = array[i++]; if (num22 >= 4) { for (int num23 = 0; num23 < 4; num23++) { midiEvent.Parameters[num23] = array[i++]; } } else { for (int num25 = 0; num25 < num22; num25++) { midiEvent.Parameters[num25] = array[i++]; } } for (int num27 = 4; num27 < num22; num27++) { i++; } break; } case 89: { midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Key_Signature; i++; int num10 = array[i++]; if (num10 >= 4) { for (int k = 0; k < 4; k++) { midiEvent.Parameters[k] = array[i++]; } } else { for (int l = 0; l < num10; l++) { midiEvent.Parameters[l] = array[i++]; } } for (int m = 4; m < num10; m++) { i++; } break; } case 127: { midiEvent.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequencer_Specific_Event; i++; midiEvent.Parameters[4] = array[i++]; byte[] array2 = new byte[(byte)midiEvent.Parameters[4]]; for (int j = 0; j < array2.Length; j++) { array2[j] = array[i++]; } midiEvent.Parameters[0] = array2; break; } } continue; case 240: break; default: continue; } for (; array[i] != 247; i++) { } i++; } tracks[num3].Programs = list.ToArray(); tracks[num3].DrumPrograms = list2.ToArray(); tracks[num3].MidiEvents = list3.ToArray(); num3++; } throw new Exception("Invalid track!"); }
/// <summary> /// Creates a notechart from the specified midi path and the actual charttype /// (i.e. ExpertSingle from notes.mid). Due to the overhead necessary to /// parse a midi file. I am going to cram all midi->chart operations into /// one function call. /// This function uses the Toub midi parser which is much faster than Sanford, /// but will throw an exception on certian midi files. /// </summary> /// <param name="chartSelection"> /// The information on which particular notechart to use. /// </param> /// <param name="chartInfo">The metadata on the chart.</param> /// <param name="BPMChanges">The list of BPM changes for this chart.</param> /// <returns> /// A filled out Notechart containing the needed information from the *.mid file. /// </returns> public static Notes ParseMidiInformationToub(ChartSelection chartSelection, Info chartInfo, List <BPMChange> BPMChanges) { Notes notechartToReturn = new Notes(); notechartToReturn.instrument = chartSelection.instrument; notechartToReturn.difficulty = chartSelection.difficulty; // The following two switch's are used to get the proper midi terminology for // the selected track and difficulty. string instrumentPart = null; string greenKey = null; string redKey = null; string yellowKey = null; string blueKey = null; string orangeKey = null; switch (chartSelection.instrument) { case "Single": instrumentPart = "PART GUITAR"; break; case "DoubleGuitar": instrumentPart = "PART GUITAR COOP"; break; case "DoubleBass": instrumentPart = "PART BASS"; break; case "Drums": instrumentPart = "PART DRUMS"; break; default: instrumentPart = "PART GUITAR"; break; } switch (chartSelection.difficulty) { case "Expert": greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; case "Hard": greenKey = "C7"; redKey = "C#7"; yellowKey = "D7"; blueKey = "D#7"; orangeKey = "E7"; break; case "Medium": greenKey = "C6"; redKey = "C#6"; yellowKey = "D6"; blueKey = "D#6"; orangeKey = "E6"; break; case "Easy": greenKey = "C5"; redKey = "C#5"; yellowKey = "D5"; blueKey = "D#5"; orangeKey = "E5"; break; default: greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; } MidiSequence mySequence = MidiSequence.Import(chartSelection.directory + "\\notes.mid"); MidiTrack[] myTracks = mySequence.GetTracks(); chartInfo.resolution = mySequence.Division; MidiTrack trackToUse = new MidiTrack(); uint totalTickValue = 0; // Go through each event in the first track (which contains the BPM changes) // and parse the resulting string. for (int i = 0; i < myTracks[0].Events.Count; i++) { Toub.Sound.Midi.MidiEvent currEvent = myTracks[0].Events[i]; string eventString = currEvent.ToString(); string[] splitEventString = eventString.Split('\t'); // Since ticks are stored relative to each other (e.g. 300 ticks // until next note), we must maintain the total tick amout. totalTickValue += Convert.ToUInt32(splitEventString[1]); if (splitEventString[0] == "Tempo") { // In midi files, bpm chages are stored as "microseconds per quarter note" // and must be converted to BPM, and then into the non decimal format the game // uses. double currBPMDouble = 60000000 / Convert.ToDouble(splitEventString[3]); uint BPMToAdd = (uint)(currBPMDouble * 1000); BPMChanges.Add(new BPMChange(totalTickValue, BPMToAdd)); } } trackToUse = new MidiTrack(); // Find the specified instrument's track foreach (MidiTrack currTrack in myTracks) { string trackHeader = currTrack.Events[0].ToString(); string[] splitHeader = trackHeader.Split('\t'); // -If we come across a "T1 GEMS" track, we're in GH1 territory. // -GH2/FoF has both PART BASS and PART RHYTHM (one or the other depending // on the chart). if (((splitHeader[3] == instrumentPart) || (splitHeader[3] == "T1 GEMS")) || ((splitHeader[3] == "PART RHYTHM") && (instrumentPart == "PART BASS"))) { trackToUse = currTrack; } } totalTickValue = 0; uint currTickValue = 0; Note currNote = new Note(); bool blankNote = true; // Scan through and record every note specific to the selected difficulty for (int i = 0; i < trackToUse.Events.Count; i++) { string currEvent = trackToUse.Events[i].ToString(); string[] splitEvent = currEvent.Split('\t'); currTickValue = Convert.ToUInt32(splitEvent[1]); totalTickValue += currTickValue; // We need to specify wether a note is blank or not so we don't add // blank notes from other difficulties into the chart, but if we have // a filled out note, any nonzero tick value means we are moving to a // new note, so we must cut our ties and add this note to the chart. if ((currTickValue != 0) && !blankNote) { notechartToReturn.notes.Add(currNote); currNote = new Note(); blankNote = true; } // The "0x64" I think means "not was hit." There is another // set of notes that use "0x00" that all appear slightly after // the "0x64" notes. if ((splitEvent[0] == "NoteOn") && (splitEvent[4] != "0x00")) { // Only consider notes within the octave our difficulty is in. if ((splitEvent[3] == greenKey) || (splitEvent[3] == redKey) || (splitEvent[3] == yellowKey) || (splitEvent[3] == blueKey) || (splitEvent[3] == orangeKey)) { // If it's a new note, we need to setup the tick value of it. if (blankNote) { currNote.tickValue = totalTickValue; blankNote = false; } if (splitEvent[3] == greenKey) { currNote.addNote(0); } else if (splitEvent[3] == redKey) { currNote.addNote(1); } else if (splitEvent[3] == yellowKey) { currNote.addNote(2); } else if (splitEvent[3] == blueKey) { currNote.addNote(3); } else if (splitEvent[3] == orangeKey) { currNote.addNote(4); } } } } return(notechartToReturn); }
public override void ExecuteEvent(MidiTrack track) { track.SetTempo(tempo); }
private void WriteMidiTrack(MidiTrack obj) { track_strings = new List <string>(); Write((byte)1); if (first_track || obj.Name == "EVENTS") { Write(-1); } else { Write(0); } first_track = false; // Subtract 1 for the end-of-track event Write(obj.Messages.Count - 1); uint ticks = 0; foreach (var m in obj.Messages) { byte kind, d1, d2, d3; switch (m) { case NoteOffEvent e: kind = 1; d1 = (byte)(0x80 | e.Channel); d2 = e.Key; d3 = e.Velocity; break; case NoteOnEvent e: kind = 1; d1 = (byte)(0x90 | e.Channel); d2 = e.Key; d3 = e.Velocity; break; case ControllerEvent e: kind = 1; d1 = (byte)(0xB0 | e.Channel); d2 = e.Controller; d3 = e.Value; break; case ProgramChgEvent e: kind = 1; d1 = (byte)(0xC0 | e.Channel); d2 = e.Program; d3 = 0; break; case ChannelPressureEvent e: kind = 1; d1 = (byte)(0xD0 | e.Channel); d2 = e.Pressure; d3 = 0; break; case PitchBendEvent e: kind = 1; d1 = (byte)(0xE0 | e.Channel); d2 = (byte)(e.Bend & 0xFF); d3 = (byte)(e.Bend >> 8); break; case TempoEvent e: kind = 2; d1 = (byte)(e.MicrosPerQn >> 16); d2 = (byte)(e.MicrosPerQn & 0xFFU); d3 = (byte)((e.MicrosPerQn >> 8) & 0xFFU); break; case TimeSignature e: kind = 4; d1 = e.Numerator; d2 = (byte)(1 << e.Denominator); d3 = 0; break; case MetaTextEvent e: kind = 8; var idx = GetString(e.Text); d2 = (byte)(idx & 0xFF); d3 = (byte)(idx >> 8); switch (e) { case TextEvent x: d1 = 1; break; case TrackName x: d1 = 3; break; case Lyric x: d1 = 5; break; default: d1 = 1; break; } break; case EndOfTrackEvent e: continue; default: throw new Exception("Unknown Midi Message type"); } ticks += m.DeltaTime; Write(ticks); Write(kind); Write(d1); Write(d2); Write(d3); } Write(track_strings.Count); track_strings.ForEach(Write); }
/** Return true if this track contains multiple channels. * If a MidiFile contains only one track, and it has multiple channels, * then we treat each channel as a separate track. */ static bool HasMultipleChannels(MidiTrack track) { int channel = track.Notes[0].Channel; foreach (MidiNote note in track.Notes) { if (note.Channel != channel) { return true; } } return false; }
//*************************************************************** // PUBLIC UTILITIES //*************************************************************** //--------------------------------------------------------------- // returns list of drumset instrument names //--------------------------------------------------------------- /* ## TO DO * function getDrumset(){ * return array( * 35=>'Acoustic Bass Drum', * 36=>'Bass Drum 1', * 37=>'Side Stick', * 38=>'Acoustic Snare', * 39=>'Hand Clap', * 40=>'Electric Snare', * 41=>'Low Floor Tom', * 42=>'Closed Hi-Hat', * 43=>'High Floor Tom', * 44=>'Pedal Hi-Hat', * 45=>'Low Tom', * 46=>'Open Hi-Hat', * 47=>'Low Mid Tom', * 48=>'High Mid Tom', * 49=>'Crash Cymbal 1', * 50=>'High Tom', * 51=>'Ride Cymbal 1', * 52=>'Chinese Cymbal', * 53=>'Ride Bell', * 54=>'Tambourine', * 55=>'Splash Cymbal', * 56=>'Cowbell', * 57=>'Crash Cymbal 2', * 58=>'Vibraslap', * 59=>'Ride Cymbal 2', * 60=>'High Bongo', * 61=>'Low Bongo', * 62=>'Mute High Conga', * 63=>'Open High Conga', * 64=>'Low Conga', * 65=>'High Timbale', * 66=>'Low Timbale', * //35..66 * 67=>'High Agogo', * 68=>'Low Agogo', * 69=>'Cabase', * 70=>'Maracas', * 71=>'Short Whistle', * 72=>'Long Whistle', * 73=>'Short Guiro', * 74=>'Long Guiro', * 75=>'Claves', * 76=>'High Wood Block', * 77=>'Low Wood Block', * 78=>'Mute Cuica', * 79=>'Open Cuica', * 80=>'Mute Triangle', * 81=>'Open Triangle'); * } */ //--------------------------------------------------------------- // returns list of standard drum kit names //--------------------------------------------------------------- /* ## TO DO * function getDrumkitList(){ * return array( * 1 => 'Dry', * 9 => 'Room', * 19 => 'Power', * 25 => 'Electronic', * 33 => 'Jazz', * 41 => 'Brush', * 57 => 'SFX', * 128 => 'Default' * ); * } */ //--------------------------------------------------------------- // returns list of note names //--------------------------------------------------------------- /* * function getNoteList(){ * //note 69 (A6) = A440 * //note 60 (C6) = Middle C * return array( * //Do Re Mi Fa So La Ti * 'C0', 'Cs0', 'D0', 'Ds0', 'E0', 'F0', 'Fs0', 'G0', 'Gs0', 'A0', 'As0', 'B0', * 'C1', 'Cs1', 'D1', 'Ds1', 'E1', 'F1', 'Fs1', 'G1', 'Gs1', 'A1', 'As1', 'B1', * 'C2', 'Cs2', 'D2', 'Ds2', 'E2', 'F2', 'Fs2', 'G2', 'Gs2', 'A2', 'As2', 'B2', * 'C3', 'Cs3', 'D3', 'Ds3', 'E3', 'F3', 'Fs3', 'G3', 'Gs3', 'A3', 'As3', 'B3', * 'C4', 'Cs4', 'D4', 'Ds4', 'E4', 'F4', 'Fs4', 'G4', 'Gs4', 'A4', 'As4', 'B4', * 'C5', 'Cs5', 'D5', 'Ds5', 'E5', 'F5', 'Fs5', 'G5', 'Gs5', 'A5', 'As5', 'B5', * 'C6', 'Cs6', 'D6', 'Ds6', 'E6', 'F6', 'Fs6', 'G6', 'Gs6', 'A6', 'As6', 'B6', * 'C7', 'Cs7', 'D7', 'Ds7', 'E7', 'F7', 'Fs7', 'G7', 'Gs7', 'A7', 'As7', 'B7', * 'C8', 'Cs8', 'D8', 'Ds8', 'E8', 'F8', 'Fs8', 'G8', 'Gs8', 'A8', 'As8', 'B8', * 'C9', 'Cs9', 'D9', 'Ds9', 'E9', 'F9', 'Fs9', 'G9', 'Gs9', 'A9', 'As9', 'B9', * 'C10','Cs10','D10','Ds10','E10','F10','Fs10','G10'); * } */ #endregion private MidiTrack parseTrack(byte[] data, int trackNumber) { MidiTrack track = new MidiTrack(); int trackLen = data.Length; int position = 0; long currentTicks = 0; int currentDelta; byte eventType; int eventTypeHigh; int eventTypeLow; byte meta; int num; int len; byte tmp; byte c; string txt; MidiEvent evt; int currentTempo = 0; long lastTempoTicks = -1; while (position < trackLen) { // timedelta currentDelta = readVarLen(ref data, ref position); currentTicks += currentDelta; eventType = data[position]; eventTypeHigh = (eventType >> 4); eventTypeLow = (eventType - eventTypeHigh * 16); switch (eventTypeHigh) { case MidiEvents.EVT_PROGRAM_CHANGE: //PrCh = ProgramChange evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1]); evt.Description = " PrCh ch=" + evt.Channel + " p=" + evt.Param0; track.Add(evt); position += 2; break; case MidiEvents.EVT_NOTE_ON: //On evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1], data[position + 2]); evt.Description = " On ch=" + evt.Channel + " n=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 3; break; case MidiEvents.EVT_NOTE_OFF: //Off evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1], data[position + 2]); evt.Description = " Off ch=" + evt.Channel + " n=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 3; break; case MidiEvents.EVT_POLY_PRESSURE: //PoPr = PolyPressure evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1], data[position + 2]); evt.Description = " PoPr ch=" + evt.Channel + " n=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 3; break; case MidiEvents.EVT_CONTROLLER_CHANGE: //Par = ControllerChange evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1], data[position + 2]); evt.Description = " Par ch=" + evt.Channel + " c=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 3; break; case MidiEvents.EVT_CHANNEL_PRESSURE: //ChPr = ChannelPressure evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, data[position + 1]); evt.Description = " ChPr ch=" + evt.Channel + " v=" + evt.Param0; track.Add(evt); position += 2; break; case MidiEvents.EVT_PITCH_BEND: //Pb = PitchBend evt = new MidiEvent(currentTicks, eventTypeHigh, eventTypeLow + 1, (data[position + 1] & 0x7F) | ((data[position + 2] & 0x7F) << 7)); evt.Description = " Pb ch=" + evt.Channel + " v=" + evt.Param0; track.Add(evt); position += 3; break; default: switch (eventType) { case 0xFF: // Meta meta = data[position + 1]; switch (meta) { case MidiEvents.META_SEQUENCE_NUM: // sequence_number tmp = data[position + 2]; if (tmp == 0x00) { num = trackNumber; position += 3; } else { num = 1; position += 5; } evt = new MidiEvent(currentTicks, meta, -1, num); evt.isMetaEvent = true; evt.Description = " Seqnr " + evt.Param0; track.Add(evt); break; case MidiEvents.META_TEXT: // Meta Text case MidiEvents.META_COPYRIGHT: // Meta Copyright case MidiEvents.META_TRACK_NAME: // Meta TrackName ???sequence_name??? case MidiEvents.META_INSTRUMENT_NAME: // Meta InstrumentName case MidiEvents.META_LYRICS: // Meta Lyrics case MidiEvents.META_MARKER: // Meta Marker case MidiEvents.META_CUE: // Meta Cue string[] texttypes = new string[7] { "Text", "Copyright", "TrkName", "InstrName", "Lyric", "Marker", "Cue" }; string textType = texttypes[meta - 1]; position += 2; len = readVarLen(ref data, ref position); if ((len + position) > trackLen) { throw new InvalidDataException("Meta " + textType + " has corrupt variable length field (" + len + ") [track: " + trackNumber + " dt: " + currentDelta + "]"); } txt = Encoding.ASCII.GetString(data, position, len); if (MidiEvents.META_TEXT == meta || MidiEvents.META_TRACK_NAME == meta || MidiEvents.META_MARKER == meta) { comment.Append(txt).Append(Settings.InternalValueSeparator); } else if (MidiEvents.META_COPYRIGHT == meta) { tagData.IntegrateValue(TagData.TAG_FIELD_COPYRIGHT, txt); } evt = new MidiEvent(currentTicks, meta, -1, meta - 1); evt.isMetaEvent = true; evt.Description = " Meta " + textType + " \"" + txt + "\""; track.Add(evt); position += len; break; case MidiEvents.META_CHANNEL_PREFIX: // ChannelPrefix evt = new MidiEvent(currentTicks, meta, -1, data[position + 3]); evt.isMetaEvent = true; evt.Description = " Meta ChannelPrefix " + evt.Param0; track.Add(evt); position += 4; break; case MidiEvents.META_CHANNEL_PREFIX_PORT: // ChannelPrefixOrPort evt = new MidiEvent(currentTicks, meta, -1, data[position + 3]); evt.isMetaEvent = true; evt.Description = " Meta ChannelPrefixOrPort " + evt.Param0; track.Add(evt); position += 4; break; case MidiEvents.META_TRACK_END: // Meta TrkEnd evt = new MidiEvent(currentTicks, meta, -1, -1); evt.isMetaEvent = true; evt.Description = " Meta TrkEnd"; track.Add(evt); track.Ticks = currentTicks; if (lastTempoTicks > -1) // there has been at least one tempo change in the track { track.Duration += (currentTicks - lastTempoTicks) * currentTempo; } else { track.Duration = currentTicks * this.tempo; } return(track); //ignore rest case MidiEvents.META_TEMPO: // Tempo // Adds (ticks since last tempo event)*current tempo to track duration if (lastTempoTicks > -1) { track.Duration += (currentTicks - lastTempoTicks) * currentTempo; } lastTempoTicks = currentTicks; currentTempo = data[position + 3] * 0x010000 + data[position + 4] * 0x0100 + data[position + 5]; if (0 == currentTempo) { currentTempo = DEFAULT_TEMPO; } evt = new MidiEvent(currentTicks, meta, -1, currentTempo); evt.isMetaEvent = true; evt.Description = " Meta Tempo " + evt.Param0 + " (duration :" + track.Duration + ")"; track.Add(evt); // Sets song tempo as last tempo event of 1st track // according to some MIDI files convention if (0 == trackNumber /* && 0 == this.tempo*/) { this.tempo = currentTempo; this.tempoMsgNum = track.events.Count - 1; } position += 6; break; case MidiEvents.META_SMPTE_OFFSET: // SMPTE offset byte h = data[position + 3]; byte m = data[position + 4]; byte s = data[position + 5]; byte f = data[position + 6]; byte fh = data[position + 7]; // TODO : store the arguments in a solid structure within MidiEvent evt = new MidiEvent(currentTicks, meta, -1, -1); evt.isMetaEvent = true; evt.Description = " Meta SMPTE " + h + " " + m + " " + s + " " + f + " " + fh; track.Add(evt); position += 8; break; case MidiEvents.META_TIME_SIGNATURE: // TimeSig byte z = data[position + 3]; int t = 2 ^ data[position + 4]; byte mc = data[position + 5]; c = data[position + 6]; // TODO : store the arguments in a solid structure within MidiEvent evt = new MidiEvent(currentTicks, meta, -1, -1); evt.isMetaEvent = true; evt.Description = " Meta TimeSig " + z + "/" + t + " " + mc + " " + c; track.Add(evt); position += 7; break; case MidiEvents.META_KEY_SIGNATURE: // KeySig evt = new MidiEvent(currentTicks, meta, -1, data[position + 3], data[position + 4]); evt.isMetaEvent = true; evt.Description = " Meta KeySig vz=" + evt.Param0 + " " + (evt.Param1 == 0 ? "major" : "minor"); track.Add(evt); position += 5; break; case MidiEvents.META_SEQUENCER_DATA: // Sequencer specific data position += 2; len = readVarLen(ref data, ref position); if ((len + position) > trackLen) { throw new InvalidDataException("SeqSpec has corrupt variable length field (" + len + ") [track: " + trackNumber + " dt: " + currentDelta + "]"); } position -= 3; { //String str = Encoding.ASCII.GetString(data, position + 3, len); //data.=' '.sprintf("%02x",(byte)($data[$p+3+$i])); evt = new MidiEvent(currentTicks, meta, -1, currentTempo); evt.isMetaEvent = true; evt.Description = " Meta SeqSpec"; track.Add(evt); } position += len + 3; break; default: // "unknown" Meta-Events byte metacode = data[position + 1]; position += 2; len = readVarLen(ref data, ref position); if ((len + position) > trackLen) { throw new InvalidDataException("Meta " + metacode + " has corrupt variable length field (" + len + ") [track: " + trackNumber + " dt: " + currentDelta + "]"); } position -= 3; { String str = Encoding.ASCII.GetString(data, position + 3, len); //sprintf("%02x",(byte)($data[$p+3+$i])); evt = new MidiEvent(currentTicks, meta, -1, currentTempo); evt.isMetaEvent = true; evt.Description = " Meta 0x" + metacode + " " + str; track.Add(evt); } position += len + 3; break; } // switch meta break; // End Meta case MidiEvents.EVT_SYSEX: // SysEx position += 1; len = readVarLen(ref data, ref position); if ((len + position) > trackLen) { throw new InvalidDataException("SysEx has corrupt variable length field (" + len + ") [track: " + trackNumber + " dt: " + currentDelta + " p: " + position + "]"); } { //String str = "f0" + Encoding.ASCII.GetString(data, position + 2, len); //str+=' '.sprintf("%02x",(byte)(data[p+2+i])); evt = new MidiEvent(currentTicks, eventTypeHigh, -1, currentTempo); evt.isMetaEvent = true; evt.Description = " SysEx"; track.Add(evt); } position += len; break; default: // Repetition of last event? if ((track.LastEvent.Type == MidiEvents.EVT_NOTE_ON) || (track.LastEvent.Type == MidiEvents.EVT_NOTE_OFF)) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, data[position], data[position + 1]); evt.Description = " " + (track.LastEvent.Type == MidiEvents.EVT_NOTE_ON ? "On" : "Off") + " ch=" + evt.Channel + " n=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 2; } else if (track.LastEvent.Type == MidiEvents.EVT_PROGRAM_CHANGE) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, data[position]); evt.Description = " PrCh ch=" + evt.Channel + " p=" + evt.Param0; track.Add(evt); position += 1; } else if (track.LastEvent.Type == MidiEvents.EVT_POLY_PRESSURE) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, data[position + 1], data[position + 2]); evt.Description = " PoPr ch=" + evt.Channel + " n=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 2; } else if (track.LastEvent.Type == MidiEvents.EVT_CHANNEL_PRESSURE) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, data[position]); evt.Description = " ChPr ch=" + evt.Channel + " v=" + evt.Param0; track.Add(evt); position += 1; } else if (track.LastEvent.Type == MidiEvents.EVT_CONTROLLER_CHANGE) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, data[position], data[position + 1]); evt.Description = " Par ch=" + evt.Channel + " c=" + evt.Param0 + " v=" + evt.Param1; track.Add(evt); position += 2; } else if (track.LastEvent.Type == MidiEvents.EVT_PITCH_BEND) { evt = new MidiEvent(currentTicks, track.LastEvent.Type, track.LastEvent.Channel, (data[position] & 0x7F) | ((data[position + 1] & 0x7F) << 7)); evt.Description = " Pb ch=" + evt.Channel + " v=" + evt.Param0; track.Add(evt); position += 2; } //default: // MM: ToDo: Repetition of SysEx and META-events? with <last>?? \n"; // _err("unknown repetition: $last"); break; } // eventType break; } // $high } // while p < trackLen track.Ticks = currentTicks; if (lastTempoTicks > -1) { track.Duration += (currentTicks - lastTempoTicks) * currentTempo; } else { track.Duration = currentTicks * this.tempo; } return(track); }
/** Split the given track into multiple tracks, separating each * channel into a separate track. */ private static List<MidiTrack> SplitChannels(MidiTrack origtrack, List<MidiEvent> events) { /* Find the instrument used for each channel */ int[] channelInstruments = new int[16]; foreach (MidiEvent mevent in events) { if (mevent.EventFlag == EventProgramChange) { channelInstruments[mevent.Channel] = mevent.Instrument; } } channelInstruments[9] = 128; /* Channel 9 = Percussion */ List<MidiTrack> result = new List<MidiTrack>(); foreach (MidiNote note in origtrack.Notes) { bool foundchannel = false; foreach (MidiTrack track in result) { if (note.Channel == track.Notes[0].Channel) { foundchannel = true; track.AddNote(note); } } if (!foundchannel) { MidiTrack track = new MidiTrack(result.Count + 1); track.AddNote(note); track.Instrument = channelInstruments[note.Channel]; result.Add(track); } } return result; }
public SequencerPointer(MidiTrack track, int eventIndex) { Track = track; EventIndex = eventIndex; }
/** Combine the notes in the given tracks into a single MidiTrack. * The individual tracks are already sorted. To merge them, we * use a mergesort-like algorithm. */ public static MidiTrack CombineToSingleTrack(List<MidiTrack> tracks) { /* Add all notes into one track */ MidiTrack result = new MidiTrack(1); if (tracks.Count == 0) { return result; } else if (tracks.Count == 1) { MidiTrack track = tracks[0]; foreach (MidiNote note in track.Notes) { result.AddNote(note); } return result; } int[] noteindex = new int[64]; int[] notecount = new int[64]; for (int tracknum = 0; tracknum < tracks.Count; tracknum++) { noteindex[tracknum] = 0; notecount[tracknum] = tracks[tracknum].Notes.Count; } MidiNote prevnote = null; while (true) { MidiNote lowestnote = null; int lowestTrack = -1; for (int tracknum = 0; tracknum < tracks.Count; tracknum++) { MidiTrack track = tracks[tracknum]; if (noteindex[tracknum] >= notecount[tracknum]) { continue; } MidiNote note = track.Notes[ noteindex[tracknum] ]; if (lowestnote == null) { lowestnote = note; lowestTrack = tracknum; } else if (note.StartTime < lowestnote.StartTime) { lowestnote = note; lowestTrack = tracknum; } else if (note.StartTime == lowestnote.StartTime && note.Number < lowestnote.Number) { lowestnote = note; lowestTrack = tracknum; } } if (lowestnote == null) { /* We've finished the merge */ break; } noteindex[lowestTrack]++; if ((prevnote != null) && (prevnote.StartTime == lowestnote.StartTime) && (prevnote.Number == lowestnote.Number) ) { /* Don't add duplicate notes, with the same start time and number */ if (lowestnote.Duration > prevnote.Duration) { prevnote.Duration = lowestnote.Duration; } } else { result.AddNote(lowestnote); prevnote = lowestnote; } } return result; }
public void LoadSequence() { _midiFile.MidiTracks.Clear(); int cMidiChn = 0; // process each track for (int currentTrack = 0; currentTrack < _smdInfo.NumTracks; currentTrack++) { long cTick = 0; int cOctave = 0; byte cProgram = 0; bool bendIsInit = false; int lastDelay = 0; int lastNoteLength = 1; MidiTrack cTrack; _midiFile.MidiTracks.Add(cTrack = new MidiTrack()); if (currentTrack == 0) { cTrack.MidiEvents.Add( new MetaMessage( 0, 3, // sequence name Encoding.ASCII.GetBytes(_smdInfo.SequenceName) ) ); } _smdReader.BaseStream.Position = _smdInfo.TrackOffset[currentTrack] + 0x14; // skip the first 4 bytes of the track data while (true) { byte cmd = _smdReader.ReadByte(); if (cmd >= 0x0 && cmd <= 0x7F) // note { int noteArgument = _smdReader.ReadByte(); int octaveDelta = ((noteArgument >> 4) & 0x3) - 2; cOctave += octaveDelta; byte midiKey = (byte)(cOctave * 12 + (noteArgument & 0xF)); int numArgs = (noteArgument >> 6) & 0x3; int noteLength = 0; if (numArgs == 0) { noteLength = lastNoteLength; } else { for (int i = 0; i < numArgs; i++) { noteLength = (noteLength << 8) + _smdReader.ReadByte(); } lastNoteLength = noteLength; } if (midiKey >= 0 && midiKey <= 0x7F) { cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), (cProgram == 127) ? drummap[midiKey] : (byte)(midiKey + (sbyte)transpose[cProgram]), cmd, NormalType.NoteOn ) ); cTrack.MidiEvents.Add( new MidiMessage( Math.Max(0, cTick + (noteLength * 2) - 1), (byte)(cMidiChn & 0xF), (cProgram == 127) ? drummap[midiKey] : (byte)(midiKey + (sbyte)transpose[cProgram]), 0, NormalType.NoteOff ) ); } else { Console.WriteLine("Dropped invalid note event on track " + currentTrack); } } else if (cmd >= 0x80 && cmd <= 0x8F) // if delay { long delayTo = cTick + (2 * (lastDelay = _delayLut[cmd - 0x80])); cTick = delayTo; } else if (cmd == 0x90) { long delayTo = cTick + (lastDelay * 2); cTick = delayTo; } else if (cmd == 0x91) { lastDelay += _smdReader.ReadSByte(); if (lastDelay < 0) { lastDelay = 0; } long delayTo = cTick + (lastDelay * 2); cTick = delayTo; } else if (cmd == 0x92) { long delayTo = cTick + (2 * (lastDelay = _smdReader.ReadByte())); cTick = delayTo; } else if (cmd == 0x93) { int a = _smdReader.ReadByte(); int b = _smdReader.ReadByte(); long delayTo = cTick + (2 * (lastDelay = (a | (b << 8)))); cTick = delayTo; } else if (cmd == 0x98) // FINE | LOOP { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes("end") ) ); break; } else if (cmd == 0x99) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes("loopStart") ) ); } else if (cmd == 0x9C) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0x9D) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2")) ) ); } else if (cmd == 0xA0) // set octave { cOctave = _smdReader.ReadByte(); } else if (cmd == 0xA4) { byte bpm = _smdReader.ReadByte(); int microsPerBeat = 60 * 1000 * 1000 / bpm; byte[] speed = new byte[3]; speed[0] = (byte)(microsPerBeat >> 16); speed[1] = (byte)((microsPerBeat >> 8) & 0xFF); speed[2] = (byte)(microsPerBeat & 0xFF); cTrack.MidiEvents.Add( new MetaMessage( cTick, 0x51, speed ) ); } else if (cmd == 0xA8) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xA9) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xAA) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xAC) { //hasEvents = true; cProgram = _smdReader.ReadByte(); cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), midimap[cProgram], 0, // dummy NormalType.Program ) ); } else if (cmd == 0xB2) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xB4) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xB5) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xBE) { cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), 1, // mod wheel _smdReader.ReadByte(), NormalType.Controller ) ); } else if (cmd == 0xBF) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xC0) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2")) ) ); } else if (cmd == 0xD0) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xD1) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xD2) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xD4) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xD6) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xD7) { //hasEvents = true; byte a = _smdReader.ReadByte(); byte b = _smdReader.ReadByte(); // insert bend rage if it hasn't been yet if (!bendIsInit) { // rp msb = 0; rp lsb = 0; de msb = 8; de lsb = 0 cTrack.MidiEvents.Add(new MidiMessage(0, (byte)(cMidiChn & 0xF), 101, 0, NormalType.Controller)); cTrack.MidiEvents.Add(new MidiMessage(0, (byte)(cMidiChn & 0xF), 100, 0, NormalType.Controller)); cTrack.MidiEvents.Add(new MidiMessage(0, (byte)(cMidiChn & 0xF), 6, 8, NormalType.Controller)); cTrack.MidiEvents.Add(new MidiMessage(0, (byte)(cMidiChn & 0xF), 38, 0, NormalType.Controller)); bendIsInit = true; } ushort pitch = (ushort)(((a << 8) | b) + 0x8000); byte msb = (byte)(pitch >> 9); byte lsb = (byte)((pitch >> 2) & 0x7F); cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), lsb, msb, NormalType.PitchBend ) ); /*cTrack.MidiEvents.Add( * new MetaMessage( * cTick, * 1, * Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + a.ToString("X2") + ":" + b.ToString("X2") + ) + ) + );*/ } else if (cmd == 0xDB) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else if (cmd == 0xDC) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xE0) { //hasEvents = true; cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), 7, // volume controller _smdReader.ReadByte(), NormalType.Controller ) ); } else if (cmd == 0xE2) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xE3) { //hasEvents = true; cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), 11, // expression controller _smdReader.ReadByte(), NormalType.Controller ) ); } else if (cmd == 0xE8) { //hasEvents = true; cTrack.MidiEvents.Add( new MidiMessage( cTick, (byte)(cMidiChn & 0xF), 0xA, // PAN controller _smdReader.ReadByte(), NormalType.Controller ) ); } else if (cmd == 0xEA) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2") ) ) ); } else if (cmd == 0xF6) { cTrack.MidiEvents.Add( new MetaMessage( cTick, 1, // text event Encoding.ASCII.GetBytes(cmd.ToString("X2") + ":" + _smdReader.ReadByte().ToString("X2")) ) ); } else { throw new Exception("Command " + cmd.ToString("X2") + " at 0x" + _smdReader.BaseStream.Position.ToString(("X8")) + " not known"); } } // end while cMidiChn++; } _midiFile.SortTrackEvents(); FixMidiChannels(); }
/* Split the given MidiTrack into two tracks, top and bottom. * The highest notes will go into top, the lowest into bottom. * This function is used to split piano songs into left-hand (bottom) * and right-hand (top) tracks. */ public static List<MidiTrack> SplitTrack(MidiTrack track, int measurelen) { List<MidiNote> notes = track.Notes; int count = notes.Count; MidiTrack top = new MidiTrack(1); MidiTrack bottom = new MidiTrack(2); List<MidiTrack> result = new List<MidiTrack>(2); result.Add(top); result.Add(bottom); if (count == 0) return result; int prevhigh = 76; /* E5, top of treble staff */ int prevlow = 45; /* A3, bottom of bass staff */ int startindex = 0; foreach (MidiNote note in notes) { int high, low, highExact, lowExact; int number = note.Number; high = low = highExact = lowExact = number; while (notes[startindex].EndTime < note.StartTime) { startindex++; } /* I've tried several algorithms for splitting a track in two, * and the one below seems to work the best: * - If this note is more than an octave from the high/low notes * (that start exactly at this start time), choose the closest one. * - If this note is more than an octave from the high/low notes * (in this note's time duration), choose the closest one. * - If the high and low notes (that start exactly at this starttime) * are more than an octave apart, choose the closest note. * - If the high and low notes (that overlap this starttime) * are more than an octave apart, choose the closest note. * - Else, look at the previous high/low notes that were more than an * octave apart. Choose the closeset note. */ FindHighLowNotes(notes, measurelen, startindex, note.StartTime, note.EndTime, ref high, ref low); FindExactHighLowNotes(notes, startindex, note.StartTime, ref highExact, ref lowExact); if (highExact - number > 12 || number - lowExact > 12) { if (highExact - number <= number - lowExact) { top.AddNote(note); } else { bottom.AddNote(note); } } else if (high - number > 12 || number - low > 12) { if (high - number <= number - low) { top.AddNote(note); } else { bottom.AddNote(note); } } else if (highExact - lowExact > 12) { if (highExact - number <= number - lowExact) { top.AddNote(note); } else { bottom.AddNote(note); } } else if (high - low > 12) { if (high - number <= number - low) { top.AddNote(note); } else { bottom.AddNote(note); } } else { if (prevhigh - number <= number - prevlow) { top.AddNote(note); } else { bottom.AddNote(note); } } /* The prevhigh/prevlow are set to the last high/low * that are more than an octave apart. */ if (high - low > 12) { prevhigh = high; prevlow = low; } } top.Notes.Sort(track.Notes[0]); bottom.Notes.Sort(track.Notes[0]); return result; }
void Output(OutputType type) { switch (type) { case OutputType.Midi: /*List<MidiEvent> midiEvents = new List<MidiEvent> (); * * double max = 0; * for (int i = 0; i < notes.Count; i++) { * double d = notes[i].duration * 4; * double t1 = notes[i].time * 4; * double t2 = t1 + d; * max = t2 > max ? t2 : max; * * NoteOnEvent note = new NoteOnEvent (Convert.ToInt64 (t1), 1, notes[i].noteNumber + 40, 127, Convert.ToInt32(d)); * midiEvents.Add (note); * } * MidiEvent endMarker = new NoteEvent (Convert.ToInt64(max), 1, MidiCommandCode.StopSequence, 0, 0); * midiEvents.Add (endMarker); * * MidiEventCollection collection = new MidiEventCollection (0, 1); * collection.AddTrack (midiEvents); * * MidiFile.Export ("C:/Users/Jonas/OneDrive/GIP/Proef/Output/midi.mid", collection);*/ MidiTrack track = new MidiTrack(); long maxTime = 0; //string path = "C:/Users/Jonas/Desktop/result.txt"; double previousTime = 0; for (int i = 0; i < notes.Count; i++) { string path = "C:/Users/Jonas/Desktop/result.txt"; File.AppendAllText(path, string.Format("{0} at {1}\n", DecodeNote(notes[i].noteNumber + 44, NoteNotation.Short), notes[i].time)); double d = 1; double t1 = notes[i].time * 20 - previousTime; double t2 = t1 + d; if (t1 < 0) { continue; } previousTime = t2; MidiEvent onEvent = new OnNoteVoiceMidiEvent(Convert.ToInt64(t1), 1, (byte)(44 + notes[i].noteNumber), 63); MidiEvent offEvent = new OffNoteVoiceMidiEvent(Convert.ToInt64(t2), 1, (byte)(44 + notes[i].noteNumber), 0); long t2long = (long)t2; maxTime = (t2long > maxTime) ? t2long : maxTime; track.Events.Add(onEvent); track.Events.Add(offEvent); } MidiEvent endMarker = new EndOfTrackMetaMidiEvent(maxTime); track.Events.Add(endMarker); MidiSequence sequence = new MidiSequence(Format.Zero, 1000); sequence.Tracks.Add(track); FileStream stream = new FileStream("C:/Users/Jonas/OneDrive/GIP/Proef/Output/midi.mid", FileMode.OpenOrCreate); sequence.Save(stream); stream.Close(); break; case OutputType.Audio: double longest = 0; for (int i = 0; i < notes.Count; i++) { double time = notes[i].time + notes[i].duration; longest = Math.Max(time, longest); } double[] newSamples = new double[(int)Math.Ceiling(longest * rawAudio.sampleRate)]; for (int i = 0; i < notes.Count; i++) { int startIndex = (int)Math.Floor(notes[i].time * rawAudio.sampleRate); int endIndex = (int)Math.Floor((notes[i].time + notes[i].duration) * rawAudio.sampleRate); double freq = 110 * Math.Pow(1.059463094359295, notes[i].noteNumber); double amp = notes[i].amplitude; for (int j = startIndex; j < endIndex; j++) { double time = j * rawAudio.SampleLength + notes[i].time; double value = Math.Sin(time * freq * 2 * Math.PI) * amp; //double maxAmp = Math.Min (1, 5 * Math.Min (Math.Abs (time - notes[i].time), Math.Abs (time - (notes[i].time + notes[i].duration)))); newSamples[j] += value; } } for (int i = 0; i < newSamples.Length; i++) { newSamples[i] = Math.Max(Math.Min(1, newSamples[i]), -1); } int avgSpread = 35; double[] smoothSamples = new double[newSamples.Length - avgSpread]; for (int i = 0; i < newSamples.Length - avgSpread; i++) { double tot = 0; for (int j = 0; j < avgSpread; j++) { tot += newSamples[i + j]; } smoothSamples[i] = tot / avgSpread; } Song song = new Song(smoothSamples, rawAudio.channels, rawAudio.sampleRate); // GIPIO.SaveSong (song, GIPIO.outputPath + "test.wav"); producedAudio = song; break; case OutputType.Wavelet: pixels = new uint[allAmps.Count * allAmps[0].Length]; for (int x = 0; x < allAmps.Count; x++) { for (int y = 0; y < allAmps[0].Length; y++) { double i = allAmps[x][y]; // i *= 2; i = Math.Min(1, i); // double r = -2 * (i - 1.0) * (2 * (i - 1.0)) + 1; // double g = -2 * (i - 0.5) * (2 * (i - 0.5)) + 1; // double b = -2 * (i - 0.0) * (2 * (i - 0.0)) + 1; double r, g, b; r = g = b = i; uint red = (uint)Math.Round(r * 255); uint green = (uint)Math.Round(g * 255); uint blue = (uint)Math.Round(b * 255); int index = (allAmps[0].Length - y - 1) * allAmps.Count + x; pixels[index] = (uint)((255 << 24) + (red << 16) + (green << 8) + blue); } } DrawWavelet(); break; } }
/** Parse the given Midi file, and return an instance of this MidiFile * class. After reading the midi file, this object will contain: * - The raw list of midi events * - The Time Signature of the song * - All the tracks in the song which contain notes. * - The number, starttime, and duration of each note. */ public void parse(MidiFileReader file, string filename) { string id; int len; this.filename = filename; tracks = new List<MidiTrack>(); trackPerChannel = false; id = file.ReadAscii(4); if (id != "MThd") { throw new MidiFileException("Doesn't start with MThd", 0); } len = file.ReadInt(); if (len != 6) { throw new MidiFileException("Bad MThd header", 4); } trackmode = file.ReadShort(); int num_tracks = file.ReadShort(); quarternote = file.ReadShort(); events = new List<MidiEvent>[num_tracks]; for (int tracknum = 0; tracknum < num_tracks; tracknum++) { events[tracknum] = ReadTrack(file); MidiTrack track = new MidiTrack(events[tracknum], tracknum); if (track.Notes.Count > 0) { tracks.Add(track); } } /* Get the length of the song in pulses */ foreach (MidiTrack track in tracks) { MidiNote last = track.Notes[track.Notes.Count-1]; if (this.totalpulses < last.StartTime + last.Duration) { this.totalpulses = last.StartTime + last.Duration; } } /* If we only have one track with multiple channels, then treat * each channel as a separate track. */ if (tracks.Count == 1 && HasMultipleChannels(tracks[0])) { tracks = SplitChannels(tracks[0], events[tracks[0].Number]); trackPerChannel = true; } CheckStartTimes(tracks); /* Determine the time signature */ int tempo = 0; int numer = 0; int denom = 0; foreach (List<MidiEvent> list in events) { foreach (MidiEvent mevent in list) { if (mevent.Metaevent == MetaEventTempo && tempo == 0) { tempo = mevent.Tempo; } if (mevent.Metaevent == MetaEventTimeSignature && numer == 0) { numer = mevent.Numerator; denom = mevent.Denominator; } } } if (tempo == 0) { tempo = 500000; /* 500,000 microseconds = 0.05 sec */ } if (numer == 0) { numer = 4; denom = 4; } timesig = new TimeSignature(numer, denom, quarternote, tempo); }
static public void addAgbCompatibleEvents(MidiFile midiFile, byte modType) { int[] channelNumber = new int[midiFile.midiTracks.Count]; for (int i = 0; i < midiFile.midiTracks.Count; i++) { channelNumber[i] = getChannelNumberFromTrack(midiFile.midiTracks[i]); } // add all MODT events Debug.WriteLine("Adding MODT and LFOS Events..."); for (int i = 0; i < midiFile.midiTracks.Count; i++) { if (channelNumber[i] == -1) { continue; // skip track if there is no regular MIDI commands } // add a MODT controller event in the beginning of the track and LFOS midiFile.midiTracks[i].midiEvents.Insert( 0, new MessageMidiEvent(0, (byte)channelNumber[i], NormalType.Controller, 21, 44)); midiFile.midiTracks[i].midiEvents.Insert( 0, new MessageMidiEvent(0, (byte)channelNumber[i], NormalType.Controller, 22, modType)); } Debug.WriteLine("Adding BENDR Events..."); for (int currentTrack = 0; currentTrack < midiFile.midiTracks.Count; currentTrack++) { MidiTrack trk = midiFile.midiTracks[currentTrack]; byte rpMSB = 0; byte rpLSB = 0; for (int currentEvent = 0; currentEvent < trk.midiEvents.Count; currentEvent++) { MessageMidiEvent ev; if (trk.midiEvents[currentEvent] is MessageMidiEvent) { ev = trk.midiEvents[currentEvent] as MessageMidiEvent; } else { continue; } if (ev.type != NormalType.Controller) { continue; } switch (ev.parameter1) { case 0x64: // midi RP rpLSB = ev.parameter2; break; case 0x65: // midi RP rpLSB = ev.parameter2; break; case 0x6: // midi data entry if (rpLSB == 0 && rpMSB == 0) { // insert new event if right parameter slots are selected long cTicks = ev.absoluteTicks; trk.midiEvents.Insert(currentEvent, new MessageMidiEvent( cTicks, (byte)channelNumber[currentTrack], NormalType.Controller, 20, ev.parameter2)); // extend the current event because we added one event and don't want to check this one again currentEvent++; } break; default: break; } } } }
private MidiTrack GetPixelTrack(Bitmap bitmap, string name, bool grayscale = true, bool invert = true) { MidiTrack result = null; if (bitmap is Bitmap) { result = new MidiTrack(); int delta = (int)(noteLen * NoteType); var gray = bitmap.Height > 128 ? Resize(bitmap, 0, 128): bitmap; gray = grayscale ? GrayScale(gray) : gray; if (InvertSource && invert) { gray = Invert(gray); } Color c; Color co = gray.GetPixel(0, 0); int count = 0; int[,] om = new int[gray.Width, gray.Height]; Dictionary <int[, ], byte> nm = new Dictionary <int[, ], byte>(); count = 0; bool[] blank = new bool[gray.Width]; for (int i = 0; i < blank.Length; i++) { blank[i] = true; } int marginL = 0; int marginR = 0; for (int x = 0; x < gray.Width; x++) { //for (int y = 0; y < gray.Height; y++) for (int y = gray.Height - 1; y >= 0; y--) { c = gray.GetPixel(x, y); byte velocity = 0x40; var alpha = CalcAlpha(c, co, out velocity); if (alpha > 0) { om[x, y] = count * delta; blank[x] = false; if (marginL == 0) { marginL = x; } count = 0; break; } } if (blank[x]) { count++; } } marginR = count; //int offset = (int)(gray.Height / 2.0 - NoteCenter); int offset = (int)(NoteCenter - gray.Height / 2.0); if (offset < 0) { offset = 0; } var track = new MidiTrack(); track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.Meta, 0x03, 0, Encoding.Default.GetBytes(name)))); track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.NoteOff, (byte)NoteCenter, 0, Encoding.Default.GetBytes("")))); double[] alphav = new double[128]; for (int x = 0; x < gray.Width; x++) { if (blank[x]) { continue; } bool IsDelta = false; for (int y = gray.Height - 1; y >= 0; y--) { c = gray.GetPixel(x, y); byte velocity = 0x40; alphav[y] = CalcAlpha(c, co, out velocity); if (alphav[y] > 0) { var noteOn = new MidiEvent(MidiEvent.NoteOn, (byte)(gray.Height - y - 1 + offset), velocity, Encoding.Default.GetBytes("")); if (IsDelta) { track.AddMessage(new MidiMessage(0, noteOn)); } else { track.AddMessage(new MidiMessage(om[x, y], noteOn)); IsDelta = true; } } } IsDelta = false; for (int y = 0; y < gray.Height; y++) { if (alphav[y] > 0) { var noteOff = new MidiEvent(MidiEvent.NoteOn, (byte)(gray.Height - y - 1 + offset), 0x00, Encoding.Default.GetBytes("")); if (IsDelta) { track.AddMessage(new MidiMessage(0, noteOff)); } else { track.AddMessage(new MidiMessage(delta, noteOff)); IsDelta = true; } } } } track.AddMessage(new MidiMessage(marginR * delta, new MidiEvent(MidiEvent.NoteOff, (byte)NoteCenter, 0, Encoding.Default.GetBytes("")))); track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.Meta, 0x2F, 0, Encoding.Default.GetBytes("")))); result = track; } return(result); }
public override void Integrate(MidiTrack track) { //Do Nothing }
/// <summary> /// /// ----------------------- /// Type Event /// ----------------------- /// 0x00 Sequence number /// 0x01 Text event /// 0x02 Copyright notice /// 0x03 Sequence or track name /// 0x04 Instrument name /// 0x05 Lyric text /// 0x06 Marker text /// 0x07 Cue point /// 0x20 MIDI channel prefix assignment /// 0x2F End of track /// 0x51 Tempo setting /// 0x54 SMPTE offset /// 0x58 Time signature /// 0x59 Key signature /// 0x7F Sequencer specific event /// ----------------------- /// /// /// </summary> /// <param name="bitmap"></param> /// <param name="singleTrack"></param> /// <returns></returns> public MidiMusic GetPixelMidi(Bitmap bitmap, bool singleTrack = true) { MidiMusic result = null; if (bitmap is Bitmap) { int delta = (int)(noteLen * NoteType); result = new MidiMusic(); result.DeltaTimeSpec = noteLen; result.Format = 1; var track_sys = GetMetaTrack(FileName); result.AddTrack(track_sys); if (singleTrack) { var track = GetPixelTrack(bitmap, FileName); result.AddTrack(track); } else { var gray = GrayScale(bitmap.Height > 128 ? Resize(bitmap, 0, 128): bitmap); if (InvertSource) { gray = Invert(gray); } Color c; Color co = gray.GetPixel(0, 0); int count = 0; for (int y = 0; y < gray.Height; y++) { count = 0; var track = new MidiTrack(); track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.Meta, 0x00, 0, Encoding.Default.GetBytes($"{y}")))); track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.Meta, 0x03, 0, Encoding.Default.GetBytes($"{FileName}_{y}")))); for (int x = 0; x < gray.Width; x++) { c = gray.GetPixel(x, y); byte velocity = 0x40; var alpha = CalcAlpha(c, co, out velocity); if (alpha > 0) { var noteOn = new MidiEvent(MidiEvent.NoteOn, 0x40, velocity, Encoding.Default.GetBytes("")); var noteOff = new MidiEvent(MidiEvent.NoteOn, 0x40, 0x00, Encoding.Default.GetBytes("")); track.AddMessage(new MidiMessage(count * delta, noteOn)); track.AddMessage(new MidiMessage(delta, noteOff)); count = 0; } else { count++; } } track.AddMessage(new MidiMessage(0, new MidiEvent(MidiEvent.Meta, 0x2F, 0, Encoding.Default.GetBytes("")))); result.AddTrack(track); } } } Music = result; return(result); }
/// <summary>Initialize the ProgramChange MIDI event message.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The delta-time since the previous message.</param> /// <param name="channel">The channel to which to write the message (0 through 15).</param> /// <param name="number">The number of the program to which to change.</param> public ProgramChangeVoiceMidiEvent(MidiTrack owner, long deltaTime, byte channel, byte number) : base(owner, deltaTime, CategoryId, channel) { Validate.SetIfInRange("number", ref m_number, number, 0, 127); }
/// <summary>Intializes the tempo meta event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="value">The tempo for the event.</param> public TempoMetaMidiEvent(MidiTrack owner, long deltaTime, int value) : base(owner, deltaTime, MetaId) { Value = value; }
/// <summary>Intializes the voice MIDI event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="category">The category identifier (0x0 through 0xF) for this voice event.</param> /// <param name="channel">The channel (0x0 through 0xF) for this voice event.</param> internal VoiceMidiEvent(MidiTrack owner, long deltaTime, byte category, byte channel) : base(owner, deltaTime) { Validate.SetIfInRange("category", ref m_category, category, 0x0, 0xF); Channel = channel; }
/// <summary>Initialize the event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> internal MidiEvent(MidiTrack owner, long deltaTime) { // Store the data Owner = owner; DeltaTime = deltaTime; }
/// <summary>Intializes the channel prefix event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="prefix">The prefix for the event.</param> public ChannelPrefixMetaMidiEvent(MidiTrack owner, long deltaTime, byte prefix) : base(owner, deltaTime, MetaId) { Prefix = prefix; }
public CutSpecification(MidiTrack track, int fromTick, int toTick) { this.midiTrack = track; this.fromTick = fromTick; this.toTick = toTick; }
public override void ExecuteEvent(MidiTrack track) => track.ExecuteRunningEvent(this);
/// <summary>Initialize the sequence/track name meta event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="text">The text associated with the event.</param> public SequenceTrackNameTextMetaMidiEvent(MidiTrack owner, long deltaTime, string text) : base(owner, deltaTime, MetaId, text) { }
public void ParseSequence(MidiSequence s, double sample_rate = 44100.0) { //First MIDI track should always contain the relevant tempo data. We need to process this data to build a tempo map between ticks, //Translate each tick to a frame position, and push an event to a stack located at the given frame of our lookup dictionary. if (s.Tracks.Count == 0) { return; } eventsAtPosition.Clear(); ActionSet.Clear(); //Initial tempo is == 120bpm int beatLen = 480000; //beat length, in microseconds. Set by tempo events. int divider = s.TicksPerBeatOrFrame > 0? s.TicksPerBeatOrFrame : 48; //Beat divider. Use this with beatLen to calculate a tick length. //Tick length in sample frames. Calculated based on sample_rate * beatLen/ticksPerBeat/1000000. //Useful to determine how many frames we can get away with skipping without worrying about an event miss. double tickLen = 441; TempoMap tempoMap = new TempoMap(); //List of frame positions for every tick. Use to stuff events into position by looking for closest index int frameOffset = 0; //Update this every new tempo event found to account for shifts in deltas. The max value is ~12h at 48000hz. //Build the tempo map. foreach (MidiSharp.Events.MidiEvent m_event in s.Tracks[0]) { if (!(m_event is TempoMetaMidiEvent)) { continue; } var ev = (TempoMetaMidiEvent)m_event; //Calculate the frames for each tick leading up to this next tempo event. Events on tick 0 will skip this and immediately update tempo. //Once the tempo map is built, each tick index will return a corresponding frame until the end of track 0. Any tick indices beyond //the list size should be calculated based on the last known tempo. //TODO: When going through all track events and their deltas, should we extend the list further? Or will precalculating the // event map, while slower initially, make keeping the tempo map completely unnecessary? for (int i = 0; i < ev.DeltaTime; i++) { //Round to the nearest frame. tempoMap.Add((int)Math.Round(frameOffset + i * tickLen)); } //Update the frame offset to the frame where this event should exist. frameOffset = frameOffset + (int)Math.Round(ev.DeltaTime * tickLen); //Now update the actual tempo for the next operation. beatLen = ev.Value; tickLen = (sample_rate * beatLen) / (double)divider / 1000000.0; //Tick length in frames } // Now that all events on track 0 are processed, update the tempo map with the last known tempo value so we can calculate frames // for the rest of the events on the other tracks should their delta offset exceed the last tempo event. tempoMap.tickLen = tickLen; tempoMap.frameOffset = frameOffset; //Now, iterate through all tracks and events and push them to the event map. // foreach (MidiTrack track in s.Tracks) //Assume enumerator moves in order... for (int t = 1; t < s.Tracks.Count; t++) //Assume enumerator moves in order... { MidiTrack track = s.Tracks[t]; int offset = 0; for (int i = 0; i < track.Events.Count; i++) { var ev = track.Events[i]; var frame = tempoMap.CalcFrame((int)(ev.DeltaTime + offset)); List <MidiSharp.Events.MidiEvent> list; if (eventsAtPosition.ContainsKey(frame)) //Make a new list if this key needs it. { list = eventsAtPosition[frame]; } else { list = new List <MidiSharp.Events.MidiEvent>(); eventsAtPosition.Add(frame, list); } //Add the event to the list at this frame position. TODO: organize by MIDI channel? list.Add(ev); offset += (int)ev.DeltaTime; //Move up the delta timer. } } }
/// <summary>Intializes the meta MIDI event.</summary> /// <param name="owner">The track that owns this event.</param> /// <param name="deltaTime">The amount of time before this event.</param> /// <param name="metaEventID">The ID of the meta event.</param> /// <param name="text">The text associated with the event.</param> internal BaseTextMetaMidiEvent(MidiTrack owner, long deltaTime, byte metaEventID, string text) : base(owner, deltaTime, metaEventID) { Text = text; }
public TrackNotification(MidiTrack track, bool enabled) { Track = track; Enabled = enabled; }
//http://www.midi.org/techspecs/midimessages.php /// <summary> /// Loads the current MidiTrack from the Midi file. /// </summary> /// <returns>The loaded MidiTrack</returns> private MidiTrack LoadTrackChunk() { MidiTrack track = new MidiTrack(this); int length = ReadInt32(); int command = 0; int midiChannel = 0; while (length > 0) { int numBytes = 0; uint vtime = ReadVariableLengthValue(out numBytes); length -= numBytes; byte eventType = _reader.ReadByte(); length--; if (eventType >= 0xF0) //outside of normal event type range { switch (eventType) { case 0xFF: //Meta Event { byte type = _reader.ReadByte(); length--; uint eventLength = ReadVariableLengthValue(out numBytes); length -= numBytes; byte[] bytes = _reader.ReadBytes((int)eventLength); length -= (int)eventLength; track.AddEvent(new MidiMetaEvent(vtime, type, bytes, this, track)); } break; case 0xF0: case 0xF7: //SysEx Event { uint sysexLength = ReadVariableLengthValue(out numBytes); length -= numBytes; byte[] bytes = _reader.ReadBytes((int)sysexLength); length -= (int)sysexLength; Debug.Log("Sysex message of length '" + sysexLength + "'."); } break; default: Debug.Log("Unknown event type '" + eventType + "'."); break; } } else { if ((eventType & 0xF0) >= 0x80) { //Channel specific midi command command = (eventType & 0xF0); midiChannel = (eventType & 0x0F); } else { //Control Change command _reader.BaseStream.Position--; length++; } byte[] args; if (command != 0xC0 && command != 0xD0) //Program Change and Channel Key Pressure only have 1 argument instead of the default 2 { args = _reader.ReadBytes(2); length -= 2; } else { args = _reader.ReadBytes(1); length -= 1; } track.AddEvent(new MidiCommandEvent(vtime, command, midiChannel, args, this, track)); } } return track; }
/// <summary> /// Creates a MidiNote sequence from the given MidiTrack /// </summary> /// <param name="track">Midi Track to read</param> public NoteSequence(MidiTrack track) { this.Track = track; this.Reload(); }
/// <summary> /// Creates a new note at a specific start and end time /// </summary> /// <param name="parent">MidiFile this is part of</param> /// <param name="start">Absolute start time in ticks</param> /// <param name="end">Absolute end time in ticks</param> public MidiNote(MidiTrack parent, int start, int end) { this.Parent = parent; this.Start = start; this.End = end; }