/// <summary> /// Dump the Midi content as a human readable text file /// </summary> /// <param name="sequence">The sequence to be dumped.</param> /// <param name="outputFilePath">text file to store dump to</param> public static void DumpMidi(this Sequence sequence, string outputFilePath) { int trackno = 1; using (var outfile = new StreamWriter(outputFilePath, false)) { // write header string midiTypeName = MidiHelper.GetMidiFormatString(sequence.MidiFileType); outfile.WriteLine("Midi Type: {0} = {1}", sequence.MidiFileType, midiTypeName); float divisionType = sequence.DivisionType; string divisionTypeString = ""; if (divisionType == Sequence.PPQ) { divisionTypeString = "PPQ"; } else if (divisionType == Sequence.SMPTE_24) { divisionTypeString = "SMPTE_24"; } else if (divisionType == Sequence.SMPTE_25) { divisionTypeString = "SMPTE_25"; } else if (divisionType == Sequence.SMPTE_30) { divisionTypeString = "SMPTE_30"; } else if (divisionType == Sequence.SMPTE_30DROP) { divisionTypeString = "SMPTE_30DROP"; } outfile.WriteLine("Division Type: {0} = {1}", divisionTypeString, divisionType); outfile.WriteLine("Resolution: {0}", sequence.Resolution); outfile.WriteLine("Tracks: {0}", sequence.Tracks.Count); outfile.WriteLine("Time: {0} ticks", sequence.GetTickLength()); outfile.WriteLine(); foreach (var track in sequence.Tracks) { outfile.WriteLine("Track {0}", trackno); foreach (var ev in track.Events) { long tick = ev.Tick; int beat = (int)tick / sequence.Resolution; int tickRemainder = (int)tick % sequence.Resolution; MidiMessage msg = ev.Message; outfile.WriteLine("{0:0000}:{1:000} {2}", beat, tickRemainder, msg); } trackno++; } } }
/// <summary> /// Return the string representation of this object /// </summary> /// <returns>the string representation of this object</returns> public override string ToString() { // GetMetaStrings: { type name, length, value string } object[] meta = this.GetMetaStrings(); string metaStrings = string.Format("{0,-22} '{2}' ({1} bytes)", meta[0], meta[1], meta[2]); int messageType = GetMetaMessageType(); string typeName = MidiHelper.GetMetaString(messageType); byte[] messageData = GetMetaMessageData(); string hex = MidiHelper.ByteArrayToString(messageData, ","); //return string.Format("{0} [{1}:{2}: {3}]", metaStrings, messageType, typeName, hex); return(string.Format("{0}", metaStrings)); }
/// <summary> /// Return the string representation of this object /// </summary> /// <returns>the string representation of this object</returns> public override string ToString() { // Event, Note, Value, Patch, Text, Channel object[] meta = this.GetShortStrings(false); string metaStrings = string.Format("{0,-22} {1,-8} {2,-8} {3,-22} {4,-8} {5,-8}", meta[0], meta[1], meta[2], meta[3], meta[4], meta[5]); int command = GetCommand(); string commandName = MidiHelper.GetEventTypeString((MidiHelper.MidiEventType)command); int channel = GetChannel(); byte[] messageData = GetMessage(); string hex = MidiHelper.ByteArrayToString(messageData, ","); //return string.Format("{0} [{1}:{2}: {3}]", metaStrings, command, commandName, hex); return(string.Format("{0}", metaStrings)); }
/// <summary> /// Return the string representation of this object /// </summary> /// <returns>the string representation of this object</returns> public override string ToString() { string hex = MidiHelper.ByteArrayToString(data, ","); return(string.Format("Sysex: [{0}]", hex)); }
/// <summary>Converts the MIDI sequence into a new one with the desired format.</summary> /// <param name="sequence">The sequence to be converted.</param> /// <param name="format">The format to which we want to convert the sequence.</param> /// <param name="options">Options used when doing the conversion.</param> /// <param name="newResolution">the new resolution (disable using 0 or -1)</param> /// <returns>The new, converted sequence.</returns> /// <remarks>This is based on the excellent MidiSharp package by Stephen Toub.</remarks> public static Sequence Convert(this Sequence sequence, int format, FormatConversionOption options, int newResolution, string trackName) { if (sequence.MidiFileType == format) { // If the desired format is the same as the original, just return a copy. // No transformation is necessary. sequence = new Sequence(sequence); } else if (format != 0 || sequence.Tracks.Count == 1) { // If the desired format is is not 0 or there's only one track, just copy the sequence with a different format number. // If it's not zero, then multiple tracks are acceptable, so no transformation is necessary. // Or if there's only one track, then there's no possible transformation to be done. var newSequence = new Sequence(sequence.DivisionType, sequence.Resolution, 0, format); foreach (Track t in sequence.Tracks) { newSequence.Tracks.Add(new Track(t)); } sequence = newSequence; } else { // Now the harder cases, converting to format 0. We need to combine all tracks into 1, // as format 0 requires that there only be a single track with all of the events for the song. int originalResolution = sequence.Resolution; if (newResolution <= 0) { newResolution = originalResolution; } sequence = new Sequence(sequence); sequence.MidiFileType = (int)MidiHelper.MidiFormat.SingleTrack; // Add all events to new track (except for end of track markers and SequenceOrTrackName events) int trackNumber = 0; var newTrack = new Track(); foreach (Track track in sequence.Tracks) { foreach (MidiEvent midiEvent in track.Events) { bool doAddEvent = true; var msg = midiEvent.Message; // check if this is a meta message var mm = msg as MetaMessage; if (mm != null) { // we have a meta message // add all meta messages except the end of track markers (we'll add our own) int type = mm.GetMetaMessageType(); if (type == (int)MidiHelper.MetaEventType.EndOfTrack) { doAddEvent = false; } else if (type == (int)MidiHelper.MetaEventType.SequenceOrTrackName) { doAddEvent = false; // store track name, will be used later if (string.IsNullOrEmpty(trackName)) { byte[] data = mm.GetMetaMessageData(); string text = MidiHelper.GetString(data); trackName = MidiHelper.TextString(text); } } } // check if this is a short message var sm = msg as ShortMessage; if (sm != null) { // get the data var channel = sm.GetChannel(); var cmd = sm.GetCommand(); var data1 = sm.GetData1(); var data2 = sm.GetData2(); // If this event has a channel, and if we're storing tracks as channels, copy to it if ((options & FormatConversionOption.CopyTrackToChannel) > 0 && trackNumber >= MidiHelper.MIN_CHANNEL && trackNumber <= MidiHelper.MAX_CHANNEL) { if (sm.IsChannelMessage()) { // store the track number as the channel sm.SetMessage(cmd, trackNumber, data1, data2); } } if ((options & FormatConversionOption.NoteOffZero2NoteOnZero) > 0) { // If the event is a NoteOff with Volume 0 if (cmd == (int)MidiHelper.MidiEventType.NoteOff && data2 == 0) { // convert to a NoteOn instead sm.SetMessage((int)MidiHelper.MidiEventType.NoteOn, channel, data1, data2); } } } // convert ticks if resolution has changed if (originalResolution != newResolution) { if (midiEvent.Tick != 0) { double fraction = (double)midiEvent.Tick / (double)originalResolution; int tick = (int)(fraction * newResolution); midiEvent.Tick = tick; } } // Add all events, except for end of track markers (we'll add our own) if (doAddEvent) { //newTrack.Events.Add(midiEvent); // add to end of list newTrack.Add(midiEvent); // add in the right position based on the tick } } trackNumber++; } if (originalResolution != newResolution) { sequence.Resolution = newResolution; } // Sort the events by total time // newTrack.Events.Sort((x, y) => x.Tick.CompareTo(y.Tick)); // Note! using newTrack.Add instead of newTrack.Events.Add, already ensures a correct sort order // Top things off with an end-of-track marker. newTrack.Add(MetaEvent.CreateMetaEvent("EndOfTrack", "", newTrack.Ticks(), 0)); // add a new track name as the very first event newTrack.Events.Insert(0, MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.SequenceOrTrackName, trackName, 0, 0)); // We now have all of the combined events in newTrack. Clear out the sequence, replacing all the tracks // with this new one. sequence.Tracks.Clear(); sequence.Tracks.Add(newTrack); } return(sequence); }
/// <summary> /// Creates C# code from a MIDI sequence. /// </summary> /// <param name="sequence">The sequence to be generated as C# code and saved.</param> /// <param name="outputFilePath">path to text file to store generated code to</param> public static void SaveGenerateMidiCode(this Sequence sequence, string outputFilePath) { // generate code int trackno = 1; using (var outfile = new StreamWriter(outputFilePath, false)) { string midiTypeName = MidiHelper.GetMidiFormatString(sequence.MidiFileType); DateTime now = DateTime.Now; outfile.WriteLine("/*"); outfile.WriteLine("* Generated by GnuMidi"); outfile.WriteLine("* Author: [email protected]"); outfile.WriteLine("* Date: {0}", now.ToString("yyyy-MM-dd")); outfile.WriteLine("* Time: {0}", now.ToString("HH:mm")); outfile.WriteLine("*/"); outfile.WriteLine("using System;"); outfile.WriteLine("using System.IO;"); outfile.WriteLine("using gnu.sound.midi;"); outfile.WriteLine("using gnu.sound.midi.file;"); outfile.WriteLine("using gnu.sound.midi.info;"); outfile.WriteLine(); outfile.WriteLine("namespace MidiGeneration"); outfile.WriteLine("{"); outfile.WriteLine(" public static class GenerateMidi"); outfile.WriteLine(" {"); outfile.WriteLine(" public static void Main(string[] args)"); outfile.WriteLine(" {"); outfile.WriteLine(" // Generate Midi"); outfile.WriteLine(" var sequence = GenerateSequence();"); outfile.WriteLine(" sequence.DumpMidi(\"generated_dump.txt\");"); outfile.WriteLine(" new MidiFileWriter().Write(sequence, sequence.MidiFileType, new FileInfo(\"generated.mid\"));"); outfile.WriteLine(" }"); outfile.WriteLine(); outfile.WriteLine(" public static Sequence GenerateSequence() {"); outfile.WriteLine(); outfile.WriteLine(" // Generate midi file"); outfile.WriteLine(" var sequence = new Sequence({0}, {1}, 0, (int) MidiHelper.MidiFormat.{2});", sequence.DivisionType, sequence.Resolution, midiTypeName); foreach (var track in sequence.Tracks) { outfile.WriteLine(); outfile.WriteLine(" var track{0} = sequence.CreateTrack();", trackno); foreach (var ev in track.Events) { long tick = ev.Tick; int beat = (int)tick / sequence.Resolution; int tickRemainder = (int)tick % sequence.Resolution; MidiMessage msg = ev.Message; // check if this is a meta message var mm = msg as MetaMessage; if (mm != null) { string metaCodeString = mm.CreateMetaEventGeneratedCode(tick, sequence.Resolution); outfile.WriteLine(" track{0}.Add({1});", trackno, metaCodeString); } // check if this is a short message var sm = msg as ShortMessage; if (sm != null) { string shortCodeString = sm.CreateShortEventGeneratedCode(true, tick); outfile.WriteLine(" track{0}.Add({1}", trackno, shortCodeString); } // check if this is a sysex message var ss = msg as SysexMessage; if (ss != null) { var data = ss.GetMessage(); string sysexString = MidiHelper.ByteArrayToString(data, ","); outfile.WriteLine(" track{0}.Add(SysexEvent.CreateSysexEvent(\"{1}\", {2}));", trackno, sysexString, tick); } } trackno++; } outfile.WriteLine(); outfile.WriteLine(" return sequence;"); outfile.WriteLine(" }"); outfile.WriteLine(" }"); outfile.WriteLine("}"); } }