private void playMidi() { int notesPlayed = 0; byte tempChannel = 0; byte tempVel = 0; MidiEvent tempEvent; while (allowPlayMidi || midiSequence.GetTracks()[0].Events.Count > notesPlayed) { tempEvent = midiSequence.GetTracks()[0].Events.GetEvent(notesPlayed); Thread.Sleep((int)tempEvent.DeltaTime); MidiPlayer.Play(tempEvent); notesPlayed++; } }
/// <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); }