示例#1
0
        /** Return a deep copy clone of this MidiTrack. */
        public MidiTrack Clone()
        {
            MidiTrack track = new MidiTrack(Number);
            track.instrument = instrument;

            foreach (MidiNote note in notes) {
            track.notes.Add( note.Clone() );
            }
            if (lyrics != null) {
            track.lyrics = new List<MidiEvent>();
            foreach (MidiEvent ev in lyrics) {
                track.lyrics.Add(ev);
            }
            }
            return track;
        }
示例#2
0
        /** 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);
        }
示例#3
0
        /* Split the given MidiTrack into two tracks, top and bottom.
         * The highest notes will go into top, the lowest into bottom.
         * This function is used to split piano songs into left-hand (bottom)
         * and right-hand (top) tracks.
         */
        public static List<MidiTrack> SplitTrack(MidiTrack track, int measurelen)
        {
            List<MidiNote> notes = track.Notes;
            int count = notes.Count;

            MidiTrack top = new MidiTrack(1);
            MidiTrack bottom = new MidiTrack(2);
            List<MidiTrack> result = new List<MidiTrack>(2);
            result.Add(top); result.Add(bottom);

            if (count == 0)
            return result;

            int prevhigh  = 76; /* E5, top of treble staff */
            int prevlow   = 45; /* A3, bottom of bass staff */
            int startindex = 0;

            foreach (MidiNote note in notes) {
            int high, low, highExact, lowExact;

            int number = note.Number;
            high = low = highExact = lowExact = number;

            while (notes[startindex].EndTime < note.StartTime) {
                startindex++;
            }

            /* I've tried several algorithms for splitting a track in two,
             * and the one below seems to work the best:
             * - If this note is more than an octave from the high/low notes
             *   (that start exactly at this start time), choose the closest one.
             * - If this note is more than an octave from the high/low notes
             *   (in this note's time duration), choose the closest one.
             * - If the high and low notes (that start exactly at this starttime)
             *   are more than an octave apart, choose the closest note.
             * - If the high and low notes (that overlap this starttime)
             *   are more than an octave apart, choose the closest note.
             * - Else, look at the previous high/low notes that were more than an
             *   octave apart.  Choose the closeset note.
             */
            FindHighLowNotes(notes, measurelen, startindex, note.StartTime, note.EndTime,
                             ref high, ref low);
            FindExactHighLowNotes(notes, startindex, note.StartTime,
                                  ref highExact, ref lowExact);

            if (highExact - number > 12 || number - lowExact > 12) {
                if (highExact - number <= number - lowExact) {
                    top.AddNote(note);
                }
                else {
                    bottom.AddNote(note);
                }
            }
            else if (high - number > 12 || number - low > 12) {
                if (high - number <= number - low) {
                    top.AddNote(note);
                }
                else {
                    bottom.AddNote(note);
                }
            }
            else if (highExact - lowExact > 12) {
                if (highExact - number <= number - lowExact) {
                    top.AddNote(note);
                }
                else {
                    bottom.AddNote(note);
                }
            }
            else if (high - low > 12) {
                if (high - number <= number - low) {
                    top.AddNote(note);
                }
                else {
                    bottom.AddNote(note);
                }
            }
            else {
                if (prevhigh - number <= number - prevlow) {
                    top.AddNote(note);
                }
                else {
                    bottom.AddNote(note);
                }
            }

            /* The prevhigh/prevlow are set to the last high/low
             * that are more than an octave apart.
             */
            if (high - low > 12) {
                prevhigh = high;
                prevlow = low;
            }
            }

            top.Notes.Sort(track.Notes[0]);
            bottom.Notes.Sort(track.Notes[0]);

            return result;
        }
示例#4
0
        /** Combine the notes in the given tracks into a single MidiTrack.
         *  The individual tracks are already sorted.  To merge them, we
         *  use a mergesort-like algorithm.
         */
        public static MidiTrack CombineToSingleTrack(List<MidiTrack> tracks)
        {
            /* Add all notes into one track */
            MidiTrack result = new MidiTrack(1);

            if (tracks.Count == 0) {
            return result;
            }
            else if (tracks.Count == 1) {
            MidiTrack track = tracks[0];
            foreach (MidiNote note in track.Notes) {
                result.AddNote(note);
            }
            return result;
            }

            int[] noteindex = new int[64];
            int[] notecount = new int[64];

            for (int tracknum = 0; tracknum < tracks.Count; tracknum++) {
            noteindex[tracknum] = 0;
            notecount[tracknum] = tracks[tracknum].Notes.Count;
            }
            MidiNote prevnote = null;
            while (true) {
            MidiNote lowestnote = null;
            int lowestTrack = -1;
            for (int tracknum = 0; tracknum < tracks.Count; tracknum++) {
                MidiTrack track = tracks[tracknum];
                if (noteindex[tracknum] >= notecount[tracknum]) {
                    continue;
                }
                MidiNote note = track.Notes[ noteindex[tracknum] ];
                if (lowestnote == null) {
                    lowestnote = note;
                    lowestTrack = tracknum;
                }
                else if (note.StartTime < lowestnote.StartTime) {
                    lowestnote = note;
                    lowestTrack = tracknum;
                }
                else if (note.StartTime == lowestnote.StartTime && note.Number < lowestnote.Number) {
                    lowestnote = note;
                    lowestTrack = tracknum;
                }
            }
            if (lowestnote == null) {
                /* We've finished the merge */
                break;
            }
            noteindex[lowestTrack]++;
            if ((prevnote != null) && (prevnote.StartTime == lowestnote.StartTime) &&
                (prevnote.Number == lowestnote.Number) ) {

                /* Don't add duplicate notes, with the same start time and number */
                if (lowestnote.Duration > prevnote.Duration) {
                    prevnote.Duration = lowestnote.Duration;
                }
            }
            else {
                result.AddNote(lowestnote);
                prevnote = lowestnote;
            }
            }

            return result;
        }
示例#5
0
        /** Split the given track into multiple tracks, separating each
         * channel into a separate track.
         */
        private static List<MidiTrack> SplitChannels(MidiTrack origtrack, List<MidiEvent> events)
        {
            /* Find the instrument used for each channel */
            int[] channelInstruments = new int[16];
            foreach (MidiEvent mevent in events) {
            if (mevent.EventFlag == EventProgramChange) {
                channelInstruments[mevent.Channel] = mevent.Instrument;
            }
            }
            channelInstruments[9] = 128; /* Channel 9 = Percussion */

            List<MidiTrack> result = new List<MidiTrack>();
            foreach (MidiNote note in origtrack.Notes) {
            bool foundchannel = false;
            foreach (MidiTrack track in result) {
                if (note.Channel == track.Notes[0].Channel) {
                    foundchannel = true;
                    track.AddNote(note);
                }
            }
            if (!foundchannel) {
                MidiTrack track = new MidiTrack(result.Count + 1);
                track.AddNote(note);
                track.Instrument = channelInstruments[note.Channel];
                result.Add(track);
            }
            }
            return result;
        }
示例#6
0
 /** Return true if this track contains multiple channels.
  * If a MidiFile contains only one track, and it has multiple channels,
  * then we treat each channel as a separate track.
  */
 static bool HasMultipleChannels(MidiTrack track)
 {
     int channel = track.Notes[0].Channel;
     foreach (MidiNote note in track.Notes) {
     if (note.Channel != channel) {
         return true;
     }
     }
     return false;
 }