/// <summary>Creates a method to create the track.</summary> /// <param name="name">The name of the track method.</param> /// <param name="track">The track to translate.</param> /// <returns>The method that creates the track.</returns> private static CodeMemberMethod CreateTrackMethod(string name, MidiTrack track) { // Create the method CodeMemberMethod method = new CodeMemberMethod(); method.Name = name; method.ReturnType = new CodeTypeReference(typeof(MidiTrack)); // MidiTrack track = new MidiTrack(); method.Statements.Add( new CodeVariableDeclarationStatement(typeof(MidiTrack), "track", new CodeObjectCreateExpression(typeof(MidiTrack), new CodeExpression[]{}))); // Add all of the events! for(int i=0; i<track.Events.Count; i++) { CodeObjectCreateExpression createEvent = CreateEvent(track.Events[i]); // track.Events.Add(new "event"()); if (createEvent != null) { method.Statements.Add( new CodeMethodInvokeExpression( new CodePropertyReferenceExpression( new CodeVariableReferenceExpression("track"), "Events"), "Add", new CodeExpression[]{createEvent})); } } // return track; method.Statements.Add(new CodeMethodReturnStatement( new CodeVariableReferenceExpression("track"))); // Return the newly created method return method; }
public void RemoveTrack(MidiTrack track) { this._tracks.Remove(track); }
/// <summary>Parses a byte array into a track's worth of events.</summary> /// <param name="data">The data to be parsed.</param> /// <returns>The track containing the parsed events.</returns> public static MidiTrack ParseToTrack(byte [] data) { long pos = 0; // current position in data bool running = false; // whether we're in running status int status = 0; // the current status byte bool sysExContinue = false; // whether we're in a multi-segment system exclusive message byte [] sysExData = null; // system exclusive data up to this point from a multi-segment message try { // Create the new track MidiTrack track = new MidiTrack(); // Process all bytes, turning them into events while (pos < data.Length) { // Read in the delta time long deltaTime = ReadVariableLength(data, ref pos); // Get the next character byte nextValue = data[pos]; // Are we continuing a sys ex? If so, the next value better be 0x7F if (sysExContinue && (nextValue != 0x7f)) { throw new MidiParserException("Expected to find a system exclusive continue byte.", pos); } // Are we in running status? Determine whether we're running and // what the current status byte is. if ((nextValue & 0x80) == 0) { // We're now in running status... if the last status was 0, uh oh! if (status == 0) { throw new MidiParserException("Status byte required for running status.", pos); } // Keep the last iteration's status byte, and now we're in running mode running = true; } else { // Not running, so store the current status byte and mark running as false status = nextValue; running = false; } // Grab the 4-bit identifier byte messageType = (byte)((status >> 4) & 0xF); MidiEvent tempEvent = null; // Handle voice events if (messageType >= 0x8 && messageType <= 0xE) { if (!running) { pos++; // if we're running, we don't advance; if we're not running, we do } byte channel = (byte)(status & 0xF); // grab the channel from the status byte tempEvent = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos); } // Handle meta events else if (status == 0xFF) { pos++; byte eventType = data[pos]; pos++; tempEvent = ParseMetaEvent(deltaTime, eventType, data, ref pos); } // Handle system exclusive events else if (status == 0xF0) { pos++; long length = ReadVariableLength(data, ref pos); // figure out how much data to read // If this is single-segment message, process the whole thing if (data[pos + length - 1] == 0xF7) { sysExData = new byte[length - 1]; Array.Copy(data, (int)pos, sysExData, 0, (int)length - 1); tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); } // It's multi-segment, so add the new data to the previously aquired data else { // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) { sysExData.CopyTo(newSysExData, 0); } Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; sysExContinue = true; } pos += length; } // Handle system exclusive continuations else if (status == 0xF7) { if (!sysExContinue) { sysExData = null; } // Figure out how much data there is pos++; long length = ReadVariableLength(data, ref pos); // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) { sysExData.CopyTo(newSysExData, 0); } Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; // Make it a system message if necessary (i.e. if we find an end marker) if (data[pos + length - 1] == 0xF7) { tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); sysExData = null; sysExContinue = false; } } // Nothing we know about else { throw new MidiParserException("Invalid status byte found.", pos); } // Add the newly parsed event if we got one if (tempEvent != null) { track.Events.Add(tempEvent); } } // Return the newly populated track return(track); } // Let MidiParserExceptions through catch (MidiParserException) { throw; } // Wrap all other exceptions in MidiParserExceptions catch (Exception exc) { throw new MidiParserException("Failed to parse MIDI file.", exc, pos); } }
public MidiTrack AddTrack() { MidiTrack track = new MidiTrack(); this.AddTrack(track); return track; }
public void AddTrack(MidiTrack track) { if (track == null) { throw new ArgumentNullException("track"); } if (this._tracks.Contains(track)) { throw new ArgumentException("This track is already part of the sequence."); } if ((this._format == 0) && (this._tracks.Count >= 1)) { throw new InvalidOperationException("Format 0 MIDI files can only have 1 track."); } this._tracks.Add(track); }
/// <summary>Removes a track that has been adding to the MIDI sequence.</summary> /// <param name="track">The track to be removed.</param> public void RemoveTrack(MidiTrack track) { // Remove the track _tracks.Remove(track); }
/// <summary>Converts a MIDI sequence from its current format to the specified 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> /// <returns>The converted sequence.</returns> /// <remarks> /// This may or may not return the same sequence as passed in. /// Regardless, the reference passed in should not be used after this call as the old /// sequence could be unusable if a different reference was returned. /// </remarks> public static MidiSequence Convert(MidiSequence sequence, int format, FormatConversionOptions options) { // Validate the parameters if (sequence == null) { throw new ArgumentNullException("sequence"); } if (format < 0 || format > 2) { throw new ArgumentOutOfRangeException("format", format, "The format must be 0, 1, or 2."); } // Handle the simple cases if (sequence.Format == format) { return(sequence); // already in requested format } if (format != 0 || sequence.NumberOfTracks == 1) // only requires change in format # { // Change the format and return the same sequence sequence.SetFormat(format); return(sequence); } // Now the hard one, converting to format 0. // We need to combine all tracks into 1. MidiSequence newSequence = new MidiSequence(format, sequence.Division); MidiTrack newTrack = newSequence.AddTrack(); // Iterate through all events in all tracks and change deltaTimes to actual times. // We'll then be able to sort based on time and change them back to deltas later foreach (MidiTrack track in sequence) { track.Events.ConvertDeltasToTotals(); } // Add all events to new track (except for end of track markers!) int trackNumber = 0; foreach (MidiTrack track in sequence) { foreach (MidiEvent midiEvent in track.Events) { // If this event has a channel, and if we're storing tracks as channels, copy to it if ((options & FormatConversionOptions.CopyTrackToChannel) > 0 && (midiEvent is VoiceMidiEvent) && trackNumber >= 0 && trackNumber <= 0xF) { ((VoiceMidiEvent)midiEvent).Channel = (byte)trackNumber; } // Add all events, except for end of track markers (we'll add our own) if (!(midiEvent is EndOfTrack)) { newTrack.Events.Add(midiEvent); } } trackNumber++; } // Sort the events newTrack.Events.SortByTime(); // Now go back through all of the events and update the times to be deltas newTrack.Events.ConvertTotalsToDeltas(); // Put an end of track on for good measure as we've already taken out // all of the ones that were in the original tracks. newTrack.Events.Add(new EndOfTrack(0)); // Return the new sequence return(newSequence); }
/// <summary> /// Creates a notechart from the specified midi path and the actual charttype /// (i.e. ExpertSingle from notes.mid). Due to the overhead necessary to /// parse a midi file. I am going to cram all midi->chart operations into /// one function call. /// This function uses the Toub midi parser which is much faster than Sanford, /// but will throw an exception on certian midi files. /// </summary> /// <param name="chartSelection"> /// The information on which particular notechart to use. /// </param> /// <param name="chartInfo">The metadata on the chart.</param> /// <param name="BPMChanges">The list of BPM changes for this chart.</param> /// <returns> /// A filled out Notechart containing the needed information from the *.mid file. /// </returns> public static Notes ParseMidiInformationToub(ChartSelection chartSelection, Info chartInfo, List<BPMChange> BPMChanges) { Notes notechartToReturn = new Notes(); notechartToReturn.instrument = chartSelection.instrument; notechartToReturn.difficulty = chartSelection.difficulty; // The following two switch's are used to get the proper midi terminology for // the selected track and difficulty. string instrumentPart = null; string greenKey = null; string redKey = null; string yellowKey = null; string blueKey = null; string orangeKey = null; switch (chartSelection.instrument) { case "Single": instrumentPart = "PART GUITAR"; break; case "DoubleGuitar": instrumentPart = "PART GUITAR COOP"; break; case "DoubleBass": instrumentPart = "PART BASS"; break; case "Drums": instrumentPart = "PART DRUMS"; break; default: instrumentPart = "PART GUITAR"; break; } switch (chartSelection.difficulty) { case "Expert": greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; case "Hard": greenKey = "C7"; redKey = "C#7"; yellowKey = "D7"; blueKey = "D#7"; orangeKey = "E7"; break; case "Medium": greenKey = "C6"; redKey = "C#6"; yellowKey = "D6"; blueKey = "D#6"; orangeKey = "E6"; break; case "Easy": greenKey = "C5"; redKey = "C#5"; yellowKey = "D5"; blueKey = "D#5"; orangeKey = "E5"; break; default: greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; } MidiSequence mySequence = MidiSequence.Import(chartSelection.directory + "\\notes.mid"); MidiTrack[] myTracks = mySequence.GetTracks(); chartInfo.resolution = mySequence.Division; MidiTrack trackToUse = new MidiTrack(); uint totalTickValue = 0; // Go through each event in the first track (which contains the BPM changes) // and parse the resulting string. for (int i = 0; i < myTracks[0].Events.Count; i++) { Toub.Sound.Midi.MidiEvent currEvent = myTracks[0].Events[i]; string eventString = currEvent.ToString(); string[] splitEventString = eventString.Split('\t'); // Since ticks are stored relative to each other (e.g. 300 ticks // until next note), we must maintain the total tick amout. totalTickValue += Convert.ToUInt32(splitEventString[1]); if (splitEventString[0] == "Tempo") { // In midi files, bpm chages are stored as "microseconds per quarter note" // and must be converted to BPM, and then into the non decimal format the game // uses. double currBPMDouble = 60000000 / Convert.ToDouble(splitEventString[3]); uint BPMToAdd = (uint)(currBPMDouble * 1000); BPMChanges.Add(new BPMChange(totalTickValue, BPMToAdd)); } } trackToUse = new MidiTrack(); // Find the specified instrument's track foreach (MidiTrack currTrack in myTracks) { string trackHeader = currTrack.Events[0].ToString(); string[] splitHeader = trackHeader.Split('\t'); // -If we come across a "T1 GEMS" track, we're in GH1 territory. // -GH2/FoF has both PART BASS and PART RHYTHM (one or the other depending // on the chart). if (((splitHeader[3] == instrumentPart) || (splitHeader[3] == "T1 GEMS")) || ((splitHeader[3] == "PART RHYTHM") && (instrumentPart == "PART BASS"))) { trackToUse = currTrack; } } totalTickValue = 0; uint currTickValue = 0; Note currNote = new Note(); bool blankNote = true; // Scan through and record every note specific to the selected difficulty for (int i = 0; i < trackToUse.Events.Count; i++) { string currEvent = trackToUse.Events[i].ToString(); string[] splitEvent = currEvent.Split('\t'); currTickValue = Convert.ToUInt32(splitEvent[1]); totalTickValue += currTickValue; // We need to specify wether a note is blank or not so we don't add // blank notes from other difficulties into the chart, but if we have // a filled out note, any nonzero tick value means we are moving to a // new note, so we must cut our ties and add this note to the chart. if ((currTickValue != 0) && !blankNote) { notechartToReturn.notes.Add(currNote); currNote = new Note(); blankNote = true; } // The "0x64" I think means "not was hit." There is another // set of notes that use "0x00" that all appear slightly after // the "0x64" notes. if ((splitEvent[0] == "NoteOn") && (splitEvent[4] != "0x00")) { // Only consider notes within the octave our difficulty is in. if ((splitEvent[3] == greenKey) || (splitEvent[3] == redKey) || (splitEvent[3] == yellowKey) || (splitEvent[3] == blueKey) || (splitEvent[3] == orangeKey)) { // If it's a new note, we need to setup the tick value of it. if (blankNote) { currNote.tickValue = totalTickValue; blankNote = false; } if (splitEvent[3] == greenKey) { currNote.addNote(0); } else if (splitEvent[3] == redKey) { currNote.addNote(1); } else if (splitEvent[3] == yellowKey) { currNote.addNote(2); } else if (splitEvent[3] == blueKey) { currNote.addNote(3); } else if (splitEvent[3] == orangeKey) { currNote.addNote(4); } } } } return notechartToReturn; }
public static void Play(MidiTrack track, int division) { MidiSequence sequence = new MidiSequence(0, division); sequence.AddTrack(track); Play(sequence); }
/// <summary>Parses a byte array into a track's worth of events.</summary> /// <param name="data">The data to be parsed.</param> /// <returns>The track containing the parsed events.</returns> public static MidiTrack ParseToTrack(byte [] data) { long pos = 0; // current position in data bool running = false; // whether we're in running status int status = 0; // the current status byte bool sysExContinue = false; // whether we're in a multi-segment system exclusive message byte [] sysExData = null; // system exclusive data up to this point from a multi-segment message try { // Create the new track MidiTrack track = new MidiTrack(); // Process all bytes, turning them into events while(pos < data.Length) { // Read in the delta time long deltaTime = ReadVariableLength(data, ref pos); // Get the next character byte nextValue = data[pos]; // Are we continuing a sys ex? If so, the next value better be 0x7F if (sysExContinue && (nextValue != 0x7f)) throw new MidiParserException("Expected to find a system exclusive continue byte.", pos); // Are we in running status? Determine whether we're running and // what the current status byte is. if ((nextValue & 0x80) == 0) { // We're now in running status... if the last status was 0, uh oh! if (status == 0) throw new MidiParserException("Status byte required for running status.", pos); // Keep the last iteration's status byte, and now we're in running mode running = true; } else { // Not running, so store the current status byte and mark running as false status = nextValue; running = false; } // Grab the 4-bit identifier byte messageType = (byte)((status >> 4) & 0xF); MidiEvent tempEvent = null; // Handle voice events if (messageType >= 0x8 && messageType <= 0xE) { if (!running) pos++; // if we're running, we don't advance; if we're not running, we do byte channel = (byte)(status & 0xF); // grab the channel from the status byte tempEvent = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos); } // Handle meta events else if (status == 0xFF) { pos++; byte eventType = data[pos]; pos++; tempEvent = ParseMetaEvent(deltaTime, eventType, data, ref pos); } // Handle system exclusive events else if (status == 0xF0) { pos++; long length = ReadVariableLength(data, ref pos); // figure out how much data to read // If this is single-segment message, process the whole thing if (data[pos+length-1] == 0xF7) { sysExData = new byte[length-1]; Array.Copy(data, (int)pos, sysExData, 0, (int)length-1); tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); } // It's multi-segment, so add the new data to the previously aquired data else { // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) sysExData.CopyTo(newSysExData, 0); Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; sysExContinue = true; } pos += length; } // Handle system exclusive continuations else if (status == 0xF7) { if (!sysExContinue) sysExData = null; // Figure out how much data there is pos++; long length = ReadVariableLength(data, ref pos); // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) sysExData.CopyTo(newSysExData, 0); Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; // Make it a system message if necessary (i.e. if we find an end marker) if (data[pos+length-1] == 0xF7) { tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); sysExData = null; sysExContinue = false; } } // Nothing we know about else throw new MidiParserException("Invalid status byte found.", pos); // Add the newly parsed event if we got one if (tempEvent != null) track.Events.Add(tempEvent); } // Return the newly populated track return track; } // Let MidiParserExceptions through catch(MidiParserException) { throw; } // Wrap all other exceptions in MidiParserExceptions catch(Exception exc) { throw new MidiParserException("Failed to parse MIDI file.", exc, pos); } }
/// <summary>Adds a track to the MIDI sequence.</summary> /// <param name="track">The complete track to be added.</param> public void AddTrack(MidiTrack track) { // Make sure the track is valid and that is hasn't already been added. if (track == null) throw new ArgumentNullException("track"); if (_tracks.Contains(track)) throw new ArgumentException("This track is already part of the sequence."); // If this is format 0, we can only have 1 track if (_format == 0 && _tracks.Count >= 1) throw new InvalidOperationException("Format 0 MIDI files can only have 1 track."); // Add the track. _tracks.Add(track); }
/// <summary>Adds a track to the MIDI sequence.</summary> /// <returns>The new track as added to the sequence. Modifications made to the track will be reflected.</returns> public MidiTrack AddTrack() { // Create a new track, add it, and return it MidiTrack track = new MidiTrack(); AddTrack(track); return track; }
/// <summary>Plays an individual MIDI track.</summary> /// <param name="track">The track to be played.</param> /// <param name="division">The MIDI division to use for playing the track.</param> public static void Play(MidiTrack track, int division) { // Wrap the track in a sequence and play it MidiSequence tempSequence = new MidiSequence(0, division); tempSequence.AddTrack(track); Play(tempSequence); }
private static CodeMemberMethod CreateTrackMethod(string name, MidiTrack track) { CodeMemberMethod method = new CodeMemberMethod { Name = name, ReturnType = new CodeTypeReference(typeof(MidiTrack)) }; CodeExpression[] parameters = new CodeExpression[0]; method.Statements.Add(new CodeVariableDeclarationStatement(typeof(MidiTrack), "track", new CodeObjectCreateExpression(typeof(MidiTrack), parameters))); for (int i = 0; i < track.Events.Count; i++) { CodeObjectCreateExpression expression = CreateEvent(track.Events[i]); if (expression != null) { method.Statements.Add(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("track"), "Events"), "Add", new CodeExpression[] { expression })); } } method.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("track"))); return method; }
public static MidiTrack ParseToTrack(byte[] data) { MidiTrack track2; long pos = 0L; bool flag = false; int num2 = 0; bool flag2 = false; byte[] destinationArray = null; try { MidiTrack track = new MidiTrack(); while (pos < data.Length) { long deltaTime = ReadVariableLength(data, ref pos); byte num4 = data[(int) ((IntPtr) pos)]; if (flag2 && (num4 != 0x7f)) { throw new MidiParserException("Expected to find a system exclusive continue byte.", pos); } if ((num4 & 0x80) == 0) { if (num2 == 0) { throw new MidiParserException("Status byte required for running status.", pos); } flag = true; } else { num2 = num4; flag = false; } byte messageType = (byte) ((num2 >> 4) & 15); MidiEvent message = null; if ((messageType >= 8) && (messageType <= 14)) { if (!flag) { pos += 1L; } byte channel = (byte) (num2 & 15); message = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos); } else if (num2 == 0xff) { pos += 1L; byte eventType = data[(int) ((IntPtr) pos)]; pos += 1L; message = ParseMetaEvent(deltaTime, eventType, data, ref pos); } else if (num2 == 240) { pos += 1L; long num8 = ReadVariableLength(data, ref pos); if (data[(int) ((IntPtr) ((pos + num8) - 1L))] == 0xf7) { destinationArray = new byte[num8 - 1L]; Array.Copy(data, (int) pos, destinationArray, 0, ((int) num8) - 1); message = new SystemExclusiveMidiEvent(deltaTime, destinationArray); } else { int destinationIndex = (destinationArray == null) ? 0 : destinationArray.Length; byte[] array = new byte[destinationIndex + num8]; if (destinationArray != null) { destinationArray.CopyTo(array, 0); } Array.Copy(data, (int) pos, array, destinationIndex, (int) num8); destinationArray = array; flag2 = true; } pos += num8; } else { if (num2 != 0xf7) { throw new MidiParserException("Invalid status byte found.", pos); } if (!flag2) { destinationArray = null; } pos += 1L; long num10 = ReadVariableLength(data, ref pos); int num11 = (destinationArray == null) ? 0 : destinationArray.Length; byte[] buffer3 = new byte[num11 + num10]; if (destinationArray != null) { destinationArray.CopyTo(buffer3, 0); } Array.Copy(data, (int) pos, buffer3, num11, (int) num10); destinationArray = buffer3; if (data[(int) ((IntPtr) ((pos + num10) - 1L))] == 0xf7) { message = new SystemExclusiveMidiEvent(deltaTime, destinationArray); destinationArray = null; flag2 = false; } } if (message != null) { track.Events.Add(message); } } track2 = track; } catch (MidiParserException) { throw; } catch (Exception exception) { throw new MidiParserException("Failed to parse MIDI file.", exception, pos); } return track2; }