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); }
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); }
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 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> /// 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> /// 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); }
/// <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; } }
PatchInfo(int id) { Id = id; Caption = PatchChangeEvent.GetPatchName(id); }
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); }