/** Parse the given Midi file, and return an instance of this MidiFile * class. After reading the midi file, this object will contain: * - The raw list of midi events * - The Time Signature of the song * - All the tracks in the song which contain notes. * - The number, starttime, and duration of each note. */ public void parse(MidiFileReader file, string filename) { string id; int len; this.filename = filename; tracks = new List<MidiTrack>(); trackPerChannel = false; id = file.ReadAscii(4); if (id != "MThd") { throw new MidiFileException("Doesn't start with MThd", 0); } len = file.ReadInt(); if (len != 6) { throw new MidiFileException("Bad MThd header", 4); } trackmode = file.ReadShort(); int num_tracks = file.ReadShort(); quarternote = file.ReadShort(); events = new List<MidiEvent>[num_tracks]; for (int tracknum = 0; tracknum < num_tracks; tracknum++) { events[tracknum] = ReadTrack(file); MidiTrack track = new MidiTrack(events[tracknum], tracknum); if (track.Notes.Count > 0) { tracks.Add(track); } } /* Get the length of the song in pulses */ foreach (MidiTrack track in tracks) { MidiNote last = track.Notes[track.Notes.Count-1]; if (this.totalpulses < last.StartTime + last.Duration) { this.totalpulses = last.StartTime + last.Duration; } } /* If we only have one track with multiple channels, then treat * each channel as a separate track. */ if (tracks.Count == 1 && HasMultipleChannels(tracks[0])) { tracks = SplitChannels(tracks[0], events[tracks[0].Number]); trackPerChannel = true; } CheckStartTimes(tracks); /* Determine the time signature */ int tempo = 0; int numer = 0; int denom = 0; foreach (List<MidiEvent> list in events) { foreach (MidiEvent mevent in list) { if (mevent.Metaevent == MetaEventTempo && tempo == 0) { tempo = mevent.Tempo; } if (mevent.Metaevent == MetaEventTimeSignature && numer == 0) { numer = mevent.Numerator; denom = mevent.Denominator; } } } if (tempo == 0) { tempo = 500000; /* 500,000 microseconds = 0.05 sec */ } if (numer == 0) { numer = 4; denom = 4; } timesig = new TimeSignature(numer, denom, quarternote, tempo); }
public bool useDefaultInstruments; /** If true, don't change instruments */ #endregion Fields #region Constructors public MidiOptions(MidiFile midifile) { int numtracks = midifile.Tracks.Count; tracks = new bool[numtracks]; mute = new bool[numtracks]; instruments = new int[numtracks]; for (int i = 0; i < tracks.Length; i++) { tracks[i] = true; mute[i] = false; instruments[i] = midifile.Tracks[i].Instrument; if (midifile.Tracks[i].InstrumentName == "Percussion") { tracks[i] = false; } } useDefaultInstruments = true; scrollVert = true; largeNoteSize = false; if (tracks.Length == 1) { twoStaffs = true; } else { twoStaffs = false; } showNoteLetters = NoteNameNone; showLyrics = true; showMeasures = false; shifttime = 0; transpose = 0; key = -1; time = midifile.Time; colors = null; shadeColor = Color.FromArgb(210, 205, 220); shade2Color = Color.FromArgb(80, 100, 250); combineInterval = 40; tempo = midifile.Time.Tempo; pauseTime = 0; playMeasuresInLoop = false; playMeasuresInLoopStart = 0; playMeasuresInLoopEnd = midifile.EndTime() / midifile.Time.Measure; }
/** In Midi Files, time is measured in pulses. Notes that have * pulse times that are close together (like within 10 pulses) * will sound like they're the same chord. We want to draw * these notes as a single chord, it makes the sheet music much * easier to read. We don't want to draw notes that are close * together as two separate chords. * * The SymbolSpacing class only aligns notes that have exactly the same * start times. Notes with slightly different start times will * appear in separate vertical columns. This isn't what we want. * We want to align notes with approximately the same start times. * So, this function is used to assign the same starttime for notes * that are close together (timewise). */ public static void RoundStartTimes(List<MidiTrack> tracks, int millisec, TimeSignature time) { /* Get all the starttimes in all tracks, in sorted order */ List<int> starttimes = new List<int>(); foreach (MidiTrack track in tracks) { foreach (MidiNote note in track.Notes) { starttimes.Add( note.StartTime ); } } starttimes.Sort(); /* Notes within "millisec" milliseconds apart will be combined. */ int interval = time.Quarter * millisec * 1000 / time.Tempo; /* If two starttimes are within interval millisec, make them the same */ for (int i = 0; i < starttimes.Count - 1; i++) { if (starttimes[i+1] - starttimes[i] <= interval) { starttimes[i+1] = starttimes[i]; } } CheckStartTimes(tracks); /* Adjust the note starttimes, so that it matches one of the starttimes values */ foreach (MidiTrack track in tracks) { int i = 0; foreach (MidiNote note in track.Notes) { while (i < starttimes.Count && note.StartTime - interval > starttimes[i]) { i++; } if (note.StartTime > starttimes[i] && note.StartTime - starttimes[i] <= interval) { note.StartTime = starttimes[i]; } } track.Notes.Sort(track.Notes[0]); } }