/// <summary> /// Update the data content of the message /// This is parsed into a <code>byte[]</code> that becomes the data of /// the MetaMessage.<br> /// Most events treat the string as a text value and just convert /// each character to a <code>byte</code> in the array but the /// following message types are handled specially /// <dl> /// <dt>Tempo</dt> /// <dd>an integer value with an optional "bpm" suffix. e.g. 120bpm</dd> /// <dt>SmpteOffset</dt> /// <dd>a string in the form <code>h:m:s:f:d</code> where<br> /// h=hours m=minutes s=seconds f=frames d=fields<br> /// If fewer than 5 values are given then the parser treats them as<br> /// 1. <b>s</b><br> /// 2. <b>m:s</b><br> /// 3. <b>h:m:s</b><br> /// 4. <b>h:m:s:f</b><br> /// 5. <b>h:m:s:f:d</b><br> /// and the unspecified values are set to zero. /// </dd> /// <dt>TimeSignature</dt> /// <dd>a time signature string in the format <code>n[/d]</code> where<br> /// n=numerator d=denominator<br> /// If only <code>n</code> is given then <code>d</code> defaults to 4</dd> /// <dt>KeySignature</dt> /// <dd>one of the following key signature strings<br> /// <b>Cb Gb Db Ab Eb Bb F C G D A E B F# C#</b/></dd> /// <dt>SequenceNumber and SequencerSpecificEvent</dt> /// <dd>a space-separated list of values that are parsed into /// a <code>byte[]</code> using <code>Convert.ToByte()</code><br> /// If any value cannot be parsed into a <code>byte</code> /// then it is treated as zero</dd> /// </dl /// </summary> /// <param name="mess">the message to update</param> /// <param name="value">a String that represents the data for the event.<br></param> /// <param name="ticksPerBeat">the tick resolution of the sequence</param> public static void SetMetaData(MetaMessage mess, string value, int ticksPerBeat) { byte[] data; int type = mess.GetMetaMessageType(); if (mess.IsText()) { data = MidiHelper.GetBytes(value); } else if (type == (int)MidiHelper.MetaEventType.Tempo) { int bpm = ParseTempo(value); data = BpmToMicroSecs(bpm); } else if (type == (int)MidiHelper.MetaEventType.TimeSignature) { data = ParseTimeSignature(value, ticksPerBeat); } else if (type == (int)MidiHelper.MetaEventType.KeySignature) { data = MidiHelper.ConvertSBytes(KeySignatures.GetKeyValues(value)); } else if (type == (int)MidiHelper.MetaEventType.SmpteOffset) { data = ParseSMPTEOffset(value); } else { // treat the string as a space separated list of // string representations of byte values // TODO: Should handle decimal, hexadecimal and octal representations // originally using the java.lang.Byte.decode() method data = MidiHelper.StringToByteArray(value, " "); } if (data != null) { mess.SetMessage(type, data, data.Length); } }
// A good MIDI Files Specification: http://www.somascape.org/midi/tech/mfile.html #region MetaMessage Extension Methods /// <summary> /// Get the values that represent the given META event. /// The returned array consists of <ol> /// <li>the event name as a String</li> /// <li>the length of the event data as an Integer</li> /// <li>the event data as a String</li> /// </ol> /// </summary> /// <param name="mess">the META message to format</param> /// <returns>the representation of the message</returns> public static object[] GetMetaStrings(this MetaMessage mess) { // Some data is String some is a series of bytes others are neither bool dumpText = false; bool dumpBytes = false; int type = mess.GetMetaMessageType(); byte[] data = mess.GetMetaMessageData(); // The returned Object array // { type name, length, value string } object[] result = { "M:", null, "" }; result[1] = data.Length; switch (type) { case (int)MidiHelper.MetaEventType.SequenceNumber: result[0] = "M:SequenceNumber"; dumpBytes = true; break; case (int)MidiHelper.MetaEventType.TextEvent: result[0] = "M:TextEvent"; dumpText = true; break; case (int)MidiHelper.MetaEventType.CopyrightNotice: result[0] = "M:CopyrightNotice"; dumpText = true; break; case (int)MidiHelper.MetaEventType.SequenceOrTrackName: result[0] = "M:SequenceOrTrackName"; dumpText = true; break; case (int)MidiHelper.MetaEventType.InstrumentName: result[0] = "M:InstrumentName"; dumpText = true; break; case (int)MidiHelper.MetaEventType.LyricText: result[0] = "M:LyricText"; dumpText = true; break; case (int)MidiHelper.MetaEventType.MarkerText: result[0] = "M:MarkerText"; dumpText = true; break; case (int)MidiHelper.MetaEventType.CuePoint: result[0] = "M:CuePoint"; dumpText = true; break; case (int)MidiHelper.MetaEventType.ProgramName: result[0] = "M:ProgramName"; dumpText = true; break; case (int)MidiHelper.MetaEventType.DeviceName: result[0] = "M:DeviceName"; dumpText = true; break; case (int)MidiHelper.MetaEventType.SmpteOffset: result[0] = "M:SmpteOffset"; // Hour, Minute, Second, Frame, Field // hr mn se fr ff result[2] = string.Format("{0:00}:{1:00}:{2:00}:{3:00}:{4:00}", data[0] & 0x00FF, data[1] & 0x00FF, data[2] & 0x00FF, data[3] & 0x00FF, data[4] & 0x00FF); break; case (int)MidiHelper.MetaEventType.TimeSignature: result[0] = "M:TimeSignature"; int nn = (data[0] & 0x00FF); // numerator int dd = (int)(Math.Pow(2, (data[1] & 0x00FF))); // denominator int cc = (data[2] & 0x00FF); // midiClocksPerMetronomeClick int bb = (data[3] & 0x00FF); // notated 32nd-notes in a MIDI quarter-note result[2] = nn + "/" + dd + ", " + cc + " clicks per metronome, " + bb + " 32nd notes per quarter-note"; break; case (int)MidiHelper.MetaEventType.KeySignature: result[0] = "M:KeySignature"; result[2] = KeySignatures.GetKeyName(data); break; case (int)MidiHelper.MetaEventType.Tempo: result[0] = "M:Tempo"; int bpm = MicroSecsToBpm(data); result[2] = string.Format("{0}", bpm); break; case (int)MidiHelper.MetaEventType.EndOfTrack: result[0] = "M:EndOfTrack"; break; case (int)MidiHelper.MetaEventType.SequencerSpecificEvent: result[0] = "M:SequencerSpecificEvent"; dumpBytes = true; break; default: result[0] = "M:" + type; dumpBytes = true; break; } if (dumpText) { result[2] = MidiHelper.GetStringWithoutNewlines(data); } if (dumpBytes) { result[2] = MidiHelper.ByteArrayToHexString(data, ","); } return(result); }
/// <summary> /// Create a C# code line for this meta event /// </summary> /// <param name="mess">the META message to process</param> /// <param name="tick">the position of the event in the sequence</param> /// <param name="ticksPerBeat">the tick resolution of the sequence</param> /// <returns>a C# code line</returns> public static string CreateMetaEventGeneratedCode(this MetaMessage mess, long tick, int ticksPerBeat) { int type = mess.GetMetaMessageType(); string typeName = MidiHelper.GetMetaString(type); byte[] data = mess.GetMetaMessageData(); int dataLength = data.Length; string value = ""; switch (type) { // First handle the text based events (0x01 - 0x09) case (int)MidiHelper.MetaEventType.TextEvent: case (int)MidiHelper.MetaEventType.CopyrightNotice: case (int)MidiHelper.MetaEventType.SequenceOrTrackName: case (int)MidiHelper.MetaEventType.InstrumentName: case (int)MidiHelper.MetaEventType.LyricText: case (int)MidiHelper.MetaEventType.MarkerText: case (int)MidiHelper.MetaEventType.CuePoint: case (int)MidiHelper.MetaEventType.ProgramName: case (int)MidiHelper.MetaEventType.DeviceName: string text = MidiHelper.GetString(data); value = MidiHelper.TextString(text); break; // And then the special events case (int)MidiHelper.MetaEventType.SmpteOffset: // Hour, Minute, Second, Frame, Field // hr mn se fr ff value = string.Format("{0:00}:{1:00}:{2:00}:{3:00}:{4:00}", data[0] & 0x00FF, data[1] & 0x00FF, data[2] & 0x00FF, data[3] & 0x00FF, data[4] & 0x00FF); break; case (int)MidiHelper.MetaEventType.TimeSignature: int nn = (data[0] & 0x00FF); // numerator int dd = (int)(Math.Pow(2, (data[1] & 0x00FF))); // denominator int cc = (data[2] & 0x00FF); // midiClocksPerMetronomeClick int bb = (data[3] & 0x00FF); // notated 32nd-notes in a MIDI quarter-note value = nn + "/" + dd; break; case (int)MidiHelper.MetaEventType.KeySignature: value = KeySignatures.GetKeyName(data); break; case (int)MidiHelper.MetaEventType.Tempo: int bpm = MicroSecsToBpm(data); value = string.Format("{0}", bpm); break; case (int)MidiHelper.MetaEventType.EndOfTrack: break; default: value = MidiHelper.ByteArrayToString(data, " "); break; } return(string.Format("MetaEvent.CreateMetaEvent((int) MidiHelper.MetaEventType.{0}, \"{1}\", {2}, {3})", typeName, value, tick, ticksPerBeat)); }