Пример #1
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;
        }
Пример #2
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;
        }
Пример #3
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;
        }