private static void RandomlyPlaystuff() { int midiOutDevice = 0; // GS Wavetable Synth Random r = new Random(); Dictionary <string, NoteOnEvent> currentlyPlaying = new Dictionary <string, NoteOnEvent>(); using (var midiOut = new MidiOut(midiOutDevice)) { // first lets set the patches for channels 1-9 for (int i = 1; i <= 9; i++) { var patchChange = new PatchChangeEvent(0, i, (i - 1) * 8); midiOut.Send(patchChange.GetAsShortMessage()); } // Okay, now lets figure out where our things are at for (int i = 0; i < 100; i++) { int noteToPlay = r.Next(20, 100); int velocityToPlay = r.Next(50, 100); int channel = r.Next(1, 2); // channel indicates patch string hash = $"{channel}"; NoteOnEvent existingNoteOn; if (currentlyPlaying.TryGetValue(hash, out existingNoteOn)) { midiOut.Send(existingNoteOn.OffEvent.GetAsShortMessage()); currentlyPlaying.Remove(hash); } var noteOn = new NoteOnEvent(0, channel, noteToPlay, velocityToPlay, 0); midiOut.Send(noteOn.GetAsShortMessage()); Console.ReadLine(); } foreach (var existingNote in currentlyPlaying.Values) { midiOut.Send(existingNote.OffEvent.GetAsShortMessage()); } } }
/// <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> /// Convert neb steps to midi file. /// </summary> /// <param name="steps"></param> /// <param name="midiFileName"></param> /// <param name="channels">Map of channel number to channel name.</param> /// <param name="bpm">Beats per minute.</param> /// <param name="info">Extra info to add to midi file.</param> public static void ExportToMidi(StepCollection steps, string midiFileName, Dictionary <int, string> channels, double bpm, string info) { int exportPpq = 96; // Events per track. Dictionary <int, IList <MidiEvent> > trackEvents = new(); ///// Meta file stuff. MidiEventCollection events = new(1, exportPpq); ///// Add Header chunk stuff. IList <MidiEvent> lhdr = events.AddTrack(); //lhdr.Add(new TimeSignatureEvent(0, 4, 2, (int)ticksPerClick, 8)); //TimeSignatureEvent me = new TimeSignatureEvent(long absoluteTime, int numerator, int denominator, int ticksInMetronomeClick, int no32ndNotesInQuarterNote); // - numerator of the time signature (as notated). // - denominator of the time signature as a negative power of 2 (ie 2 represents a quarter-note, 3 represents an eighth-note, etc). // - number of MIDI clocks between metronome clicks. // - number of notated 32nd-notes in a MIDI quarter-note (24 MIDI Clocks). The usual value for this parameter is 8. //lhdr.Add(new KeySignatureEvent(0, 0, 0)); // - number of flats (-ve) or sharps (+ve) that identifies the key signature (-7 = 7 flats, -1 = 1 //flat, 0 = key of C, 1 = 1 sharp, etc). // - major (0) or minor (1) key. // - abs time. // Tempo. lhdr.Add(new TempoEvent(0, 0) { Tempo = bpm }); // General info. lhdr.Add(new TextEvent("Midi file created by Nebulator.", MetaEventType.TextEvent, 0)); lhdr.Add(new TextEvent(info, MetaEventType.TextEvent, 0)); lhdr.Add(new MetaEvent(MetaEventType.EndTrack, 0, 0)); ///// Make one midi event collection per track. foreach (int channel in channels.Keys) { IList <MidiEvent> le = events.AddTrack(); trackEvents.Add(channel, le); le.Add(new TextEvent(channels[channel], MetaEventType.SequenceTrackName, 0)); // >> 0 SequenceTrackName G.MIDI Acou Bass } // Make a transformer. MidiTime mt = new() { InternalPpq = Time.SubdivsPerBeat, MidiPpq = exportPpq, Tempo = bpm }; // Run through the main steps and create a midi event per. foreach (Time time in steps.Times) { long mtime = mt.InternalToMidi(time.TotalSubdivs); foreach (Step step in steps.GetSteps(time)) { MidiEvent evt; switch (step) { case StepNoteOn stt: evt = new NoteEvent(mtime, stt.ChannelNumber, MidiCommandCode.NoteOn, (int)MathUtils.Constrain(stt.NoteNumber, 0, Definitions.MAX_MIDI), (int)(MathUtils.Constrain(stt.VelocityToPlay, 0, 1.0) * Definitions.MAX_MIDI)); trackEvents[step.ChannelNumber].Add(evt); if (stt.Duration.TotalSubdivs > 0) // specific duration { evt = new NoteEvent(mtime + mt.InternalToMidi(stt.Duration.TotalSubdivs), stt.ChannelNumber, MidiCommandCode.NoteOff, (int)MathUtils.Constrain(stt.NoteNumber, 0, Definitions.MAX_MIDI), 0); trackEvents[step.ChannelNumber].Add(evt); } break; case StepNoteOff stt: evt = new NoteEvent(mtime, stt.ChannelNumber, MidiCommandCode.NoteOff, (int)MathUtils.Constrain(stt.NoteNumber, 0, Definitions.MAX_MIDI), 0); trackEvents[step.ChannelNumber].Add(evt); break; case StepControllerChange stt: if (stt.ControllerId == ControllerDef.NoteControl) { // Shouldn't happen, ignore. } else if (stt.ControllerId == ControllerDef.PitchControl) { evt = new PitchWheelChangeEvent(mtime, stt.ChannelNumber, (int)MathUtils.Constrain(stt.Value, 0, Definitions.MAX_MIDI)); trackEvents[step.ChannelNumber].Add(evt); } else // CC { evt = new ControlChangeEvent(mtime, stt.ChannelNumber, (MidiController)stt.ControllerId, (int)MathUtils.Constrain(stt.Value, 0, Definitions.MAX_MIDI)); trackEvents[step.ChannelNumber].Add(evt); } break; case StepPatch stt: evt = new PatchChangeEvent(mtime, stt.ChannelNumber, (int)stt.Patch); trackEvents[step.ChannelNumber].Add(evt); break; default: break; } } } // Finish up channels with end marker. foreach (IList <MidiEvent> let in trackEvents.Values) { long ltime = let.Last().AbsoluteTime; let.Add(new MetaEvent(MetaEventType.EndTrack, 0, ltime)); } MidiFile.Export(midiFileName, events); } }
public void Load(string path) { LoadedMidi = new MidiFile(path, false); // Parse the midi for info; we want to start by getting a list of all the channels // We also need to know the highest and lowest note for each Instrument/Channel (screw tracks) // And of course, that gives us overall highest/lowest MidiChannelMap = new Dictionary <int, MidiChannel>(); MidiTrackMap = new Dictionary <int, MidiTrack>(); MidiChannels = new List <MidiChannel>(); MidiTracks = new List <MidiTrack>(); for (int i = 0; i < LoadedMidi.Tracks; i++) { var newTrack = new MidiTrack("Untitled", i); MidiTrackMap[i] = newTrack; MidiTracks.Add(newTrack); var e = LoadedMidi.Events[i]; foreach (var ie in e) { if (ie is TextEvent) { var me = ie as TextEvent; if (me.MetaEventType == MetaEventType.SequenceTrackName) { MidiTrackMap[i].TrackName = me.Text; } } else if (ie is PatchChangeEvent) { // These are the correct events, they happen at the start to set the instrument for a channel // Note that specifically channel 10 is usually interpreted as drums, and the instrument they set on it is arbitrary var patchChange = ie as PatchChangeEvent; string instrumentName = "Drums"; if (ie.Channel != 10) { instrumentName = PatchChangeEvent.GetPatchName(patchChange.Patch); } var channel = new MidiChannel(instrumentName, ie.Channel); MidiChannelMap[ie.Channel] = channel; MidiChannels.Add(channel); } else if (ie is NoteOnEvent) { var noteEvent = ie as NoteOnEvent; MidiChannel channel; if (!MidiChannelMap.ContainsKey(ie.Channel)) { // We got a note for a channel that never had an instrument set // Initialize it as piano channel = new MidiChannel("Piano (Default)", ie.Channel); MidiChannelMap[ie.Channel] = channel; MidiChannels.Add(channel); } else { channel = MidiChannelMap[ie.Channel]; } // If the OffEvent is null, that means this is itself an off event... otherwise it links to the one that ends it if (noteEvent.OffEvent != null && noteEvent.Velocity > 0) { if (channel.HighestNote == null || noteEvent.NoteNumber > channel.HighestNote.Number) { channel.HighestNote = new MidiNote(noteEvent.NoteNumber); } if (channel.LowestNote == null || noteEvent.NoteNumber < channel.LowestNote.Number) { channel.LowestNote = new MidiNote(noteEvent.NoteNumber); } } } } } }
/// <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> /// Read midi event Tempo and Patch change from start /// </summary> /// <param name="timeFromStartMS"></param> /// <returns></returns> public List <MPTKEvent> ReadChangeFromStart(int position) { List <MPTKEvent> midievents = new List <MPTKEvent>();; try { if (midifile != null) { if (position < 0 || position >= MidiSorted.Count) { position = MidiSorted.Count - 1; } for (int currentPosEvent = 0; currentPosEvent < position; currentPosEvent++) { TrackMidiEvent trackEvent = MidiSorted[currentPosEvent]; MPTKEvent midievent = null; switch (trackEvent.Event.CommandCode) { case MidiCommandCode.ControlChange: ControlChangeEvent controlchange = (ControlChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.ControlChange, Channel = trackEvent.Event.Channel - 1, Controller = (MPTKController)controlchange.Controller, Value = controlchange.ControllerValue, }; break; case MidiCommandCode.PatchChange: PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.PatchChange, Channel = trackEvent.Event.Channel - 1, Value = change.Patch, }; break; case MidiCommandCode.MetaEvent: MetaEvent meta = (MetaEvent)trackEvent.Event; if (meta.MetaEventType == MetaEventType.SetTempo) { TempoEvent tempo = (TempoEvent)meta; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.MetaEvent, Channel = trackEvent.Event.Channel - 1, Meta = (MPTKMeta)meta.MetaEventType, Duration = tempo.Tempo, }; } break; } if (midievent != null) { midievents.Add(midievent); } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(midievents); }
/// <summary> /// Add a TrackMidiEvent to a list of MPTKEvent. /// </summary> /// <param name="mptkEvents">Must be alloc before the call</param> /// <param name="trackEvent"></param> /// <returns></returns> private bool ConvertToEventForNote(List <MPTKEvent> mptkEvents, TrackMidiEvent trackEvent) { bool exitLoop = false; MPTKEvent midievent = null; switch (trackEvent.Event.CommandCode) { case MidiCommandCode.NoteOn: if (((NoteOnEvent)trackEvent.Event).OffEvent != null) { NoteOnEvent noteon = (NoteOnEvent)trackEvent.Event; //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)); if (noteon.NoteLength > 0) { midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.NoteOn, Value = noteon.NoteNumber, Channel = trackEvent.Event.Channel - 1, Velocity = noteon.Velocity, Duration = noteon.NoteLength * TickLengthMs, Length = noteon.NoteLength, }; //mptkEvents.Add(midievent); // show playing note //Debug.Log(midievent.Channel); if (midievent.Channel == selectedChannel) { Kbd_PKeyGen.instance.ShowMidiNoteOn(noteon.NoteNumber); } if (LogEvents) { string notename = (midievent.Channel != 9) ? String.Format("{0}{1}", NoteNames[midievent.Value % 12], midievent.Value / 12) : "Drum"; Debug.Log(BuildInfoTrack(trackEvent) + string.Format("NoteOn {0,3:000}\t{1,-4}\tLenght:{2,5}\t{3}\tVeloc:{4,3}", midievent.Value, notename, noteon.NoteLength, NoteLength(midievent), noteon.Velocity)); } } else if (KeepNoteOff) { midievent = CreateNoteOffForNote(mptkEvents, trackEvent); } } break; case MidiCommandCode.NoteOff: midievent = CreateNoteOffForNote(mptkEvents, trackEvent); break; case MidiCommandCode.ControlChange: ControlChangeEvent controlchange = (ControlChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.ControlChange, Channel = trackEvent.Event.Channel - 1, Controller = (MPTKController)controlchange.Controller, Value = controlchange.ControllerValue, }; mptkEvents.Add(midievent); // Other midi event if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Control {0} {1}", controlchange.Controller, controlchange.ControllerValue)); } break; case MidiCommandCode.PatchChange: PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.PatchChange, Channel = trackEvent.Event.Channel - 1, Value = change.Patch, }; mptkEvents.Add(midievent); if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Patch {0,3:000} {1}", change.Patch, PatchChangeEvent.GetPatchName(change.Patch))); } break; case MidiCommandCode.MetaEvent: MetaEvent meta = (MetaEvent)trackEvent.Event; midievent = new MPTKEvent() { Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.MetaEvent, Channel = trackEvent.Event.Channel - 1, Meta = (MPTKMeta)meta.MetaEventType, }; switch (meta.MetaEventType) { case MetaEventType.TimeSignature: AnalyzeTimeSignature(meta); break; case MetaEventType.SetTempo: TempoEvent tempo = (TempoEvent)meta; // Tempo change will be done in MidiFilePlayer midievent.Duration = tempo.Tempo; MPTK_MicrosecondsPerQuarterNote = tempo.MicrosecondsPerQuarterNote; // Force exit loop exitLoop = true; break; case MetaEventType.SequenceTrackName: midievent.Info = ((TextEvent)meta).Text; if (!string.IsNullOrEmpty(SequenceTrackName)) { SequenceTrackName += "\n"; } SequenceTrackName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, midievent.Info); //if (LogEvents) Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Sequence '{0}'", note.Info)); break; case MetaEventType.ProgramName: midievent.Info = ((TextEvent)meta).Text; ProgramName += midievent.Info + " "; //if (LogEvents) Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Program '{0}'", note.Info)); break; case MetaEventType.TrackInstrumentName: midievent.Info = ((TextEvent)meta).Text; if (!string.IsNullOrEmpty(TrackInstrumentName)) { TrackInstrumentName += "\n"; } TrackInstrumentName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, midievent.Info); //if (LogEvents) Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Text '{0}'", ((TextEvent)meta).Text)); break; case MetaEventType.TextEvent: midievent.Info = ((TextEvent)meta).Text; TextEvent += midievent.Info + " "; //if (LogEvents) Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Sequence '{0}'", ((TextEvent)meta).Text)); break; case MetaEventType.Copyright: midievent.Info = ((TextEvent)meta).Text; Copyright += midievent.Info + " "; //if (LogEvents) Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Copyright '{0}'", ((TextEvent)meta).Text)); break; case MetaEventType.Lyric: // lyric midievent.Info = ((TextEvent)meta).Text; break; case MetaEventType.Marker: // marker case MetaEventType.CuePoint: // cue point case MetaEventType.DeviceName: break; } if (LogEvents && !string.IsNullOrEmpty(midievent.Info)) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0,15} '{1}'", midievent.Meta, midievent.Info)); } mptkEvents.Add(midievent); //Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0} {1}", meta.MetaEventType, meta.ToString())); break; default: // Other midi event if (LogEvents) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Other {0,15} Not handle by MPTK", trackEvent.Event.CommandCode)); } break; } return(exitLoop); }
IEnumerator MidiPlayer(MidiEventCollection mec) { Debug.Log("Player"); int counter = 0; IList <MidiEvent> list = mec[0]; //We only have 1 track using (MidiOut midiOut = new MidiOut(0)) { while (counter < list.Count) { MidiEvent mEv = list[counter]; if (realNoteList[counter].RealTime > Time.time) //Not time yet to play TODO Make it general, not from start of app (Time.time) { //Debug.Log("Yield: " + list[i].AbsoluteTime + " " + Time.time); yield return(null); } else { //midiOut.Volume = 65535; //Debug.Log(mEv.ToString()); bool off = false; if (mEv is NoteEvent) { if ((mEv as NoteEvent).Velocity == 0) { off = true; } } if (mEv.CommandCode == MidiCommandCode.NoteOn && !off) { NoteOnEvent nOE = (NoteOnEvent)mEv; int note = nOE.NoteNumber; int velocity = nOE.Velocity; if (velocity == 0) { off = true; continue; } int channel = nOE.Channel; midiOut.Send(MidiMessage.StartNote(note, velocity, channel).RawData); float lifeTime = (float)nOE.NoteLength / 1000; lifeTime *= realNoteList[counter].Factor; int relativePos = note - 40; GameObject cubeCopy = Instantiate(cubePrefab, new Vector3(relativePos, channel * 3 - 20, 50), Quaternion.identity) as GameObject; cubeCopy.transform.localScale *= ((float)velocity / 127.0f); StartCoroutine(CubeFade(cubeCopy, lifeTime)); } else if (mEv.CommandCode == MidiCommandCode.NoteOff || off) { NoteEvent nE = (NoteEvent)mEv; int note = nE.NoteNumber; int velocity = nE.Velocity; int channel = nE.Channel; midiOut.Send(MidiMessage.StopNote(note, velocity, channel).RawData); } else if (mEv.CommandCode == MidiCommandCode.PatchChange) { PatchChangeEvent pce = (PatchChangeEvent)mEv; midiOut.Send(MidiMessage.ChangePatch(pce.Patch, pce.Channel).RawData); } else if (mEv.CommandCode == MidiCommandCode.ControlChange) { ControlChangeEvent cce = (ControlChangeEvent)mEv; midiOut.Send(MidiMessage.ChangeControl(cce.Controller.GetHashCode(), cce.ControllerValue, cce.Channel).RawData); } /** * else if (mEv.CommandCode == MidiCommandCode.MetaEvent) * { * * MetaEvent metaEv = (MetaEvent)mEv; * switch (metaEv.MetaEventType) * { * case MetaEventType.TrackSequenceNumber: * break; * case MetaEventType.TextEvent: * break; * case MetaEventType.Copyright: * break; * case MetaEventType.SequenceTrackName: * break; * case MetaEventType.TrackInstrumentName: * break; * case MetaEventType.Lyric: * break; * case MetaEventType.Marker: * break; * case MetaEventType.CuePoint: * break; * case MetaEventType.ProgramName: * break; * case MetaEventType.DeviceName: * break; * case MetaEventType.MidiChannel: * break; * case MetaEventType.MidiPort: * break; * case MetaEventType.EndTrack: * break; * case MetaEventType.SetTempo: * break; * case MetaEventType.SmpteOffset: * break; * case MetaEventType.TimeSignature: * break; * case MetaEventType.KeySignature: * break; * case MetaEventType.SequencerSpecific: * break; * default: * break; * } * * * }**/ //Debug.Log("Yield: " + ((float)list[i].AbsoluteTime) / 1000 + " " + Time.time); counter++; } } } }
public void FromVegas(Vegas vegas) { // select a midi file MessageBox.Show("请选择一个MIDI文件。"); OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "*.mid|*.mid|所有文件|*.*"; openFileDialog.RestoreDirectory = true; openFileDialog.FilterIndex = 1; if (openFileDialog.ShowDialog() == DialogResult.OK) { midiName = openFileDialog.FileName; } else { return; } MidiFile midi = new MidiFile(midiName); // generate statistics of each midi track String[] trackInfo = new String[midi.Events.Tracks]; int ticksPerQuarter = midi.DeltaTicksPerQuarterNote; double msPerQuarter = 0; for (int i = 0; i < midi.Events.Tracks; i++) { String info1 = "轨道 " + i.ToString() + ": "; String info2 = ""; int notesCount = 0; String info3 = "起音 "; foreach (MidiEvent midiEvent in midi.Events[i]) { if ((midiEvent is NoteEvent) && !(midiEvent is NoteOnEvent)) { NoteEvent noteEvent = midiEvent as NoteEvent; if (notesCount == 0) { info3 = info3 + noteEvent.NoteName; } notesCount++; } if ((midiEvent is PatchChangeEvent) && info2.Length == 0) { PatchChangeEvent patchEvent = midiEvent as PatchChangeEvent; for (int j = 4; j < patchEvent.ToString().Split(' ').Length; j++) { info2 += patchEvent.ToString().Split(' ')[j]; } } if ((midiEvent is TempoEvent) && msPerQuarter == 0) { TempoEvent tempoEvent = midiEvent as TempoEvent; msPerQuarter = Convert.ToDouble(tempoEvent.MicrosecondsPerQuarterNote) / 1000; } } trackInfo[i] = info1 + info2 + "; 音符数: " + notesCount.ToString() + "; " + info3; } // select a video clip MessageBox.Show("请选择一个视频或图片素材片段。"); openFileDialog.Filter = "所有文件|*.*"; openFileDialog.RestoreDirectory = true; openFileDialog.FilterIndex = 1; if (openFileDialog.ShowDialog() == DialogResult.OK) { clipName = openFileDialog.FileName; } else { return; } Media media = new Media(clipName); double mediaLength = media.Length.ToMilliseconds(); // start configuration Form2 configForm = new Form2(); for (int i = 1; i < midi.Events.Tracks; i++) { configForm.comboBox1.Items.Add(trackInfo[i]); } configForm.comboBox1.SelectedIndex = 0; Application.Run(configForm); // apply condiguration for (int i = 1; i < midi.Events.Tracks; i++) { if (trackInfo[i] == configForm.comboBox1.SelectedItem.ToString()) { midiTrack = i; } } sheetWidth = int.Parse(configForm.width); sheetPosition = int.Parse(configForm.position); sheetGap = int.Parse(configForm.gap); if (configForm.comboBox2.Text == "2/4") { sheetTempo = 2; } if (configForm.comboBox2.Text == "3/4") { sheetTempo = 3; } if (configForm.comboBox3.Text == "低音") { sheetCelf = 1; } // start processing MIDI VideoTrack[] noteTracks = new VideoTrack[100]; int trackCount = -1; int trackPointer = 0; double barStartTime = 0; double barLength = msPerQuarter * sheetTempo; foreach (MidiEvent midiEvent in midi.Events[midiTrack]) { if (midiEvent is NoteOnEvent) { NoteEvent noteEvent = midiEvent as NoteEvent; NoteOnEvent noteOnEvent = midiEvent as NoteOnEvent; double startTime = midiEvent.AbsoluteTime * msPerQuarter / ticksPerQuarter; double duration = noteOnEvent.NoteLength * msPerQuarter / ticksPerQuarter; int pitch = noteEvent.NoteNumber; // next page while (startTime >= barStartTime + barLength) { barStartTime = barStartTime + barLength; trackPointer = 0; } // generate video events if (trackPointer > trackCount) { trackCount = trackCount + 1; noteTracks[trackCount] = vegas.Project.AddVideoTrack(); } VideoEvent videoEvent = noteTracks[trackPointer].AddVideoEvent(Timecode.FromMilliseconds(startTime), Timecode.FromMilliseconds(barStartTime + barLength - startTime)); Take take = videoEvent.AddTake(media.GetVideoStreamByIndex(0)); TrackEvent trackEvent = videoEvent as TrackEvent; trackEvent.Loop = true; TrackMotionKeyframe keyFrame = noteTracks[trackPointer].TrackMotion.InsertMotionKeyframe(Timecode.FromMilliseconds(startTime)); keyFrame.Type = VideoKeyframeType.Hold; keyFrame.Width = sheetGap * 2 * vegas.Project.Video.Width / vegas.Project.Video.Height; keyFrame.Height = sheetGap * 2; keyFrame.PositionX = -sheetWidth / 2 + sheetWidth / barLength * (startTime - barStartTime); int octave = pitch / 12; int line = pitchMap[pitch % 12]; keyFrame.PositionY = sheetPosition - sheetGap * 3 + (octave - 5) * sheetGap * 3.5 + line * sheetGap * 0.5 + sheetCelf * 12; trackPointer = trackPointer + 1; } } }
private void sr_OnTrackEvent(object sender, TrackEventArgs e) { long counter = 0; long notesBefore = TotalNotes; foreach (var ev in e.Events.Where(x => x is MidiUtils.IO.MidiEvent)) { counter++; // Convert the event to NAudio's system so we can get a shortMessage var em = (MidiUtils.IO.MidiEvent)ev; if (ev.Type == EventType.NoteOn) { TotalNotes++; } MidiEvent me; try { switch (ev.Type) { case EventType.NoteOn: case EventType.NoteOff: case EventType.PolyphonicKeyPressure: if (em.Data2 > 0 && ev.Type == EventType.NoteOn) { me = new NoteOnEvent(ev.Tick, em.Channel + 1, em.Data1, em.Data2, 0); } else { me = new NoteEvent(ev.Tick, em.Channel + 1, (MidiCommandCode)(int)ev.Type, em.Data1, em.Data2); } break; case EventType.ControlChange: me = new ControlChangeEvent(ev.Tick, em.Channel + 1, (MidiController)em.Data1, em.Data2); break; case EventType.ProgramChange: me = new PatchChangeEvent(ev.Tick, em.Channel + 1, em.Data1); break; case EventType.ChannelPressure: me = new ChannelAfterTouchEvent(ev.Tick, em.Channel + 1, em.Data1); break; case EventType.Pitchbend: me = new PitchWheelChangeEvent(ev.Tick, em.Channel + 1, em.Data1 + (em.Data2 << 7)); break; default: throw new InvalidOperationException("Unsupported MIDI event type: " + ev.Type); } } catch (Exception ex) { Exceptions++; continue; } // Send the message try { _midi.Send(me.GetAsShortMessage()); } catch (MmException ex) { if ((int)ex.Result == 67) { Overloads++; } else { Exceptions++; } continue; } if (ev.Type == EventType.NoteOn) { SuccessNotes++; } } PeakNotesPerInterval = Math.Max(PeakNotesPerInterval, TotalNotes - notesBefore); PeakEventsPerInterval = Math.Max(PeakEventsPerInterval, counter); TotalEvents += counter; }
/// <summary> /// Creates MusicTwo instance using midi file /// </summary> /// <param name="midiFile">Midi file to use</param> /// <param name="firstNoteMode">Use first note mode. If some notes appears at same time, use the first one, else the last</param> /// <returns>Created MusicTwo instance</returns> public static MusicTwo FromMidiFile(MidiFile midiFile, bool firstNoteMode) { double tempo = 0; List <Track> tracks = new List <Track>(); for (int track_ = 1; track_ <= midiFile.Tracks; track_++) { Track track__ = new Track(); foreach (var e in midiFile.Events) { foreach (var ee in e) { if (ee is NoteEvent) { NoteEvent eee = (NoteEvent)ee; if (eee is NoteOnEvent) { var eeee = (NoteOnEvent)eee; Note nt = new Note(); nt.time = (int)(eeee.AbsoluteTime / (double)midiFile.DeltaTicksPerQuarterNote * tempo); nt.length = 0; if (eeee.OffEvent != null) { nt.length = (int)(eeee.NoteLength / (double)midiFile.DeltaTicksPerQuarterNote * tempo); } nt.number = eeee.NoteNumber; nt.instrument = track__.instrument; if (eeee.Channel == track_) { track__.notes.Add(nt); } } } if (ee is MetaEvent) { MetaEvent eee = (MetaEvent)ee; if (eee.MetaEventType == MetaEventType.SetTempo) { TempoEvent eeee = (TempoEvent)eee; tempo = eeee.MicrosecondsPerQuarterNote / 1000; } } if (ee is PatchChangeEvent) { PatchChangeEvent eee = (PatchChangeEvent)ee; if (eee.Channel == track_) { track__.instrument = eee.Patch; } } } } tracks.Add(track__); } for (int i = 0; i < tracks.Count; i++) { for (int ii = 0; ii < tracks[i].notes.Count; ii++) { if (tracks[i].notes[ii].length == 0) { tracks[i].notes.RemoveAt(ii); } } if (tracks[i].notes.Count == 0) { tracks.RemoveAt(i); i--; } } MusicTwo music = new MusicTwo(); for (int tracko = 0; tracko < tracks.Count; tracko++) { Track track = tracks[tracko]; TrackTwo nts = new TrackTwo(); NoteTwo ntt = new NoteTwo(); ntt.Number = 0; ntt.Length = 0; nts.Notes.Add(ntt); Note lastNote = (new Note()); Note en = new Note(); lastNote.time = 0; lastNote.number = 0; lastNote.length = 0; en.time = 0; en.number = 0; en.length = 0; for (int i = 0; i < track.notes.Last().time + track.notes.Last().length; i++) { Note nowNote = en; for (int ii = 0; ii < track.notes.Count; ii++) { if (i >= track.notes[ii].time && i <= track.notes[ii].time + track.notes[ii].length) { nowNote = track.notes[ii]; if (firstNoteMode) { goto c1; } } } c1: if (nts.Notes.Last().Number == nowNote.number) { nts.Notes.Last().Length++; continue; } else { NoteTwo nttt = new NoteTwo(); nttt.Number = nowNote.number; nttt.Length = 1; nttt.IsBass = isInstrumentBass(nowNote.instrument); nts.Notes.Add(nttt); } } music.tracks.Add(nts); } return(music); }
PatchInfo(int id) { Id = id; Caption = PatchChangeEvent.GetPatchName(id); }
public void FromVegas(Vegas vegas) { // select a midi file MessageBox.Show("请选择一个MIDI文件。"); OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "*.mid|*.mid|所有文件|*.*"; openFileDialog.RestoreDirectory = true; openFileDialog.FilterIndex = 1; if (openFileDialog.ShowDialog() == DialogResult.OK) { midiName = openFileDialog.FileName; } else { return; } MidiFile midi = new MidiFile(midiName); // generate statistics of each midi track String[] trackInfo = new String[midi.Events.Tracks]; int ticksPerQuarter = midi.DeltaTicksPerQuarterNote; double msPerQuarter = 0; for (int i = 0; i < midi.Events.Tracks; i++) { String info1 = "轨道 " + i.ToString() + ": "; String info2 = ""; int notesCount = 0; String info3 = "起音 "; foreach (MidiEvent midiEvent in midi.Events[i]) { if ((midiEvent is NoteEvent) && !(midiEvent is NoteOnEvent)) { NoteEvent noteEvent = midiEvent as NoteEvent; if (notesCount == 0) { info3 = info3 + noteEvent.NoteName; } notesCount++; } if ((midiEvent is PatchChangeEvent) && info2.Length == 0) { PatchChangeEvent patchEvent = midiEvent as PatchChangeEvent; for (int j = 4; j < patchEvent.ToString().Split(' ').Length; j++) { info2 += patchEvent.ToString().Split(' ')[j]; } } if ((midiEvent is TempoEvent) && msPerQuarter == 0) { TempoEvent tempoEvent = midiEvent as TempoEvent; msPerQuarter = Convert.ToDouble(tempoEvent.MicrosecondsPerQuarterNote) / 1000; } } trackInfo[i] = info1 + info2 + "; 音符数: " + notesCount.ToString() + "; " + info3; } // select a video clip MessageBox.Show("请选择一个视频或音频素材片段。"); openFileDialog.Filter = "所有文件|*.*"; openFileDialog.RestoreDirectory = true; openFileDialog.FilterIndex = 1; if (openFileDialog.ShowDialog() == DialogResult.OK) { clipName = openFileDialog.FileName; } else { return; } Media media = new Media(clipName); double mediaLength = media.Length.ToMilliseconds(); // start configuration ConfigForm configForm = new ConfigForm(); for (int i = 0; i < midi.Events.Tracks; i++) { configForm.comboBoxTrack.Items.Add(trackInfo[i]); } configForm.comboBoxTrack.SelectedIndex = 0; Application.Run(configForm); // apply configuration aconfig = configForm.checkBoxA.Checked; aconfigNoTune = configForm.checkBoxNoTune.Checked; vconfig = configForm.checkBoxV.Checked; vconfigAutoFlip = configForm.checkBoxFlip.Checked; vconfigStartSize = configForm.hScrollBar1.Value; vconfigEndSize = configForm.hScrollBar2.Value; vconfigFadein = configForm.hScrollBar4.Value; vconfigFadeout = configForm.hScrollBar3.Value; aconfigBasePitch = pitchMap[configForm.comboBoxA1.SelectedItem.ToString() + configForm.comboBoxA2.SelectedItem.ToString()]; for (int i = 0; i < midi.Events.Tracks; i++) { if (trackInfo[i] == configForm.comboBoxTrack.SelectedItem.ToString()) { aconfigTrack = i; } } configStartTime = Convert.ToDouble(configForm.startT) * 1000; configEndTime = Convert.ToDouble(configForm.endT) * 1000; // start processing MIDI VideoTrack vTrack = vegas.Project.AddVideoTrack(); AudioTrack[] aTracks = new AudioTrack[20]; double vTrackPosition = 0; int vTrackDirection = 1; double[] aTrackPositions = new double[20]; aTracks[0] = vegas.Project.AddAudioTrack(); aTrackPositions[0] = 0; int aTrackCount = 1; foreach (MidiEvent midiEvent in midi.Events[aconfigTrack]) { if (midiEvent is NoteOnEvent) { NoteEvent noteEvent = midiEvent as NoteEvent; NoteOnEvent noteOnEvent = midiEvent as NoteOnEvent; double startTime = midiEvent.AbsoluteTime * msPerQuarter / ticksPerQuarter; double duration = noteOnEvent.NoteLength * msPerQuarter / ticksPerQuarter; int pitch = noteEvent.NoteNumber; int trackIndex = 0; if (startTime < configStartTime) { continue; } if (startTime > configEndTime) { break; } // generate audio events if (aconfig == true) { while (startTime < aTrackPositions[trackIndex]) { trackIndex++; if (trackIndex == aTrackCount) { aTrackCount++; aTracks[trackIndex] = vegas.Project.AddAudioTrack(); } } AudioEvent audioEvent = aTracks[trackIndex].AddAudioEvent(Timecode.FromMilliseconds(startTime), Timecode.FromMilliseconds(duration)); Take take = audioEvent.AddTake(media.GetAudioStreamByIndex(0)); aTrackPositions[trackIndex] = startTime + duration; TrackEvent trackEvent = audioEvent as TrackEvent; trackEvent.PlaybackRate = mediaLength / duration; trackEvent.Loop = false; // apply pitch shifting if (aconfigNoTune == false) { int pitchDelta = pitch - aconfigBasePitch; if (pitchDelta > 0) { while (pitchDelta > 12) { PlugInNode plugIn0 = vegas.AudioFX.FindChildByName("Pitch Shift"); Effect effect0 = new Effect(plugIn0); audioEvent.Effects.Add(effect0); effect0.Preset = "12"; pitchDelta -= 12; } PlugInNode plugIn = vegas.AudioFX.FindChildByName("Pitch Shift"); Effect effect = new Effect(plugIn); audioEvent.Effects.Add(effect); effect.Preset = pitchDelta.ToString(); } else { while (pitchDelta < -12) { PlugInNode plugIn0 = vegas.AudioFX.FindChildByName("Pitch Shift"); Effect effect0 = new Effect(plugIn0); audioEvent.Effects.Add(effect0); effect0.Preset = "-12"; pitchDelta += 12; } PlugInNode plugIn = vegas.AudioFX.FindChildByName("Pitch Shift"); Effect effect = new Effect(plugIn); audioEvent.Effects.Add(effect); effect.Preset = pitchDelta.ToString(); } } } // generate video events if (vconfig == true) { vTrackPosition = startTime + duration; VideoEvent videoEvent = vTrack.AddVideoEvent(Timecode.FromMilliseconds(startTime), Timecode.FromMilliseconds(duration)); Take take = videoEvent.AddTake(media.GetVideoStreamByIndex(0)); TrackEvent trackEvent = videoEvent as TrackEvent; trackEvent.PlaybackRate = mediaLength / duration; trackEvent.Loop = false; videoEvent.FadeIn.Length = Timecode.FromMilliseconds(duration * vconfigFadein / 100); videoEvent.FadeOut.Length = Timecode.FromMilliseconds(duration * vconfigFadeout / 100); VideoMotionKeyframe key0 = videoEvent.VideoMotion.Keyframes[0]; VideoMotionKeyframe key1 = new VideoMotionKeyframe(Timecode.FromMilliseconds(duration)); videoEvent.VideoMotion.Keyframes.Add(key1); key0.ScaleBy(new VideoMotionVertex((vconfigStartSize / 100) * vTrackDirection, (vconfigStartSize / 100))); key1.ScaleBy(new VideoMotionVertex((vconfigEndSize / 100) * vTrackDirection, (vconfigEndSize / 100))); if (vconfigAutoFlip == true) { vTrackDirection *= -1; } } } } }
public static List <HitsoundLayer> ImportMidi(string path, double offset = 0, bool instruments = true, bool keysounds = true, bool lengths = true, double lengthRoughness = 1, bool velocities = true, double velocityRoughness = 1) { List <HitsoundLayer> hitsoundLayers = new List <HitsoundLayer>(); var strictMode = false; var mf = new MidiFile(path, strictMode); Console.WriteLine( $@"Format {mf.FileFormat}, " + $@"Tracks {mf.Tracks}, " + $@"Delta Ticks Per Quarter Note {mf.DeltaTicksPerQuarterNote}"); List <TempoEvent> tempos = new List <TempoEvent>(); foreach (var track in mf.Events) { tempos.AddRange(track.OfType <TempoEvent>()); } tempos = tempos.OrderBy(o => o.AbsoluteTime).ToList(); List <double> cumulativeTime = CalculateCumulativeTime(tempos, mf.DeltaTicksPerQuarterNote); Dictionary <int, int> channelBanks = new Dictionary <int, int>(); Dictionary <int, int> channelPatches = new Dictionary <int, int>(); // Loop through every event of every track for (int track = 0; track < mf.Tracks; track++) { foreach (var midiEvent in mf.Events[track]) { if (midiEvent is PatchChangeEvent pc) { channelPatches[pc.Channel] = pc.Patch; } else if (midiEvent is ControlChangeEvent co) { if (co.Controller == MidiController.BankSelect) { channelBanks[co.Channel] = (co.ControllerValue * 128) + (channelBanks.ContainsKey(co.Channel) ? (byte)channelBanks[co.Channel] : 0); } else if (co.Controller == MidiController.BankSelectLsb) { channelBanks[co.Channel] = co.ControllerValue + (channelBanks.ContainsKey(co.Channel) ? channelBanks[co.Channel] >> 8 * 128 : 0); } } else if (MidiEvent.IsNoteOn(midiEvent)) { var on = midiEvent as NoteOnEvent; double time = CalculateTime(on.AbsoluteTime, tempos, cumulativeTime, mf.DeltaTicksPerQuarterNote); double length = on.OffEvent != null ? CalculateTime(on.OffEvent.AbsoluteTime, tempos, cumulativeTime, mf.DeltaTicksPerQuarterNote) - time : -1; length = RoundLength(length, lengthRoughness); bool keys = keysounds || on.Channel == 10; int bank = instruments ? on.Channel == 10 ? 128 : channelBanks.ContainsKey(on.Channel) ? channelBanks[on.Channel] : 0 : -1; int patch = instruments && channelPatches.ContainsKey(on.Channel) ? channelPatches[on.Channel] : -1; int instrument = -1; int key = keys ? on.NoteNumber : -1; length = lengths ? length : -1; int velocity = velocities ? on.Velocity : -1; velocity = (int)RoundVelocity(velocity, velocityRoughness); string lengthString = Math.Round(length).ToString(CultureInfo.InvariantCulture); string filename = $"{bank}\\{patch}\\{instrument}\\{key}\\{lengthString}\\{velocity}.wav"; string instrumentName = on.Channel == 10 ? "Percussion" : patch >= 0 && patch <= 127 ? PatchChangeEvent.GetPatchName(patch) : "Undefined"; string keyName = on.NoteName; string name = instrumentName; if (keysounds) { name += "," + keyName; } if (lengths) { name += "," + lengthString; } if (velocities) { name += "," + velocity; } var sampleArgs = new SampleGeneratingArgs(filename, bank, patch, instrument, key, length, velocity); var importArgs = new LayerImportArgs(ImportType.MIDI) { Path = path, Bank = bank, Patch = patch, Key = key, Length = length, LengthRoughness = lengthRoughness, Velocity = velocity, VelocityRoughness = velocityRoughness }; // Find the hitsoundlayer with this path HitsoundLayer layer = hitsoundLayers.Find(o => o.ImportArgs == importArgs); if (layer != null) { // Find hitsound layer with this path and add this time layer.Times.Add(time + offset); } else { // Add new hitsound layer with this path HitsoundLayer newLayer = new HitsoundLayer(name, SampleSet.Normal, Hitsound.Normal, sampleArgs, importArgs); newLayer.Times.Add(time + offset); hitsoundLayers.Add(newLayer); } } } } // Stretch the velocities to reach 127 int maxVelocity = hitsoundLayers.Max(o => o.SampleArgs.Velocity); foreach (var hsl in hitsoundLayers) { hsl.SampleArgs.Velocity = (int)Math.Round(hsl.SampleArgs.Velocity / (float)maxVelocity * 127); } // Sort the times hitsoundLayers.ForEach(o => o.Times = o.Times.OrderBy(t => t).ToList()); // Sort layers by name hitsoundLayers = hitsoundLayers.OrderBy(o => o.Name).ToList(); return(hitsoundLayers); }
/// <summary> /// Add a TrackMidiEvent to a list of MPTKEvent. /// </summary> /// <param name="mptkEvents">Must be alloc before the call</param> /// <param name="trackEvent"></param> /// <returns></returns> private void ConvertToEvent(List <MPTKEvent> mptkEvents, TrackMidiEvent trackEvent) { MPTKEvent midievent = null; switch (trackEvent.Event.CommandCode) { case MidiCommandCode.NoteOn: //if (((NoteOnEvent)trackEvent.Event).OffEvent != null) { NoteOnEvent noteon = (NoteOnEvent)trackEvent.Event; //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)); if (noteon.OffEvent != null) { midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.NoteOn, Value = noteon.NoteNumber, Channel = trackEvent.Event.Channel - 1, Velocity = noteon.Velocity, Duration = Convert.ToInt64(noteon.NoteLength * MPTK_PulseLenght), Length = noteon.NoteLength, }; mptkEvents.Add(midievent); if (LogEvents && seek_ticks < 0) { string notename = (midievent.Channel != 9) ? String.Format("{0}{1}", NoteNames[midievent.Value % 12], midievent.Value / 12) : "Drum"; Debug.Log(BuildInfoTrack(trackEvent) + string.Format("NoteOn {0,3:000}\t{1,-4}\tLenght:{2,5}\t{3}\tVeloc:{4,3}", midievent.Value, notename, noteon.NoteLength, NoteLength(midievent), noteon.Velocity)); } } else // It's a noteoff { if (KeepNoteOff) { midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.NoteOff, Value = noteon.NoteNumber, Channel = trackEvent.Event.Channel - 1, Velocity = noteon.Velocity, Duration = Convert.ToInt64(noteon.NoteLength * MPTK_PulseLenght), Length = noteon.NoteLength, }; mptkEvents.Add(midievent); if (LogEvents && seek_ticks < 0) { string notename = (midievent.Channel != 9) ? String.Format("{0}{1}", NoteNames[midievent.Value % 12], midievent.Value / 12) : "Drum"; Debug.Log(BuildInfoTrack(trackEvent) + string.Format("NoteOff {0,3:000}\t{1,-4}\tLenght:{2}", midievent.Value, notename, " Note Off")); } } } } break; case MidiCommandCode.NoteOff: if (KeepNoteOff) { NoteEvent noteoff = (NoteEvent)trackEvent.Event; //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)); midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.NoteOff, Value = noteoff.NoteNumber, Channel = trackEvent.Event.Channel - 1, Velocity = noteoff.Velocity, Duration = 0, Length = 0, }; mptkEvents.Add(midievent); if (LogEvents && seek_ticks < 0) { string notename = (midievent.Channel != 9) ? String.Format("{0}{1}", NoteNames[midievent.Value % 12], midievent.Value / 12) : "Drum"; Debug.Log(BuildInfoTrack(trackEvent) + string.Format("NoteOff {0,3:000}\t{1,-4}\tLenght:{2}", midievent.Value, notename, " Note Off")); } } break; case MidiCommandCode.PitchWheelChange: PitchWheelChangeEvent pitch = (PitchWheelChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.PitchWheelChange, Channel = trackEvent.Event.Channel - 1, Value = pitch.Pitch, // Pitch Wheel Value 0 is minimum, 0x2000 (8192) is default, 0x3FFF (16383) is maximum }; mptkEvents.Add(midievent); if (LogEvents && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("PitchWheelChange {0}", pitch.Pitch)); } break; case MidiCommandCode.ControlChange: ControlChangeEvent controlchange = (ControlChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.ControlChange, Channel = trackEvent.Event.Channel - 1, Controller = (MPTKController)controlchange.Controller, Value = controlchange.ControllerValue, }; //if ((MPTKController)controlchange.Controller != MPTKController.Sustain) mptkEvents.Add(midievent); // Other midi event if (LogEvents && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Control {0} {1}", controlchange.Controller, controlchange.ControllerValue)); } break; case MidiCommandCode.PatchChange: PatchChangeEvent change = (PatchChangeEvent)trackEvent.Event; midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.PatchChange, Channel = trackEvent.Event.Channel - 1, Value = change.Patch, }; mptkEvents.Add(midievent); if (LogEvents && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Patch {0,3:000} {1}", change.Patch, PatchChangeEvent.GetPatchName(change.Patch))); } break; case MidiCommandCode.MetaEvent: MetaEvent meta = (MetaEvent)trackEvent.Event; midievent = new MPTKEvent() { Track = trackEvent.IndexTrack, Tick = trackEvent.AbsoluteQuantize, Command = MPTKCommand.MetaEvent, Channel = trackEvent.Event.Channel - 1, Meta = (MPTKMeta)meta.MetaEventType, }; switch (meta.MetaEventType) { case MetaEventType.EndTrack: midievent.Info = "End Track"; break; case MetaEventType.TimeSignature: AnalyzeTimeSignature(meta, trackEvent); break; case MetaEventType.SetTempo: if (EnableChangeTempo) { TempoEvent tempo = (TempoEvent)meta; // Tempo change will be done in MidiFilePlayer midievent.Duration = (long)tempo.Tempo; MPTK_MicrosecondsPerQuarterNote = tempo.MicrosecondsPerQuarterNote; fluid_player_set_midi_tempo(tempo.MicrosecondsPerQuarterNote); // Force exit loop if (LogEvents && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0,-15} Tempo:{1} MicrosecondsPerQuarterNote:{2}", meta.MetaEventType, tempo.Tempo, tempo.MicrosecondsPerQuarterNote)); } } break; case MetaEventType.SequenceTrackName: midievent.Info = ((TextEvent)meta).Text; if (!string.IsNullOrEmpty(SequenceTrackName)) { SequenceTrackName += "\n"; } SequenceTrackName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, midievent.Info); break; case MetaEventType.ProgramName: midievent.Info = ((TextEvent)meta).Text; ProgramName += midievent.Info + " "; break; case MetaEventType.TrackInstrumentName: midievent.Info = ((TextEvent)meta).Text; if (!string.IsNullOrEmpty(TrackInstrumentName)) { TrackInstrumentName += "\n"; } TrackInstrumentName += string.Format("T{0,2:00} {1}", trackEvent.IndexTrack, midievent.Info); break; case MetaEventType.TextEvent: midievent.Info = ((TextEvent)meta).Text; TextEvent += midievent.Info + " "; break; case MetaEventType.Copyright: midievent.Info = ((TextEvent)meta).Text; Copyright += midievent.Info + " "; break; case MetaEventType.Lyric: // lyric midievent.Info = ((TextEvent)meta).Text; TextEvent += midievent.Info + " "; break; case MetaEventType.Marker: // marker midievent.Info = ((TextEvent)meta).Text; TextEvent += midievent.Info + " "; break; case MetaEventType.CuePoint: // cue point case MetaEventType.DeviceName: break; } if (LogEvents && !string.IsNullOrEmpty(midievent.Info) && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0,-15} '{1}'", midievent.Meta, midievent.Info)); } mptkEvents.Add(midievent); //Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Meta {0} {1}", meta.MetaEventType, meta.ToString())); break; default: // Other midi event if (LogEvents && seek_ticks < 0) { Debug.Log(BuildInfoTrack(trackEvent) + string.Format("Other {0,-15} Not handle by MPTK", trackEvent.Event.CommandCode)); } break; } }
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 static List <TimeSlice> ReadMidiFile(MidiFile mf, ICollection <int> tracksToSkip, double tempoRatio, bool skipDrums) { var timeSliceList = new List <TimeSlice>(); var tempo = (int)Math.Round(120 * tempoRatio); Console.WriteLine("Default Tempo: {0}", tempo); var trackNumber = 0; foreach (var midiEventList in mf.Events) { Console.WriteLine(""); Console.WriteLine("Track #{0}", trackNumber); Console.WriteLine("---------"); if (tracksToSkip.Contains(trackNumber)) { trackNumber++; continue; } Console.WriteLine("Track length: {0}", midiEventList.Count); var drumFilteredMidiEventList = midiEventList.Where(midiEvent => !skipDrums || (midiEvent.Channel != 10)); foreach (var midiEvent in drumFilteredMidiEventList) { if (midiEvent is TextEvent textEvent) // : MetaEvent { Console.WriteLine("{0}: {1}", textEvent.MetaEventType.ToString(), textEvent.Text); } else if (midiEvent is TimeSignatureEvent timeSignatureEvent) // : MetaEvent { //WriteWarningLine("TimeSignatureEvent", timeSignatureEvent.ToString()); } else if (midiEvent is TrackSequenceNumberEvent trackSequenceNumberEvent) // : MetaEvent { WriteWarningLine("TrackSequenceNumberEvent", trackSequenceNumberEvent.ToString()); } else if (midiEvent is KeySignatureEvent keySignatureEvent) // : MetaEvent { //WriteWarningLine("KeySignatureEvent", keySignatureEvent.ToString()); } else if (midiEvent is TempoEvent tempoEvent) // : MetaEvent { tempo = (int)Math.Round(tempoEvent.Tempo * tempoRatio); Console.WriteLine("Tempo: {0}", tempo); } else if (midiEvent is MetaEvent metaEvent) { if ((metaEvent.MetaEventType != MetaEventType.SequencerSpecific) && (metaEvent.MetaEventType != MetaEventType.EndTrack) && (metaEvent.MetaEventType != MetaEventType.MidiChannel) && (metaEvent.MetaEventType != MetaEventType.MidiPort)) { WriteWarningLine("MetaEvent", metaEvent.ToString()); } } else if (midiEvent is ControlChangeEvent controlChangeEvent) { //WriteWarningLine("ControlChangeEvent", controlChangeEvent.ToString()); } else if (midiEvent is PatchChangeEvent patchChangeEvent) { Console.WriteLine("Patch: {0}", PatchChangeEvent.GetPatchName(patchChangeEvent.Patch)); if (skipDrums && (patchChangeEvent.Patch >= 112)) { Console.WriteLine("Skipping track..."); break; } } else if (midiEvent is NoteOnEvent noteEvent1) { if (MidiEvent.IsNoteOff(noteEvent1) == false) { if (noteEvent1.OffEvent != null) { var ticksPerMinute = mf.DeltaTicksPerQuarterNote * tempo; var millisecondsPerTick = 60.0 * 1000.0 / ticksPerMinute; var start = (int)Math.Round(noteEvent1.AbsoluteTime * millisecondsPerTick); var stop = (int)Math.Round(noteEvent1.OffEvent.AbsoluteTime * millisecondsPerTick); timeSliceList.Add(new TimeSlice(start, stop, new Note(noteEvent1.NoteNumber, noteEvent1.Velocity))); } else { WriteWarningLine("NoteOnEvent", "No Corresponding Note Off Event"); } } } else if (midiEvent is NoteEvent noteEvent) { if (noteEvent.CommandCode != MidiCommandCode.NoteOff) { WriteWarningLine("NoteEvent", noteEvent.ToString()); } } else { WriteWarningLine("MIDI Event Type", midiEvent.GetType().ToString()); } } trackNumber++; } timeSliceList.Sort(CompareTimeSlicesByStartTime); return(timeSliceList); }