public void GetStandardMetaEventStatusBytes() { var statusBytes = MetaEvent.GetStandardMetaEventStatusBytes(); CollectionAssert.AreEqual( new[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x20, 0x21, 0x2F, 0x51, 0x54, 0x58, 0x59, 0x7F }, statusBytes, "Status bytes are invalid."); }
/// <summary>Trims a MIDI file to a specified length.</summary> /// <param name="sequence">The sequence to be copied and trimmed.</param> /// <param name="totalTime">The requested time length of the new MIDI sequence.</param> /// <returns>A MIDI sequence with only those events that fell before the requested time limit.</returns> public static Sequence Trim(this Sequence sequence, long totalTime) { // Create a new sequence to mimic the old var newSequence = new Sequence(sequence.DivisionType, sequence.Resolution, 0, sequence.MidiFileType); // Copy each track up to the specified time limit foreach (Track track in sequence.Tracks) { // Create a new track in the new sequence to match the old track in the old sequence var newTrack = newSequence.CreateTrack(); // Copy over all events that fell before the specified time for (int i = 0; i < track.Events.Count && track.Events[i].Tick < totalTime; i++) { newTrack.Events.Add(track.Events[i].DeepClone()); // add at the end //newTrack.Add(track.Events[i].DeepClone()); // insert at correct timing } // If the new track lacks an end of track, add one if (!newTrack.HasEndOfTrack) { newTrack.Add(MetaEvent.CreateMetaEvent("EndOfTrack", "", newTrack.Ticks(), 0)); } } // Return the new sequence return(newSequence); }
public void TestAddNamedTrack() { var manualMidi = new MidiEventCollection(1, 200); var noteEvent = new NoteOnEvent(0, 1, 1, 1, 1); const string trackName = "name"; var trackNameEvent = new TextEvent(trackName, MetaEventType.SequenceTrackName, 0); var endTrackEvent = new MetaEvent(MetaEventType.EndTrack, 0, noteEvent.OffEvent.AbsoluteTime); var track = manualMidi.AddTrack(); track.Add(trackNameEvent); track.Add(noteEvent); track.Add(noteEvent.OffEvent); track.Add(endTrackEvent); var extensionMidi = new MidiEventCollection(1, 200); var events = new MidiEvent[] { noteEvent, noteEvent.OffEvent }; extensionMidi.AddNamedTrack(trackName, events); MidiAssert.Equal(manualMidi, extensionMidi); // Assert events (not name / end) are the same objects var manualTrack = manualMidi[0]; var extensionTrack = extensionMidi[0]; for (var e = 1; e < manualTrack.Count - 1; e++) { Assert.That(extensionTrack[e], Is.SameAs(manualTrack[e])); } }
private void ImplementEvent(ClassEmitter emitter, MetaEvent @event, ProxyGenerationOptions options, INamingScope namingScope) { @event.BuildEventEmitter(emitter); ImplementMethod(@event.Adder, emitter, options, @event.Emitter.CreateAddMethod, namingScope); ImplementMethod(@event.Remover, emitter, options, @event.Emitter.CreateRemoveMethod, namingScope); }
private void AddEvent(EventInfo @event, IProxyGenerationHook hook) { var addMethod = @event.GetAddMethod(true); var removeMethod = @event.GetRemoveMethod(true); MetaMethod adder = null; MetaMethod remover = null; if (addMethod != null) { adder = AddMethod(addMethod, hook, false); } if (removeMethod != null) { remover = AddMethod(removeMethod, hook, false); } if (adder == null && remover == null) { return; } events[@event] = new MetaEvent(@event.Name, @event.DeclaringType, @event.EventHandlerType, adder, remover, EventAttributes.None); }
public IMessage getMessage(MidiEvent midiEvent) { switch (midiEvent.CommandCode) { case MidiCommandCode.NoteOn: NoteEvent noteEvent = (NoteEvent)midiEvent; Note note = new Note(noteEvent.channel, noteEvent.NoteNumber, noteEvent.Velocity); NoteOnMessage message = new NoteOnMessage(note); return(message); case MidiCommandCode.NoteOff: NoteEvent noteEvent = (NoteEvent)midiEvent; Note note = new Note(noteEvent.channel, noteEvent.NoteNumber, noteEvent.Velocity); NoteOffMessage message = new NoteOffMessage(note); trackMessages.add(message); return(message); case MidiCommandCode.TimingClock: NoteOffMessage message = new NoteOffMessage(midiEvent.AbsoluteTime); trackMessages.add(message); return(message); case MidiCommandCode.MetaEvent: MetaEvent metaEvent = (MetaEvent)midiEvent; return(getMessage(metaEvent)); default: return(null); // TODO implement remaining IMessage types } }
public void TestAddNamedTrackCopyWithOwnEnd() { var manualMidi = new MidiEventCollection(1, 200); var noteEvent = new NoteOnEvent(0, 1, 1, 1, 1); const string trackName = "name"; var trackNameEvent = new TextEvent(trackName, MetaEventType.SequenceTrackName, 0); var endTrackEvent = new MetaEvent(MetaEventType.EndTrack, 0, 90); var track = manualMidi.AddTrack(); track.Add(trackNameEvent); track.Add(noteEvent); track.Add(noteEvent.OffEvent); track.Add(endTrackEvent); var extensionMidi = new MidiEventCollection(1, 200); var events = new MidiEvent[] { noteEvent, noteEvent.OffEvent, endTrackEvent }; extensionMidi.AddNamedTrackCopy(trackName, events); MidiAssert.Equal(manualMidi, extensionMidi); // Assert they aren't the same objects var manualTrack = manualMidi[0]; var extensionTrack = extensionMidi[0]; for (var e = 0; e < manualTrack.Count; e++) { Assert.That(extensionTrack[e], Is.Not.SameAs(manualTrack[e])); } // Verify the NoteOnEvent.OffEvent link Assert.AreEqual(extensionMidi[0][2], ((NoteOnEvent)extensionMidi[0][1]).OffEvent); }
float ParseTempo(MetaEvent ev) { var microSec = (ev.data[0] << 16) + (ev.data[1] << 8) + ev.data[2]; var bpm = (60 * 1000 * 1000) / (float)microSec; return(bpm); }
public void SaveMidiSequence(string filePath) { long ticks = midiTrack.Ticks(); midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.EndOfTrack, "", ticks, midiTickResolution)); midiSequence.DumpMidi("output.mid.txt"); new MidiFileWriter().Write(midiSequence, midiSequence.MidiFileType, new FileInfo(filePath)); }
/// <summary> /// Default Constructor /// </summary> public MetaEventViewModel(MetaEvent metaEventData, EventsUserData userData) { this.metaEventData = metaEventData; this.UserData = userData; this.IsVisible = true; var currentTime = DateTime.UtcNow.TimeOfDay; this.InitializeStagesAndTimers(currentTime); this.prevUpdateTimeUtc = currentTime; this.UserData.HiddenMetaEvents.CollectionChanged += (o, e) => this.RefreshVisibility(); }
private void AnalyzeTimeSignature(MetaEvent meta) { TimeSignatureEvent timesig = (TimeSignatureEvent)meta; // Numerator: counts the number of beats in a measure. // For example a numerator of 4 means that each bar contains four beats. MPTK_TimeSigNumerator = timesig.Numerator; // Denominator: number of quarter notes in a beat.0=ronde, 1=blanche, 2=quarter, 3=eighth, etc. MPTK_TimeSigDenominator = timesig.Denominator; MPTK_NumberBeatsMeasure = timesig.Numerator; MPTK_NumberQuarterBeat = System.Convert.ToInt32(System.Math.Pow(2, timesig.Denominator)); MPTK_TicksInMetronomeClick = timesig.TicksInMetronomeClick; MPTK_No32ndNotesInQuarterNote = timesig.No32ndNotesInQuarterNote; }
public void MetaEventHandler(MetaEvent metaEvent) { if (metaEvent.Type == MetaEventType.Tempo) { ticksPerSample = (double)file.TicksPerBeat / (metaEvent.Tempo * samplesPerMicrosecond); // ticks per sample = ticks per beat / (ms per beat * samples per ms); // = ticks per ms / samples per beat; // = ticks per sample; ticksPerSecond = (double)file.TicksPerBeat / metaEvent.Tempo * microsecondPerSecond; // ticks per second = ticks per beat / ms per beat * ms per second; // = ticks per ms * ms per second; // = ticks per second; } }
private MetaEventStatus DbGetMetaEventStatus(string id) { MetaEvent meta = MetaEventDefinitions.MetaEvents.Where(m => m.Id == id).FirstOrDefault(); if (meta == null) { throw new KeyNotFoundException(string.Format("Meta event [{0}] does not exist", id)); } MetaEventStatus status = new MetaEventStatus() { Id = meta.Id, Name = meta.Name, Countdown = 0, StageId = -1, StageTypeEnum = MetaEventStage.StageType.Reset, StageName = null, Timestamp = DbGetTimestamp() }; IDbCommand cmd = m_DbConn.CreateCommand(); try { cmd.CommandText = "SELECT * FROM eventtimerapi_events WHERE id = @id"; cmd.AddParameter("@id", id); using (IDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { status.Id = reader["id"].ToString(); status.Name = reader["name"].ToString(); status.MinCountdown = uint.Parse(reader["mincountdown"].ToString()); status.MaxCountdown = uint.Parse(reader["maxcountdown"].ToString()); status.StageId = int.Parse(reader["stageid"].ToString()); status.StageName = reader["stagename"].ToString(); status.StageType = reader["stagetype"].ToString(); status.Timestamp = long.Parse(reader["timestamp"].ToString()); } } } catch (Exception e) { LOGGER.Error(string.Format("Exception thrown when attempting to get event data [{0}]", id), e); } return(status); }
public ActionResult Create(MetaEvent model) { model.Event = new Event(); if (ModelState.IsValid && model.Date >= DateTime.Now) { for (int i = 0; i < model.Recurrence; i++) { Event newEvent = new Event(); newEvent.IdTutorNavigation = db.SscisUser.Find(model.TutorID); newEvent.IdTutor = model.TutorID; newEvent.IsCancelled = false; if (newEvent.IdTutorNavigation.IdRoleNavigation.Role.Equals(SSCISResources.Resources.ADMIN)) { newEvent.IsAccepted = true; } else { newEvent.IsAccepted = false; } newEvent.IsExtraLesson = false; newEvent.TimeFrom = new DateTime(model.Date.Year, model.Date.Month, model.Date.Day, model.TimeFrom.Hour, model.TimeFrom.Minute, 0); if (!BoolParser.Parse(db.SscisParam.Single(p => p.ParamKey.Equals(SSCISParameters.STANDARTEVENTLENGTH)).ParamValue)) { int hour = Convert.ToInt32(db.SscisParam.Where(p => p.ParamKey.Equals(SSCISParameters.STANDARTEVENTLENGTH)).Single().ParamValue); newEvent.TimeTo = new DateTime(model.Date.Year, model.Date.Month, model.Date.Day, model.TimeFrom.Hour + hour, model.TimeFrom.Minute, 0); } else { newEvent.TimeTo = model.Event.TimeFrom.AddHours(2); } newEvent.IdSubjectNavigation = db.EnumSubject.Find(model.SubjectID); newEvent.IdSubject = (int)model.SubjectID; newEvent.IdApplicantNavigation = null; newEvent.IdApplicant = null; int increase = i * 7; newEvent.TimeFrom = newEvent.TimeFrom.AddDays(increase); newEvent.TimeTo = newEvent.TimeTo.AddDays(increase); db.Event.Add(newEvent); db.SaveChanges(); } return(RedirectToAction("TutorEvents", new { created = 1 })); } return(RedirectToAction("Create")); }
public static MDEvent Parsing(byte[] buffer, ref int offset, MDEvent bef_event) { int oldoffset = offset; int delta = StaticFunc.ReadDeltaTime(buffer, ref offset); if (buffer[offset] == 0xFF) { offset++; return(MetaEvent.MakeEvent(delta, buffer, ref offset, oldoffset)); } if (buffer[offset] < 0xF0) { return(MidiEvent.makeEvent(buffer[offset++], delta, buffer, ref offset, oldoffset, bef_event.EventType)); } return(SysEvent.MakeEvent(buffer[offset++], delta, buffer, ref offset, oldoffset)); }
private void OpenSmfFile() { if (!this.mode_smf) { return; } if (this.smfFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { if (this.playing) { this.connector.Stop(); this.connector.Reset(); } SmfConnector smf = (SmfConnector)this.connector; smf.Load(this.smfFileDialog.FileName); smf.Sequencer.SequenceEnd += (sender, e) => this.Stop(false); smf.Sequencer.OnTrackEvent += (sender, e) => { foreach (MetaEvent meta in e.Events.Where(m => m is MetaEvent && ((MetaEvent)m).MetaType == MetaType.Lyrics)) { this.Invoke(new Action(() => this.label_title.Text = this.sjis.GetString(meta.Data).Trim())); } }; this.label_resolution.Text = smf.Sequence.Resolution.ToString(); this.label_tick_right.Text = smf.Sequence.MaxTick.ToString(); this.hScrollBar.SmallChange = smf.Sequence.Resolution; this.hScrollBar.LargeChange = smf.Sequence.Resolution * 4; MetaEvent title = (MetaEvent)smf.Sequence.Tracks.SelectMany(t => t.Events).Where(e => e is MetaEvent && ((MetaEvent)e).MetaType == MetaType.TrackName).FirstOrDefault(); if (title != null) { this.label_title.Text = this.sjis.GetString(title.Data).Trim(); } this.hScrollBar.Maximum = (int)smf.Sequence.MaxTick; if (this.playing) { this.connector.Play(); } } }
private void AnalyzeTimeSignature(MetaEvent meta, TrackMidiEvent trackEvent = null) { TimeSignatureEvent timesig = (TimeSignatureEvent)meta; // Numerator: counts the number of beats in a measure. // For example a numerator of 4 means that each bar contains four beats. MPTK_TimeSigNumerator = timesig.Numerator; // Denominator: number of quarter notes in a beat.0=ronde, 1=blanche, 2=quarter, 3=eighth, etc. MPTK_TimeSigDenominator = timesig.Denominator; MPTK_NumberBeatsMeasure = timesig.Numerator; MPTK_NumberQuarterBeat = System.Convert.ToInt32(Mathf.Pow(2f, timesig.Denominator)); MPTK_TicksInMetronomeClick = timesig.TicksInMetronomeClick; MPTK_No32ndNotesInQuarterNote = timesig.No32ndNotesInQuarterNote; if (LogEvents && trackEvent != null) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0,-15} Numerator:{1} Denominator:{2}", meta.MetaEventType, timesig.Numerator, timesig.Denominator)); } }
public void TestClone() { var manualMidi = new MidiEventCollection(1, 200); var noteEvent1 = new NoteOnEvent(0, 1, 1, 1, 1); const string trackName1 = "name"; var trackNameEvent1 = new TextEvent(trackName1, MetaEventType.SequenceTrackName, 0); var endTrackEvent1 = new MetaEvent(MetaEventType.EndTrack, 0, noteEvent1.OffEvent.AbsoluteTime); var track1 = manualMidi.AddTrack(); track1.Add(trackNameEvent1); track1.Add(noteEvent1); track1.Add(noteEvent1.OffEvent); track1.Add(endTrackEvent1); var noteEvent2 = new NoteOnEvent(0, 1, 1, 1, 1); const string trackName2 = "name"; var trackNameEvent2 = new TextEvent(trackName2, MetaEventType.SequenceTrackName, 0); var endTrackEvent2 = new MetaEvent(MetaEventType.EndTrack, 0, noteEvent2.OffEvent.AbsoluteTime); var track2 = manualMidi.AddTrack(); track2.Add(trackNameEvent2); track2.Add(noteEvent2); track2.Add(noteEvent2.OffEvent); track2.Add(endTrackEvent2); var clone = manualMidi.Clone(); MidiAssert.Equal(manualMidi, clone); for (var t = 0; t < manualMidi.Tracks; t++) { var manualTrack = manualMidi[t]; var extensionTrack = clone[t]; for (var e = 1; e < manualTrack.Count - 1; e++) { Assert.That(extensionTrack[e], Is.Not.SameAs(manualTrack[e])); } } }
public IMessage getMessage(MetaEvent metaEvent) { switch (metaEvent.MetaEventType) { case SetTempo: TempoEvent tempoEvent = (TempoEvent)metaEvent; TempoMetaMessage message = TempoMetaMessage(tempoEvent.Tempo, tempoEvent.AbsoluteTime); return(message); case TimeSignature: TimeSignatureEvent timeSignatureEvent = (TimeSignatureEvent)metaEvent; TimeSignatureMetaMessage message = TimeSignatureMetaMessage(timeSignatureEvent.Numerator, timeSignatureEvent.Numerator, timeSignatureEvent.AbsoluteTime); return(message); case KeySignature: KeySignatureEvent keySignatureEvent = (KeySignatureEvent)metaEvent; TimeSignatureMetaMessage message = TimeSignatureMetaMessage((sbyte)keySignatureEvent.SharpsFlats, (byte)keySignatureEvent.MajorMinor, timeSignatureEvent.AbsoluteTime); return(message); default: return(null); // TODO remaining MetaMessage types } }
private void CreateTrackEvents(List<MidiEvent> outputList, IList<MidiEvent> inputList, long startAbsoluteTime, long endAbsoluteTime, bool allowMetaEvents) { foreach (MidiEvent midiEvent in inputList) { if ((midiEvent.AbsoluteTime >= startAbsoluteTime) && (midiEvent.AbsoluteTime < endAbsoluteTime)) { bool exclude = false; MetaEvent metaEvent = midiEvent as MetaEvent; if (metaEvent != null) { if (allowMetaEvents) { if (metaEvent.MetaEventType == MetaEventType.EndTrack) { exclude = true; } else if (metaEvent.MetaEventType == MetaEventType.SequenceTrackName) { exclude = true; } } else { exclude = true; } } if (!exclude) { AddMidiEvent(midiEvent, outputList, endAbsoluteTime); } } } outputList.Sort(new MidiEventComparer()); outputList.Add(new MetaEvent(MetaEventType.EndTrack, 0, outputList[outputList.Count - 1].AbsoluteTime)); }
void InitMidiSequence() { // Generate midi file midiSequence = new Sequence(Sequence.PPQ, midiTickResolution, 0, (int)MidiHelper.MidiFormat.SingleTrack); midiTrack = midiSequence.CreateTrack(); midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.SequenceOrTrackName, "Audio2Midi", 0, midiTickResolution)); midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.CopyrightNotice, "*****@*****.**", 0, midiTickResolution)); midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.Tempo, "" + midiBPM, 0, midiTickResolution)); midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.TimeSignature, "4/4", 0, midiTickResolution)); // Convert from ticks to duration // Midi timings are explained here // http://sites.uci.edu/camp2014/2014/05/19/timing-in-midi-files/ // http://stackoverflow.com/questions/2038313/midi-ticks-to-actual-playback-seconds-midi-music // The formula is 60000 / (BPM * PPQ) (milliseconds). // Where BPM is the tempo of the track (Beats Per Minute). // (i.e. a 120 BPM track would have a MIDI time of: // (60000 / (120 * 192)) or 2.604 ms for 1 tick. double timeBufferMs = ((double)bufferSize / sampleRate) * 1000; double timeTickMs = (60000 / (double)(midiBPM * midiTickResolution)); tickMultiplier = timeBufferMs / timeTickMs; }
/// <summary> /// Compares two MidiEvents /// Sorts by time, with EndTrack always sorted to the end /// </summary> public int Compare(MidiEvent x, MidiEvent y) { long xTime = x.AbsoluteTime; long yTime = y.AbsoluteTime; if (xTime == yTime) { // sort meta events before note events, except end track MetaEvent xMeta = x as MetaEvent; MetaEvent yMeta = y as MetaEvent; if (xMeta != null) { if (xMeta.MetaEventType == MetaEventType.EndTrack) { xTime = Int64.MaxValue; } else { xTime = Int64.MinValue; } } if (yMeta != null) { if (yMeta.MetaEventType == MetaEventType.EndTrack) { yTime = Int64.MaxValue; } else { yTime = Int64.MinValue; } } } return(xTime.CompareTo(yTime)); }
public void AddEvent(MetaEvent @event) { events.Add(@event); }
/// <summary> /// Opens a MIDI file for reading /// </summary> /// <param name="filename">Name of MIDI file</param> /// <param name="strictChecking">If true will error on non-paired note events</param> public MidiFile(string filename, bool strictChecking) { this.strictChecking = strictChecking; var br = new BinaryReader(File.OpenRead(filename)); using (br) { string chunkHeader = Encoding.UTF8.GetString(br.ReadBytes(4)); if (chunkHeader != "MThd") { throw new FormatException("Not a MIDI file - header chunk missing"); } uint chunkSize = SwapUInt32(br.ReadUInt32()); if (chunkSize != 6) { throw new FormatException("Unexpected header chunk length"); } // 0 = single track, 1 = multi-track synchronous, 2 = multi-track asynchronous fileFormat = SwapUInt16(br.ReadUInt16()); int tracks = SwapUInt16(br.ReadUInt16()); deltaTicksPerQuarterNote = SwapUInt16(br.ReadUInt16()); events = new MidiEventCollection((fileFormat == 0)?0:1, deltaTicksPerQuarterNote); for (int n = 0; n < tracks; n++) { events.AddTrack(); } long absoluteTime = 0; for (int track = 0; track < tracks; track++) { if (fileFormat == 1) { absoluteTime = 0; } chunkHeader = Encoding.UTF8.GetString(br.ReadBytes(4)); if (chunkHeader != "MTrk") { throw new FormatException("Invalid chunk header"); } chunkSize = SwapUInt32(br.ReadUInt32()); long startPos = br.BaseStream.Position; MidiEvent me = null; var outstandingNoteOns = new List <NoteOnEvent>(); while (br.BaseStream.Position < startPos + chunkSize) { me = MidiEvent.ReadNextEvent(br, me); absoluteTime += me.DeltaTime; me.AbsoluteTime = absoluteTime; events[track].Add(me); if (me.CommandCode == MidiCommandCode.NoteOn) { NoteEvent ne = (NoteEvent)me; if (ne.Velocity > 0) { outstandingNoteOns.Add((NoteOnEvent)ne); } else { // don't remove the note offs, even though // they are annoying // events[track].Remove(me); FindNoteOn(ne, outstandingNoteOns); } } else if (me.CommandCode == MidiCommandCode.NoteOff) { FindNoteOn((NoteEvent)me, outstandingNoteOns); } else if (me.CommandCode == MidiCommandCode.MetaEvent) { MetaEvent metaEvent = (MetaEvent)me; if (metaEvent.MetaEventType == MetaEventType.EndTrack) { //break; // some dodgy MIDI files have an event after end track if (strictChecking) { if (br.BaseStream.Position < startPos + chunkSize) { throw new FormatException(String.Format("End Track event was not the last MIDI event on track {0}", track)); } } } } } if (outstandingNoteOns.Count > 0) { if (strictChecking) { throw new FormatException(String.Format("Note ons without note offs {0} (file format {1})", outstandingNoteOns.Count, fileFormat)); } } if (br.BaseStream.Position != startPos + chunkSize) { throw new FormatException(String.Format("Read too far {0}+{1}!={2}", chunkSize, startPos, br.BaseStream.Position)); } } } }
private void ImplementEvent(ClassEmitter emitter, MetaEvent @event, ProxyGenerationOptions options) { @event.BuildEventEmitter(emitter); ImplementMethod(@event.Adder, emitter, options, @event.Emitter.CreateAddMethod); ImplementMethod(@event.Remover, emitter, options, @event.Emitter.CreateRemoveMethod); }
void ParseTimeSignature(MetaEvent ev, out int numerator, out int denominator) { numerator = ev.data[0]; denominator = (int)Math.Pow(2, ev.data[1]); }
/// <summary> /// Return information about a midifile : patch change, copyright, ... /// </summary> /// <param name="pathfilename"></param> /// <param name="Info"></param> static public List <string> GeneralInfo(string pathfilename, bool withNoteOn, bool withNoteOff, bool withControlChange, bool withPatchChange, bool withAfterTouch, bool withMeta, bool withOthers) { List <string> Info = new List <string>(); try { int NumberBeatsMeasure; int NumberQuarterBeat; MidiLoad midifile = new MidiLoad(); midifile.KeepNoteOff = withNoteOff; midifile.MPTK_Load(pathfilename); if (midifile != null) { Info.Add(string.Format("Format: {0}", midifile.midifile.FileFormat)); Info.Add(string.Format("Tracks: {0}", midifile.midifile.Tracks)); Info.Add(string.Format("Events count: {0}", midifile.MidiSorted.Count())); Info.Add(string.Format("Duration: {0} ({1} seconds) {2} Ticks", midifile.MPTK_RealDuration, midifile.MPTK_RealDuration.TotalSeconds, midifile.MPTK_TickLast)); Info.Add(string.Format("Initial Tempo: {0,0:F2} BPM", midifile.MPTK_InitialTempo)); Info.Add(string.Format("Beats in a measure: {0}", midifile.MPTK_NumberBeatsMeasure)); Info.Add(string.Format("Quarters count in a beat:{0}", midifile.MPTK_NumberQuarterBeat)); Info.Add(string.Format("Ticks per Quarter Note: {0}", midifile.midifile.DeltaTicksPerQuarterNote)); Info.Add(""); //if (false) { foreach (TrackMidiEvent trackEvent in midifile.MidiSorted) { switch (trackEvent.Event.CommandCode) { case MidiCommandCode.NoteOn: if (withNoteOn) { if (((NoteOnEvent)trackEvent.Event).OffEvent != null) { NoteOnEvent noteon = (NoteOnEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("NoteOn {0,3} ({1,3}) Len:{2,3} Vel:{3,3}", noteon.NoteName, noteon.NoteNumber, noteon.NoteLength, noteon.Velocity)); } } break; case MidiCommandCode.NoteOff: if (withNoteOff) { NoteEvent noteoff = (NoteEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("NoteOff {0,3} ({1,3}) Vel:{2,3}", noteoff.NoteName, noteoff.NoteNumber, noteoff.Velocity)); } break; case MidiCommandCode.PitchWheelChange: if (withOthers) { PitchWheelChangeEvent aftertouch = (PitchWheelChangeEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("PitchWheelChange {0,3}", aftertouch.Pitch)); } break; case MidiCommandCode.KeyAfterTouch: if (withAfterTouch) { NoteEvent aftertouch = (NoteEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("KeyAfterTouch {0,3} ({1,3}) Pressure:{2,3}", aftertouch.NoteName, aftertouch.NoteNumber, aftertouch.Velocity)); } break; case MidiCommandCode.ChannelAfterTouch: if (withAfterTouch) { ChannelAfterTouchEvent aftertouch = (ChannelAfterTouchEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("ChannelAfterTouch Pressure:{0,3}", aftertouch.AfterTouchPressure)); } break; case MidiCommandCode.ControlChange: if (withControlChange) { ControlChangeEvent controlchange = (ControlChangeEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("ControlChange {0,3} ({1,3}) Value:{2,3}", controlchange.Controller, controlchange.Controller, controlchange.ControllerValue)); } break; case MidiCommandCode.PatchChange: if (withPatchChange) { PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("PatchChange {0,3:000} {1}", change.Patch, PatchChangeEvent.GetPatchName(change.Patch))); } break; case MidiCommandCode.MetaEvent: if (withMeta) { MetaEvent meta = (MetaEvent)trackEvent.Event; switch (meta.MetaEventType) { case MetaEventType.SetTempo: TempoEvent tempo = (TempoEvent)meta; Info.Add(BuildInfoTrack(trackEvent) + string.Format("SetTempo Tempo:{0} MicrosecondsPerQuarterNote:{1}", Math.Round(tempo.Tempo, 0), tempo.MicrosecondsPerQuarterNote)); //tempo.Tempo break; case MetaEventType.TimeSignature: TimeSignatureEvent timesig = (TimeSignatureEvent)meta; // Numerator: counts the number of beats in a measure. // For example a numerator of 4 means that each bar contains four beats. // Denominator: number of quarter notes in a beat.0=ronde, 1=blanche, 2=quarter, 3=eighth, etc. // Set default value NumberBeatsMeasure = timesig.Numerator; NumberQuarterBeat = System.Convert.ToInt32(Mathf.Pow(2, timesig.Denominator)); Info.Add(BuildInfoTrack(trackEvent) + string.Format("TimeSignature Beats Measure:{0} Beat Quarter:{1}", NumberBeatsMeasure, NumberQuarterBeat)); break; default: string text = meta is TextEvent ? " '" + ((TextEvent)meta).Text + "'" : ""; Info.Add(BuildInfoTrack(trackEvent) + meta.MetaEventType.ToString() + text); break; } } break; default: // Other midi event if (withOthers) { Info.Add(BuildInfoTrack(trackEvent) + string.Format(" {0} ({1})", trackEvent.Event.CommandCode, (int)trackEvent.Event.CommandCode)); } break; } } } //else DebugMidiSorted(midifile.MidiSorted); } else { Info.Add("Error reading midi file"); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(Info); }
/// <summary> /// Return information about a midifile : patch change, copyright, ... /// </summary> /// <param name="pathfilename"></param> /// <param name="Info"></param> static public void GeneralInfo(string pathfilename, BuilderInfo Info) { try { int NumberBeatsMeasure; int NumberQuarterBeat; Debug.Log("Open midifile :" + pathfilename); MidiLoad midifile = new MidiLoad(); midifile.Load(pathfilename); if (midifile != null) { Info.Add(string.Format("Format: {0}", midifile.midifile.FileFormat)); Info.Add(string.Format("Tracks: {0}", midifile.midifile.Tracks)); Info.Add(string.Format("Ticks Quarter Note: {0}", midifile.midifile.DeltaTicksPerQuarterNote)); //if (false) { foreach (TrackMidiEvent trackEvent in midifile.MidiSorted) { if (trackEvent.Event.CommandCode == MidiCommandCode.NoteOn) { // Not used //if (((NoteOnEvent)trackEvent.Event).OffEvent != null) //{ // //infoTrackMidi[e.Channel].Events.Add((NoteOnEvent)e); // NoteOnEvent noteon = (NoteOnEvent)trackEvent.Event; //} } else if (trackEvent.Event.CommandCode == MidiCommandCode.NoteOff) { Debug.Log("NoteOff"); } else if (trackEvent.Event.CommandCode == MidiCommandCode.ControlChange) { // Not used //ControlChangeEvent controlchange = (ControlChangeEvent)e; //Debug.Log(string.Format("CtrlChange Track:{0} Channel:{1,2:00} {2}", track, e.Channel, controlchange.ToString())); } else if (trackEvent.Event.CommandCode == MidiCommandCode.PatchChange) { PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; Info.Add(BuildInfoTrack(trackEvent) + string.Format("PatchChange {0,3:000} {1}", change.Patch, PatchChangeEvent.GetPatchName(change.Patch)), 2); } else if (trackEvent.Event.CommandCode == MidiCommandCode.MetaEvent) { MetaEvent meta = (MetaEvent)trackEvent.Event; switch (meta.MetaEventType) { case MetaEventType.SetTempo: TempoEvent tempo = (TempoEvent)meta; Info.Add(BuildInfoTrack(trackEvent) + string.Format("SetTempo Tempo:{0} MicrosecondsPerQuarterNote:{1}", Math.Round(tempo.Tempo, 0), tempo.MicrosecondsPerQuarterNote), 2); //tempo.Tempo break; case MetaEventType.TimeSignature: TimeSignatureEvent timesig = (TimeSignatureEvent)meta; // Numerator: counts the number of beats in a measure. // For example a numerator of 4 means that each bar contains four beats. // Denominator: number of quarter notes in a beat.0=ronde, 1=blanche, 2=quarter, 3=eighth, etc. // Set default value NumberBeatsMeasure = timesig.Numerator; NumberQuarterBeat = System.Convert.ToInt32(System.Math.Pow(2, timesig.Denominator)); Info.Add(BuildInfoTrack(trackEvent) + string.Format("TimeSignature Beats Measure:{0} Beat Quarter:{1}", NumberBeatsMeasure, NumberQuarterBeat), 2); break; case MetaEventType.SequenceTrackName: // Sequence / Track Name case MetaEventType.ProgramName: case MetaEventType.TrackInstrumentName: // Track instrument name case MetaEventType.TextEvent: // Text event case MetaEventType.Copyright: // Copyright Info.Add(BuildInfoTrack(trackEvent) + ((TextEvent)meta).Text, 1); break; case MetaEventType.Lyric: // lyric case MetaEventType.Marker: // marker case MetaEventType.CuePoint: // cue point case MetaEventType.DeviceName: //Info.Add(BuildInfoTrack(trackEvent) + string.Format("{0} '{1}'", meta.MetaEventType.ToString(), ((TextEvent)meta).Text)); break; } } else { // Other midi event //Debug.Log(string.Format("Track:{0} Channel:{1,2:00} CommandCode:{2,3:000} AbsoluteTime:{3,6:000000}", track, e.Channel, e.CommandCode.ToString(), e.AbsoluteTime)); } } } //else DebugMidiSorted(midifile.MidiSorted); } else { Info.Add("Error reading midi file"); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Loads the notes and tempo changes from the specified midi file. /// </summary> /// <param name="file">The file path of the midi file to upload.</param> void LoadMidi(string file) { // Display file name FileLbl.Text = Path.GetFileName(file); // Open midi file midi = new MidiFile(file, false); // Create save name for midi file saveName = Path.GetFileNameWithoutExtension(file); // Resets the list of channels Channels = new List <int>(); ChannelTable.Rows.Clear(); // The list of voices used in the midi file List <PatchChangeEvent> PatchChanges = new List <PatchChangeEvent>(); // For every track/channel foreach (IList <MidiEvent> track in midi.Events) { // For every midi event (note, metadata, etc.) foreach (MidiEvent midiEvent in track) { switch (midiEvent.CommandCode) { case MidiCommandCode.NoteOn: // Note NoteOnEvent ne = (NoteOnEvent)midiEvent; if (ne.OffEvent != null) { Notes.Add(ne); } if (!Channels.Contains(ne.Channel) && ne.Channel != 16 && ne.Channel != 10) { // New instument channel discovered so add to channel list Channels.Add(ne.Channel); } break; case MidiCommandCode.MetaEvent: // Metadata MetaEvent me = (MetaEvent)midiEvent; switch (me.MetaEventType) { case MetaEventType.SetTempo: // Tempo Change TempoChanges.Add((TempoEvent)me); break; } break; case MidiCommandCode.PatchChange: // Patch (instrument) PatchChanges.Add((PatchChangeEvent)midiEvent); break; } } } // Sort channels numerically Channels.Sort(); // Add rows to ChannelTable (TODO: refactor code) // For each channel foreach (int channel in Channels) { // Identify MIDI voice (default is acoustic grand) string instrument = "Acoustic Grand"; // For each midi voice foreach (PatchChangeEvent pe in PatchChanges) { if (pe.Channel == channel) // If instrument channel matches current channel { // Use sneaky hacks to find instument name instrument = pe.ToString().Split(':')[1].Replace(" " + pe.Channel + " ", ""); } } // Add row to ChannelTable ChannelTable.Rows.Add(channel, instrument, false, "Piano"); } // Finally, sort notes by position in the piece Notes.Sort((x, y) => x.AbsoluteTime.CompareTo(y.AbsoluteTime)); }
public List <MidiNote> ReadMidiEvents(double timeFromStartMS) { List <MidiNote> notes = null; try { EndMidiEvent = false; if (midifile != null) { if (NextPosEvent < MidiSorted.Count) { // The BPM measures how many quarter notes happen in a minute. To work out the length of each pulse we can use the following formula: // Pulse Length = 60 / (BPM * PPQN) // Calculate current pulse to play CurrentPulse += Convert.ToInt64((timeFromStartMS - LastTimeFromStartMS) / PulseLengthMs); LastTimeFromStartMS = timeFromStartMS; // From the last position played for (int currentPosEvent = NextPosEvent; currentPosEvent < MidiSorted.Count; currentPosEvent++) { TrackMidiEvent trackEvent = MidiSorted[currentPosEvent]; if (Quantization != 0) { trackEvent.AbsoluteQuantize = ((trackEvent.Event.AbsoluteTime + Quantization / 2) / Quantization) * Quantization; } else { trackEvent.AbsoluteQuantize = trackEvent.Event.AbsoluteTime; } //Debug.Log("ReadMidiEvents - timeFromStartMS:" + Convert.ToInt32(timeFromStartMS) + " LastTimeFromStartMS:" + Convert.ToInt32(LastTimeFromStartMS) + " CurrentPulse:" + CurrentPulse + " AbsoluteQuantize:" + trackEvent.AbsoluteQuantize); if (trackEvent.AbsoluteQuantize <= CurrentPulse) { NextPosEvent = currentPosEvent + 1; if (trackEvent.Event.CommandCode == MidiCommandCode.NoteOn) { if (((NoteOnEvent)trackEvent.Event).OffEvent != null) { NoteOnEvent noteon = (NoteOnEvent)trackEvent.Event; // if (noteon.OffEvent != null) { if (notes == null) { notes = new List <MidiNote>(); } //Debug.Log(string.Format("Track:{0} NoteNumber:{1,3:000} AbsoluteTime:{2,6:000000} NoteLength:{3,6:000000} OffDeltaTime:{4,6:000000} ", track, noteon.NoteNumber, noteon.AbsoluteTime, noteon.NoteLength, noteon.OffEvent.DeltaTime)); MidiNote note = new MidiNote() { AbsoluteQuantize = trackEvent.AbsoluteQuantize, Midi = noteon.NoteNumber, Channel = trackEvent.Event.Channel, Velocity = noteon.Velocity, Duration = noteon.NoteLength * PulseLengthMs, Length = noteon.NoteLength, Patch = PatchChanel[trackEvent.Event.Channel - 1], Drum = (trackEvent.Event.Channel == 10), Delay = 0, Pan = EnablePanChange ? PanChanel[trackEvent.Event.Channel - 1] : -1, }; if (VolumeChanel[note.Channel - 1] != 127) { note.Velocity = Mathf.RoundToInt(((float)note.Velocity) * ((float)VolumeChanel[trackEvent.Event.Channel - 1]) / 127f); } notes.Add(note); if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("{0,-4} {1,3:000} Lenght:{2} {3} Veloc:{4}", noteon.NoteName, noteon.NoteNumber, noteon.NoteLength, NoteLength(note), noteon.Velocity)); } } } } else if (trackEvent.Event.CommandCode == MidiCommandCode.NoteOff) { // no need, noteoff are associated with noteon } else if (trackEvent.Event.CommandCode == MidiCommandCode.ControlChange) { ControlChangeEvent controlchange = (ControlChangeEvent)trackEvent.Event; if (controlchange.Controller == MidiController.Expression) { VolumeChanel[trackEvent.Event.Channel - 1] = controlchange.ControllerValue; } else if (controlchange.Controller == MidiController.MainVolume) { VolumeChanel[trackEvent.Event.Channel - 1] = controlchange.ControllerValue; } else if (controlchange.Controller == MidiController.Pan) { PanChanel[trackEvent.Event.Channel - 1] = controlchange.ControllerValue; } // Other midi event if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Control {0} {1}", controlchange.Controller, controlchange.ControllerValue)); } } else if (trackEvent.Event.CommandCode == MidiCommandCode.PatchChange) { PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; PatchChanel[trackEvent.Event.Channel - 1] = trackEvent.Event.Channel == 10 ? 0 : change.Patch; if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Patch {0,3:000} {1}", change.Patch, PatchChangeEvent.GetPatchName(change.Patch))); } } else if (trackEvent.Event.CommandCode == MidiCommandCode.MetaEvent) { MetaEvent meta = (MetaEvent)trackEvent.Event; switch (meta.MetaEventType) { case MetaEventType.SetTempo: if (EnableChangeTempo) { TempoEvent tempo = (TempoEvent)meta; //NewQuarterPerMinuteValue = tempo.Tempo; ChangeTempo(tempo.Tempo); //if (LogEvents)Debug.Log(BuildInfoTrack(trackEvent) + string.Format("SetTempo {0} MicrosecondsPerQuarterNote:{1}", tempo.Tempo, tempo.MicrosecondsPerQuarterNote)); } break; case MetaEventType.SequenceTrackName: if (!string.IsNullOrEmpty(SequenceTrackName)) { SequenceTrackName += "\n"; } SequenceTrackName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, ((TextEvent)meta).Text); if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Sequence '{0}'", ((TextEvent)meta).Text)); } break; case MetaEventType.ProgramName: ProgramName += ((TextEvent)meta).Text + " "; if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Program '{0}'", ((TextEvent)meta).Text)); } break; case MetaEventType.TrackInstrumentName: if (!string.IsNullOrEmpty(TrackInstrumentName)) { TrackInstrumentName += "\n"; } TrackInstrumentName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, ((TextEvent)meta).Text); if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Text '{0}'", ((TextEvent)meta).Text)); } break; case MetaEventType.TextEvent: TextEvent += ((TextEvent)meta).Text + " "; if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Sequence '{0}'", ((TextEvent)meta).Text)); } break; case MetaEventType.Copyright: Copyright += ((TextEvent)meta).Text + " "; if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Copyright '{0}'", ((TextEvent)meta).Text)); } break; case MetaEventType.Lyric: // lyric case MetaEventType.Marker: // marker case MetaEventType.CuePoint: // cue point case MetaEventType.DeviceName: break; } //Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0} {1}", meta.MetaEventType, meta.ToString())); } else { // Other midi event //Debug.Log(string.Format("Track:{0} Channel:{1,2:00} CommandCode:{2,3:000} AbsoluteTime:{3,6:000000}", track, e.Channel, e.CommandCode.ToString(), e.AbsoluteTime)); } } else { // Out of time, exit for loop break; } } if (notes != null) { //if (CancelNextReadEvents) //{ // notes = null; // //Debug.Log("CancelNextReadEvents"); // CancelNextReadEvents = false; //} //else //if (notes.Count > 3 && (notes[notes.Count - 1].AbsoluteQuantize - notes[0].AbsoluteQuantize) > midifile.DeltaTicksPerQuarterNote * 8) //{ // //notes.RemoveRange(0, notes.Count - 1); // Debug.Log("--> Too much notes " + notes.Count + " timeFromStartMS:" + Convert.ToInt32(timeFromStartMS) + " Start:" + notes[0].AbsoluteQuantize + " Ecart:" + (notes[notes.Count - 1].AbsoluteQuantize - notes[0].AbsoluteQuantize) + " CurrentPulse:" + CurrentPulse); // //notes = null; //} } } else { // End of midi events EndMidiEvent = true; } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(notes); }
private List <TrackMidiEvent> GetEvents() { try { List <TrackMidiEvent> events = new List <TrackMidiEvent>(); foreach (IList <MidiEvent> track in midifile.Events) { CountTracks++; foreach (MidiEvent e in track) { try { if (e.Channel > 0 && e.Channel <= 16) { Chanels[e.Channel - 1] = true; } bool keepEvent = false; switch (e.CommandCode) { case MidiCommandCode.NoteOn: //Debug.Log("NoteOn"); if (KeepNoteOff) { // keep note even if no offevent keepEvent = true; } else if (((NoteOnEvent)e).OffEvent != null) { keepEvent = true; } break; case MidiCommandCode.NoteOff: //Debug.Log("NoteOff"); if (KeepNoteOff) { keepEvent = true; } break; case MidiCommandCode.ControlChange: //ControlChangeEvent ctrl = (ControlChangeEvent)e; //Debug.Log("NoteOff"); keepEvent = true; break; case MidiCommandCode.PatchChange: keepEvent = true; break; case MidiCommandCode.MetaEvent: MetaEvent meta = (MetaEvent)e; switch (meta.MetaEventType) { case MetaEventType.SetTempo: // Set the first tempo value find //if (QuarterPerMinuteValue < 0d) //Debug.Log("MicrosecondsPerQuarterNote:"+((TempoEvent)e).MicrosecondsPerQuarterNote); ChangeTempo(((TempoEvent)e).Tempo); break; } keepEvent = true; break; } if (keepEvent) { events.Add(new TrackMidiEvent() { IndexTrack = CountTracks, Event = e.Clone() }); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); //List<TrackMidiEvent> MidiSorted = events.OrderBy(o => o.Event.AbsoluteTime).ToList(); return(events.OrderBy(o => o.Event.AbsoluteTime).ToList()); } } } /// Sort midi event by time List <TrackMidiEvent> MidiSorted = events.OrderBy(o => o.Event.AbsoluteTime).ToList(); return(MidiSorted); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(null); }
private static string FormatEvent(MetaEvent _event) { var ret = ""; ret += "After " + _event.TimeDelta.ToNumber() + " ticks, "; switch (_event.MetaEventType) { case 0x00: ret += "Sequence Number "; ushort temp = 0; temp |= _event.EventData[0]; temp <<= 8; temp |= _event.EventData[1]; ret += temp + "\n"; break; case 0x01: ret += "Text: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x02: ret += "Copyright: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x03: ret += "Track Name: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x04: ret += "Instrument: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x05: ret += "Lyric: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x06: ret += "Mark: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x07: ret += "Cue: "; foreach (var b in _event.EventData) { ret += (char)b; } ret += "\n"; break; case 0x20: ret += "Mark: " + (_event.EventData[0] + 1) + "\n"; break; case 0x21: ret += "A thing! " + ((_event.EventData[0] & 0x0F) + 1) + "\n"; break; case 0x2F: ret += "END OF TRACK\n\n"; break; case 0x51: ret += "Tempo Change: "; uint temp1 = 0; temp1 |= _event.EventData[0]; temp1 <<= 8; temp1 |= _event.EventData[1]; temp1 <<= 8; temp1 |= _event.EventData[2]; ret += ((int)(1F / temp1 / 60F) * 1e+6) + "\n"; break; case 0x54: ret += "SMTPE Offset: " + _event.EventData[0] + "." + _event.EventData[1] + "." + _event.EventData[2] + "." + _event.EventData[3] + "." + _event.EventData[4] + "\n"; break; case 0x58: ret += "Time Signature: " + _event.EventData[0] + "/" + (0x01 << _event.EventData[1]) + ", " + _event.EventData[2] + "clocks per tick, " + _event.EventData[3] + " 32nd notes per 24 clocks\n"; break; case 0x59: ret += "Key Signature: "; switch (_event.EventData[1]) { case 0: switch ((sbyte)_event.EventData[0]) { case -7: ret += "Cb Major\n"; break; case -6: ret += "Gb Major\n"; break; case -5: ret += "Db Major\n"; break; case -4: ret += "Ab Major\n"; break; case -3: ret += "Eb Major\n"; break; case -2: ret += "Bb Major\n"; break; case -1: ret += "F Major\n"; break; case 0: ret += "C Major\n"; break; case 1: ret += "G Major\n"; break; case 2: ret += "D Major\n"; break; case 3: ret += "A Major\n"; break; case 4: ret += "E Major\n"; break; case 5: ret += "B Major\n"; break; case 6: ret += "F# Major\n"; break; case 7: ret += "C# Major\n"; break; default: ret += ""; break; } break; case 1: switch ((sbyte)_event.EventData[0]) { case -7: ret += "Ab minor\n"; break; case -6: ret += "Eb minor\n"; break; case -5: ret += "Bb minor\n"; break; case -4: ret += "F minor\n"; break; case -3: ret += "C minor\n"; break; case -2: ret += "G minor\n"; break; case -1: ret += "D minor\n"; break; case 0: ret += "A minor\n"; break; case 1: ret += "E minor\n"; break; case 2: ret += "B minor\n"; break; case 3: ret += "F# minor\n"; break; case 4: ret += "C# minor\n"; break; case 5: ret += "G# minor\n"; break; case 6: ret += "D# minor\n"; break; case 7: ret += "A# minor\n"; break; default: ret += ""; break; } break; default: ret += ""; break; } break; case 0x7F: ret += "sequencer specific event\n"; break; default: ret += ""; break; } return(ret); }