Пример #1
0
 public void TestNotesOverlap()
 {
     SheetMusic.SetNoteSize(false);
     KeySignature key = new KeySignature(0, 0);
     int quarter = 400;
     TimeSignature time = new TimeSignature(4, 4, quarter, 60000);
     int num1 = WhiteNote.BottomTreble.Number();
     int num2 = num1 + 1;
     MidiNote note1 = new MidiNote(0, 0, num1, quarter);
     MidiNote note2 = new MidiNote(0, 0, num2, quarter);
     List<MidiNote> notes = new List<MidiNote>(2);
     notes.Add(note1);
     notes.Add(note2);
     ChordSymbol chord = new ChordSymbol(notes, key, time, Clef.Treble, null);
     Assert.AreEqual(chord.ToString(),
                     "ChordSymbol clef=Treble start=0 end=400 width=25 hastwostems=False AccidSymbol accid=Sharp whitenote=F4 clef=Treble width=9 Note whitenote=F4 duration=Quarter leftside=True Note whitenote=F4 duration=Quarter leftside=True Stem duration=Quarter direction=1 top=F4 bottom=F4 end=E5 overlap=False side=2 width_to_pair=0 receiver_in_pair=False ");
 }
Пример #2
0
    public void TestRoundStartTimes()
    {
        const byte notenum = 20;

        List<MidiTrack> tracks = new List<MidiTrack>();
        MidiTrack track1 = new MidiTrack(0);
        track1.AddNote(new MidiNote(0, 0, notenum, 60));
        track1.AddNote(new MidiNote(3, 0, notenum+1, 60));
        track1.AddNote(new MidiNote(15, 0, notenum+2, 60));
        track1.AddNote(new MidiNote(22, 0, notenum+3, 60));
        track1.AddNote(new MidiNote(62, 0, notenum+4, 60));

        MidiTrack track2 = new MidiTrack(1);
        track2.AddNote(new MidiNote(2, 0, notenum+10, 60));
        track2.AddNote(new MidiNote(10, 0, notenum+11, 60));
        track2.AddNote(new MidiNote(20, 0, notenum+12, 60));
        track2.AddNote(new MidiNote(35, 0, notenum+13, 60));
        track2.AddNote(new MidiNote(36, 0, notenum+14, 60));

        tracks.Add(track1);
        tracks.Add(track2);

        int quarter = 130;
        int tempo = 500000;
        TimeSignature time = new TimeSignature(4, 4, quarter, tempo);

        /* quarternote * 60,000 / 500,000 = 15 pulses
         * So notes within 15 pulses should be grouped together.
         * 0, 2, 3, 10, 15 are grouped to starttime 0
         * 20, 22, 35      are grouped to starttime 20
         * 36              is still 36
         * 62              is still 62
         */
        MidiFile.RoundStartTimes(tracks, 60, time);
        List<MidiNote> notes1 = tracks[0].Notes;
        List<MidiNote> notes2 = tracks[1].Notes;
        Assert.AreEqual(notes1.Count, 5);
        Assert.AreEqual(notes2.Count, 5);

        Assert.AreEqual(notes1[0].Number, notenum);
        Assert.AreEqual(notes1[1].Number, notenum+1);
        Assert.AreEqual(notes1[2].Number, notenum+2);
        Assert.AreEqual(notes1[3].Number, notenum+3);
        Assert.AreEqual(notes1[4].Number, notenum+4);

        Assert.AreEqual(notes2[0].Number, notenum+10);
        Assert.AreEqual(notes2[1].Number, notenum+11);
        Assert.AreEqual(notes2[2].Number, notenum+12);
        Assert.AreEqual(notes2[3].Number, notenum+13);
        Assert.AreEqual(notes2[4].Number, notenum+14);

        Assert.AreEqual(notes1[0].StartTime, 0);
        Assert.AreEqual(notes1[1].StartTime, 0);
        Assert.AreEqual(notes1[2].StartTime, 0);
        Assert.AreEqual(notes1[3].StartTime, 20);
        Assert.AreEqual(notes1[3].StartTime, 20);
        Assert.AreEqual(notes1[4].StartTime, 62);

        Assert.AreEqual(notes2[0].StartTime, 0);
        Assert.AreEqual(notes2[1].StartTime, 0);
        Assert.AreEqual(notes2[2].StartTime, 20);
        Assert.AreEqual(notes2[3].StartTime, 20);
        Assert.AreEqual(notes2[4].StartTime, 36);
    }
Пример #3
0
 public void TestSixteenthDuration()
 {
     SheetMusic.SetNoteSize(false);
     KeySignature key = new KeySignature(0, 0);
     int quarter = 400;
     TimeSignature time = new TimeSignature(4, 4, quarter, 60000);
     int num1 = WhiteNote.BottomTreble.Number();
     MidiNote note1 = new MidiNote(0, 0, num1, quarter/4);
     List<MidiNote> notes = new List<MidiNote>(2);
     notes.Add(note1);
     ChordSymbol chord = new ChordSymbol(notes, key, time, Clef.Treble, null);
     Assert.AreEqual(chord.ToString(),
                     "ChordSymbol clef=Treble start=0 end=100 width=16 hastwostems=False Note whitenote=F4 duration=Sixteenth leftside=True Stem duration=Sixteenth direction=1 top=F4 bottom=F4 end=G5 overlap=False side=2 width_to_pair=0 receiver_in_pair=False ");
     Assert.AreEqual(chord.AboveStaff, SheetMusic.NoteHeight);
 }
Пример #4
0
        bool CanCreateBeam(ChordSymbol[] chords, TimeSignature time, bool startQuarter)
        {
            int  numChords = chords.Length;
            Stem firstStem = chords[0].Stem;
            Stem lastStem  = chords[chords.Length - 1].Stem;

            if (firstStem == null || lastStem == null)
            {
                return(false);
            }
            int          measure = chords[0].StartTime / time.Measure;
            NoteDuration dur     = firstStem.Duration;
            NoteDuration dur2    = lastStem.Duration;

            bool dotted8_to_16 = false;

            if (chords.Length == 2 && dur == NoteDuration.DottedEighth &&
                dur2 == NoteDuration.Sixteenth)
            {
                dotted8_to_16 = true;
            }

            if (dur == NoteDuration.Whole || dur == NoteDuration.Half ||
                dur == NoteDuration.DottedHalf || dur == NoteDuration.Quarter ||
                dur == NoteDuration.DottedQuarter ||
                (dur == NoteDuration.DottedEighth && !dotted8_to_16))
            {
                return(false);
            }

            if (numChords == 6)
            {
                if (dur != NoteDuration.Eighth)
                {
                    return(false);
                }
                bool correctTime =
                    ((time.Numerator == 3 && time.Denominator == 4) ||
                     (time.Numerator == 6 && time.Denominator == 8) ||
                     (time.Numerator == 6 && time.Denominator == 4));

                if (!correctTime)
                {
                    return(false);
                }

                if (time.Numerator == 6 && time.Denominator == 4)
                {
                    /* first chord must start at 1st or 4th quarter note */
                    int beat = time.Quarter * 3;
                    if ((chords[0].StartTime % beat) > time.Quarter / 6)
                    {
                        return(false);
                    }
                }
            }
            else if (numChords == 4)
            {
                if (time.Numerator == 3 && time.Denominator == 8)
                {
                    return(false);
                }
                bool correctTime =
                    (time.Numerator == 2 || time.Numerator == 4 || time.Numerator == 8);
                if (!correctTime && dur != NoteDuration.Sixteenth)
                {
                    return(false);
                }

                /* chord must start on quarter note */
                int beat = time.Quarter;
                if (dur == NoteDuration.Eighth)
                {
                    /* 8th note chord must start on 1st or 3rd quarter note */
                    beat = time.Quarter * 2;
                }
                else if (dur == NoteDuration.ThirtySecond)
                {
                    /* 32nd note must start on an 8th beat */
                    beat = time.Quarter / 2;
                }

                if ((chords[0].StartTime % beat) > time.Quarter / 6)
                {
                    return(false);
                }
            }
            else if (numChords == 3)
            {
                bool valid = (dur == NoteDuration.Triplet) ||
                             (dur == NoteDuration.Eighth &&
                              time.Numerator == 12 && time.Denominator == 8);
                if (!valid)
                {
                    return(false);
                }

                /* chord must start on quarter note */
                int beat = time.Quarter;
                if (time.Numerator == 12 && time.Denominator == 8)
                {
                    /* In 12/8 time, chord must start on 3*8th beat */
                    beat = time.Quarter / 2 * 3;
                }
                if ((chords[0].StartTime % beat) > time.Quarter / 6)
                {
                    return(false);
                }
            }

            else if (numChords == 2)
            {
                if (startQuarter)
                {
                    int beat = time.Quarter;
                    if ((chords[0].StartTime % beat) > time.Quarter / 6)
                    {
                        return(false);
                    }
                }
            }

            foreach (ChordSymbol chord in chords)
            {
                if ((chord.StartTime / time.Measure) != measure)
                {
                    return(false);
                }
                if (chord.Stem == null)
                {
                    return(false);
                }
                if (chord.Stem.Duration != dur && !dotted8_to_16)
                {
                    return(false);
                }
                if (chord.Stem.isBeam)
                {
                    return(false);
                }
            }

            /* Check that all stems can point in same direction */
            bool hasTwoStems = false;
            int  direction   = Stem.Up;

            foreach (ChordSymbol chord in chords)
            {
                if (chord.HasTwoStems)
                {
                    if (hasTwoStems && chord.Stem.Direction != direction)
                    {
                        return(false);
                    }
                    hasTwoStems = true;
                    direction   = chord.Stem.Direction;
                }
            }

            /* Get the final stem direction */
            if (!hasTwoStems)
            {
                WhiteNote note1;
                WhiteNote note2;
                note1     = (firstStem.Direction == Stem.Up ? firstStem.Top : firstStem.Bottom);
                note2     = (lastStem.Direction == Stem.Up ? lastStem.Top : lastStem.Bottom);
                direction = StemDirection(note1, note2, chords[0].Clef);
            }

            /* If the notes are too far apart, don't use a beam */
            if (direction == Stem.Up)
            {
                if (Math.Abs(firstStem.Top.Dist(lastStem.Top)) >= 11)
                {
                    return(false);
                }
            }
            else
            {
                if (Math.Abs(firstStem.Bottom.Dist(lastStem.Bottom)) >= 11)
                {
                    return(false);
                }
            }
            return(true);
        }
Пример #5
0
        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;
        }
Пример #6
0
        private int width; /** The width of the chord */

        #endregion Fields

        #region Constructors

        /** Create a new Chord Symbol from the given list of midi notes.
         * All the midi notes will have the same start time.  Use the
         * key signature to get the white key and accidental symbol for
         * each note.  Use the time signature to calculate the duration
         * of the notes. Use the clef when drawing the chord.
         */
        public ChordSymbol(List<MidiNote> midinotes, KeySignature key, 
                       TimeSignature time, Clef c, SheetMusic sheet)
        {
            int len = midinotes.Count;
            int i;

            hastwostems = false;
            clef = c;
            sheetmusic = sheet;

            starttime = midinotes[0].StartTime;
            endtime = midinotes[0].EndTime;

            for (i = 0; i < midinotes.Count; i++) {
            if (i > 1) {
                if (midinotes[i].Number < midinotes[i-1].Number) {
                    throw new System.ArgumentException("Chord notes not in increasing order by number");
                }
            }
            endtime = Math.Max(endtime, midinotes[i].EndTime);
            }

            notedata = CreateNoteData(midinotes, key, time);
            accidsymbols = CreateAccidSymbols(notedata, clef);

            /* Find out how many stems we need (1 or 2) */
            NoteDuration dur1 = notedata[0].duration;
            NoteDuration dur2 = dur1;
            int change = -1;
            for (i = 0; i < notedata.Length; i++) {
            dur2 = notedata[i].duration;
            if (dur1 != dur2) {
                change = i;
                break;
            }
            }

            if (dur1 != dur2) {
            /* We have notes with different durations.  So we will need
             * two stems.  The first stem points down, and contains the
             * bottom note up to the note with the different duration.
             *
             * The second stem points up, and contains the note with the
             * different duration up to the top note.
             */
            hastwostems = true;
            stem1 = new Stem(notedata[0].whitenote,
                             notedata[change-1].whitenote,
                             dur1,
                             Stem.Down,
                             NotesOverlap(notedata, 0, change)
                            );

            stem2 = new Stem(notedata[change].whitenote,
                             notedata[notedata.Length-1].whitenote,
                             dur2,
                             Stem.Up,
                             NotesOverlap(notedata, change, notedata.Length)
                            );
            }
            else {
            /* All notes have the same duration, so we only need one stem. */
            int direction = StemDirection(notedata[0].whitenote,
                                          notedata[notedata.Length-1].whitenote,
                                          clef);

            stem1 = new Stem(notedata[0].whitenote,
                             notedata[notedata.Length-1].whitenote,
                             dur1,
                             direction,
                             NotesOverlap(notedata, 0, notedata.Length)
                            );
            stem2 = null;
            }

            /* For whole notes, no stem is drawn. */
            if (dur1 == NoteDuration.Whole)
            stem1 = null;
            if (dur2 == NoteDuration.Whole)
            stem2 = null;

            width = MinWidth;
        }
Пример #7
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);
        }
Пример #8
0
        /** Add in the vertical bars delimiting measures.
         *  Also, add the time signature symbols.
         */
        private List<MusicSymbol> AddBars(List<ChordSymbol> chords, TimeSignature time,
                              int lastStart)
        {
            List<MusicSymbol> symbols = new List<MusicSymbol>();

            TimeSigSymbol timesig = new TimeSigSymbol(time.Numerator, time.Denominator);
            symbols.Add(timesig);

            /* The starttime of the beginning of the measure */
            int measuretime = 0;

            int i = 0;
            while (i < chords.Count) {
            if (measuretime <= chords[i].StartTime) {
                symbols.Add(new BarSymbol(measuretime) );
                measuretime += time.Measure;
            }
            else {
                symbols.Add(chords[i]);
                i++;
            }
            }

            /* Keep adding bars until the last StartTime (the end of the song) */
            while (measuretime < lastStart) {
            symbols.Add(new BarSymbol(measuretime) );
            measuretime += time.Measure;
            }

            /* Add the final vertical bar to the last measure */
            symbols.Add(new BarSymbol(measuretime) );
            return symbols;
        }
Пример #9
0
        /** Connect chords of the same duration with a horizontal beam.
         *  numChords is the number of chords per beam (2, 3, 4, or 6).
         *  if startBeat is true, the first chord must start on a quarter note beat.
         */
        private static void CreateBeamedChords(List<MusicSymbol>[] allsymbols, TimeSignature time,
                       int numChords, bool startBeat)
        {
            int[] chordIndexes = new int[numChords];
            ChordSymbol[] chords = new ChordSymbol[numChords];

            foreach (List<MusicSymbol> symbols in allsymbols) {
            int startIndex = 0;
            while (true) {
                int horizDistance = 0;
                bool found = FindConsecutiveChords(symbols, time,
                                                   startIndex,
                                                   chordIndexes,
                                                   ref horizDistance);
                if (!found) {
                    break;
                }
                for (int i = 0; i < numChords; i++) {
                    chords[i] = (ChordSymbol)symbols[ chordIndexes[i] ];
                }

                if (ChordSymbol.CanCreateBeam(chords, time, startBeat)) {
                    ChordSymbol.CreateBeam(chords, horizDistance);
                    startIndex = chordIndexes[numChords-1] + 1;
                }
                else {
                    startIndex = chordIndexes[0] + 1;
                }

                /* What is the value of startIndex here?
                 * If we created a beam, we start after the last chord.
                 * If we failed to create a beam, we start after the first chord.
                 */
            }
            }
        }
Пример #10
0
        /** Find 2, 3, 4, or 6 chord symbols that occur consecutively (without any
         *  rests or bars in between).  There can be BlankSymbols in between.
         *
         *  The startIndex is the index in the symbols to start looking from.
         *
         *  Store the indexes of the consecutive chords in chordIndexes.
         *  Store the horizontal distance (pixels) between the first and last chord.
         *  If we failed to find consecutive chords, return false.
         */
        private static bool FindConsecutiveChords(List<MusicSymbol> symbols, TimeSignature time,
                          int startIndex, int[] chordIndexes, 
                          ref int horizDistance)
        {
            int i = startIndex;
            int numChords = chordIndexes.Length;

            while (true) {
            horizDistance = 0;

            /* Find the starting chord */
            while (i < symbols.Count - numChords) {
                if (symbols[i] is ChordSymbol) {
                    ChordSymbol c = (ChordSymbol) symbols[i];
                    if (c.Stem != null) {
                        break;
                    }
                }
                i++;
            }
            if (i >= symbols.Count - numChords) {
                chordIndexes[0] = -1;
                return false;
            }
            chordIndexes[0] = i;
            bool foundChords = true;
            for (int chordIndex = 1; chordIndex < numChords; chordIndex++) {
                i++;
                int remaining = numChords - 1 - chordIndex;
                while ((i < symbols.Count - remaining) && (symbols[i] is BlankSymbol)) {
                    horizDistance += symbols[i].Width;
                    i++;
                }
                if (i >= symbols.Count - remaining) {
                    return false;
                }
                if (!(symbols[i] is ChordSymbol)) {
                    foundChords = false;
                    break;
                }
                chordIndexes[chordIndex] = i;
                horizDistance += symbols[i].Width;
            }
            if (foundChords) {
                return true;
            }

            /* Else, start searching again from index i */
            }
        }
Пример #11
0
        /** Connect chords of the same duration with a horizontal beam.
         *
         *  We create beams in the following order:
         *  - 6 connected 8th note chords, in 3/4, 6/8, or 6/4 time
         *  - Triplets that start on quarter note beats
         *  - 3 connected chords that start on quarter note beats (12/8 time only)
         *  - 4 connected chords that start on quarter note beats (4/4 or 2/4 time only)
         *  - 2 connected chords that start on quarter note beats
         *  - 2 connected chords that start on any beat
         */
        private static void CreateAllBeamedChords(List<MusicSymbol>[] allsymbols, TimeSignature time)
        {
            if ((time.Numerator == 3 && time.Denominator == 4) ||
            (time.Numerator == 6 && time.Denominator == 8) ||
            (time.Numerator == 6 && time.Denominator == 4) ) {

            CreateBeamedChords(allsymbols, time, 6, true);
            }
            CreateBeamedChords(allsymbols, time, 3, true);
            CreateBeamedChords(allsymbols, time, 4, true);
            CreateBeamedChords(allsymbols, time, 2, true);
            CreateBeamedChords(allsymbols, time, 2, false);
        }
Пример #12
0
        /** Return the rest symbols needed to fill the time interval between
         * start and end.  If no rests are needed, return nil.
         */
        private RestSymbol[] GetRests(TimeSignature time, int start, int end)
        {
            RestSymbol[] result;
            RestSymbol r1, r2;

            if (end - start < 0)
            return null;

            NoteDuration dur = time.GetNoteDuration(end - start);
            switch (dur) {
            case NoteDuration.Whole:
            case NoteDuration.Half:
            case NoteDuration.Quarter:
            case NoteDuration.Eighth:
                r1 = new RestSymbol(start, dur);
                result = new RestSymbol[]{ r1 };
                return result;

            case NoteDuration.DottedHalf:
                r1 = new RestSymbol(start, NoteDuration.Half);
                r2 = new RestSymbol(start + time.Quarter*2,
                                    NoteDuration.Quarter);
                result = new RestSymbol[]{ r1, r2 };
                return result;

            case NoteDuration.DottedQuarter:
                r1 = new RestSymbol(start, NoteDuration.Quarter);
                r2 = new RestSymbol(start + time.Quarter,
                                    NoteDuration.Eighth);
                result = new RestSymbol[]{ r1, r2 };
                return result;

            case NoteDuration.DottedEighth:
                r1 = new RestSymbol(start, NoteDuration.Eighth);
                r2 = new RestSymbol(start + time.Quarter/2,
                                    NoteDuration.Sixteenth);
                result = new RestSymbol[]{ r1, r2 };
                return result;

            default:
                return null;
            }
        }
Пример #13
0
        /** Given the chord symbols for a track, create a new symbol list
         * that contains the chord symbols, vertical bars, rests, and clef changes.
         * Return a list of symbols (ChordSymbol, BarSymbol, RestSymbol, ClefSymbol)
         */
        private List<MusicSymbol> CreateSymbols(List<ChordSymbol> chords, ClefMeasures clefs,
                  TimeSignature time, int lastStart)
        {
            List<MusicSymbol> symbols = new List<MusicSymbol>();
            symbols = AddBars(chords, time, lastStart);
            symbols = AddRests(symbols, time);
            symbols = AddClefChanges(symbols, clefs, time);

            return symbols;
        }
Пример #14
0
 public void TestStemUpBass()
 {
     SheetMusic.SetNoteSize(false);
     KeySignature key = new KeySignature(0, 0);
     int quarter = 400;
     TimeSignature time = new TimeSignature(4, 4, quarter, 60000);
     int num1 = WhiteNote.BottomBass.Number();
     int num2 = num1 + 2;
     MidiNote note1 = new MidiNote(0, 0, num1, quarter);
     MidiNote note2 = new MidiNote(0, 0, num2, quarter);
     List<MidiNote> notes = new List<MidiNote>(2);
     notes.Add(note1);
     notes.Add(note2);
     ChordSymbol chord = new ChordSymbol(notes, key, time, Clef.Bass, null);
     Assert.AreEqual(chord.ToString(),
                     "ChordSymbol clef=Bass start=0 end=400 width=16 hastwostems=False Note whitenote=A3 duration=Quarter leftside=True Note whitenote=B3 duration=Quarter leftside=False Stem duration=Quarter direction=1 top=B3 bottom=A3 end=A4 overlap=True side=2 width_to_pair=0 receiver_in_pair=False ");
 }
Пример #15
0
 /** The current clef is always shown at the beginning of the staff, on
  * the left side.  However, the clef can also change from measure to
  * measure. When it does, a Clef symbol must be shown to indicate the
  * change in clef.  This function adds these Clef change symbols.
  * This function does not add the main Clef Symbol that begins each
  * staff.  That is done in the Staff() contructor.
  */
 private List<MusicSymbol> AddClefChanges(List<MusicSymbol> symbols,
                              ClefMeasures clefs,
                              TimeSignature time)
 {
     List<MusicSymbol> result = new List<MusicSymbol>( symbols.Count );
     Clef prevclef = clefs.GetClef(0);
     foreach (MusicSymbol symbol in symbols) {
     /* A BarSymbol indicates a new measure */
     if (symbol is BarSymbol) {
         Clef clef = clefs.GetClef(symbol.StartTime);
         if (clef != prevclef) {
             result.Add(new ClefSymbol(clef, symbol.StartTime-1, true));
         }
         prevclef = clef;
     }
     result.Add(symbol);
     }
     return result;
 }
Пример #16
0
 public void TestWholeDuration()
 {
     SheetMusic.SetNoteSize(false);
     KeySignature key = new KeySignature(0, 0);
     int quarter = 400;
     TimeSignature time = new TimeSignature(4, 4, quarter, 60000);
     int num1 = WhiteNote.BottomTreble.Number();
     MidiNote note1 = new MidiNote(0, 0, num1, quarter*4);
     List<MidiNote> notes = new List<MidiNote>(2);
     notes.Add(note1);
     ChordSymbol chord = new ChordSymbol(notes, key, time, Clef.Treble, null);
     Assert.AreEqual(chord.ToString(),
                     "ChordSymbol clef=Treble start=0 end=1600 width=16 hastwostems=False Note whitenote=F4 duration=Whole leftside=True ");
 }
Пример #17
0
        /** Add rest symbols between notes.  All times below are
         * measured in pulses.
         */
        private List<MusicSymbol> AddRests(List<MusicSymbol> symbols, TimeSignature time)
        {
            int prevtime = 0;

            List<MusicSymbol> result = new List<MusicSymbol>( symbols.Count );

            foreach (MusicSymbol symbol in symbols) {
            int starttime = symbol.StartTime;
            RestSymbol[] rests = GetRests(time, prevtime, starttime);
            if (rests != null) {
                foreach (RestSymbol r in rests) {
                    result.Add(r);
                }
            }

            result.Add(symbol);

            /* Set prevtime to the end time of the last note/symbol. */
            if (symbol is ChordSymbol) {
                ChordSymbol chord = (ChordSymbol)symbol;
                prevtime = Math.Max( chord.EndTime, prevtime );
            }
            else {
                prevtime = Math.Max(starttime, prevtime);
            }
            }
            return result;
        }
Пример #18
0
        /** 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]);
            }
        }
Пример #19
0
        /** Create the chord symbols for a single track.
         * @param midinotes  The Midinotes in the track.
         * @param key        The Key Signature, for determining sharps/flats.
         * @param time       The Time Signature, for determining the measures.
         * @param clefs      The clefs to use for each measure.
         * @ret An array of ChordSymbols
         */
        private List<ChordSymbol> CreateChords(List<MidiNote> midinotes, 
                                   KeySignature key,
                                   TimeSignature time,
                                   ClefMeasures clefs)
        {
            int i = 0;
            List<ChordSymbol> chords = new List<ChordSymbol>();
            List<MidiNote> notegroup = new List<MidiNote>(12);
            int len = midinotes.Count;

            while (i < len) {

            int starttime = midinotes[i].StartTime;
            Clef clef = clefs.GetClef(starttime);

            /* Group all the midi notes with the same start time
             * into the notes list.
             */
            notegroup.Clear();
            notegroup.Add(midinotes[i]);
            i++;
            while (i < len && midinotes[i].StartTime == starttime) {
                notegroup.Add(midinotes[i]);
                i++;
            }

            /* Create a single chord from the group of midi notes with
             * the same start time.
             */
            ChordSymbol chord = new ChordSymbol(notegroup, key, time, clef, this);
            chords.Add(chord);
            }

            return chords;
        }
Пример #20
0
        /** Return true if the chords can be connected, where their stems are
         * joined by a horizontal beam. In order to create the beam:
         *
         * - The chords must be in the same measure.
         * - The chord stems should not be a dotted duration.
         * - The chord stems must be the same duration, with one exception
         *   (Dotted Eighth to Sixteenth).
         * - The stems must all point in the same direction (up or down).
         * - The chord cannot already be part of a beam.
         *
         * - 6-chord beams must be 8th notes in 3/4, 6/8, or 6/4 time
         * - 3-chord beams must be either triplets, or 8th notes (12/8 time signature)
         * - 4-chord beams are ok for 2/2, 2/4 or 4/4 time, any duration
         * - 4-chord beams are ok for other times if the duration is 16th
         * - 2-chord beams are ok for any duration
         *
         * If startQuarter is true, the first note should start on a quarter note
         * (only applies to 2-chord beams).
         */
        public static bool CanCreateBeam(ChordSymbol[] chords, TimeSignature time, bool startQuarter)
        {
            int numChords = chords.Length;
            Stem firstStem = chords[0].Stem;
            Stem lastStem = chords[chords.Length-1].Stem;
            if (firstStem == null || lastStem == null) {
            return false;
            }
            int measure = chords[0].StartTime / time.Measure;
            NoteDuration dur = firstStem.Duration;
            NoteDuration dur2 = lastStem.Duration;

            bool dotted8_to_16 = false;
            if (chords.Length == 2 && dur == NoteDuration.DottedEighth &&
            dur2 == NoteDuration.Sixteenth) {
            dotted8_to_16 = true;
            }

            if (dur == NoteDuration.Whole || dur == NoteDuration.Half ||
            dur == NoteDuration.DottedHalf || dur == NoteDuration.Quarter ||
            dur == NoteDuration.DottedQuarter ||
            (dur == NoteDuration.DottedEighth && !dotted8_to_16)) {

            return false;
            }

            if (numChords == 6) {
            if (dur != NoteDuration.Eighth) {
                return false;
            }
            bool correctTime =
               ((time.Numerator == 3 && time.Denominator == 4) ||
                (time.Numerator == 6 && time.Denominator == 8) ||
                (time.Numerator == 6 && time.Denominator == 4) );

            if (!correctTime) {
                return false;
            }

            if (time.Numerator == 6 && time.Denominator == 4) {
                /* first chord must start at 1st or 4th quarter note */
                int beat = time.Quarter * 3;
                if ((chords[0].StartTime % beat) > time.Quarter/6) {
                    return false;
                }
            }
            }
            else if (numChords == 4) {
            if (time.Numerator == 3 && time.Denominator == 8) {
                return false;
            }
            bool correctTime =
              (time.Numerator == 2 || time.Numerator == 4 || time.Numerator == 8);
            if (!correctTime && dur != NoteDuration.Sixteenth) {
                return false;
            }

            /* chord must start on quarter note */
            int beat = time.Quarter;
            if (dur == NoteDuration.Eighth) {
                /* 8th note chord must start on 1st or 3rd quarter note */
                beat = time.Quarter * 2;
            }
            else if (dur == NoteDuration.ThirtySecond) {
                /* 32nd note must start on an 8th beat */
                beat = time.Quarter / 2;
            }

            if ((chords[0].StartTime % beat) > time.Quarter/6) {
                return false;
            }
            }
            else if (numChords == 3) {
            bool valid = (dur == NoteDuration.Triplet) ||
                          (dur == NoteDuration.Eighth &&
                           time.Numerator == 12 && time.Denominator == 8);
            if (!valid) {
                return false;
            }

            /* chord must start on quarter note */
            int beat = time.Quarter;
            if (time.Numerator == 12 && time.Denominator == 8) {
                /* In 12/8 time, chord must start on 3*8th beat */
                beat = time.Quarter/2 * 3;
            }
            if ((chords[0].StartTime % beat) > time.Quarter/6) {
                return false;
            }
            }

            else if (numChords == 2) {
            if (startQuarter) {
                int beat = time.Quarter;
                if ((chords[0].StartTime % beat) > time.Quarter/6) {
                    return false;
                }
            }
            }

            foreach (ChordSymbol chord in chords) {
            if ((chord.StartTime / time.Measure) != measure)
                return false;
            if (chord.Stem == null)
                return false;
            if (chord.Stem.Duration != dur && !dotted8_to_16)
                return false;
            if (chord.Stem.isBeam)
                return false;
            }

            /* Check that all stems can point in same direction */
            bool hasTwoStems = false;
            int direction = Stem.Up;
            foreach (ChordSymbol chord in chords) {
            if (chord.HasTwoStems) {
                if (hasTwoStems && chord.Stem.Direction != direction) {
                    return false;
                }
                hasTwoStems = true;
                direction = chord.Stem.Direction;
            }
            }

            /* Get the final stem direction */
            if (!hasTwoStems) {
            WhiteNote note1;
            WhiteNote note2;
            note1 = (firstStem.Direction == Stem.Up ? firstStem.Top : firstStem.Bottom);
            note2 = (lastStem.Direction == Stem.Up ? lastStem.Top : lastStem.Bottom);
            direction = StemDirection(note1, note2, chords[0].Clef);
            }

            /* If the notes are too far apart, don't use a beam */
            if (direction == Stem.Up) {
            if (Math.Abs(firstStem.Top.Dist(lastStem.Top)) >= 11) {
                return false;
            }
            }
            else {
            if (Math.Abs(firstStem.Bottom.Dist(lastStem.Bottom)) >= 11) {
                return false;
            }
            }
            return true;
        }
Пример #21
0
        private SheetMusic sheetmusic;      /** Used to get colors and other options */


        /** Create a new Chord Symbol from the given list of midi notes.
         * All the midi notes will have the same start time.  Use the
         * key signature to get the white key and accidental symbol for
         * each note.  Use the time signature to calculate the duration
         * of the notes. Use the clef when drawing the chord.
         */
        public ChordSymbol(List <MidiNote> midinotes, KeySignature key,
                           TimeSignature time, Clef c, SheetMusic sheet)
        {
            int len = midinotes.Count;
            int i;

            hastwostems = false;
            clef        = c;
            sheetmusic  = sheet;

            starttime = midinotes[0].StartTime;
            endtime   = midinotes[0].EndTime;

            for (i = 0; i < midinotes.Count; i++)
            {
                if (i > 1)
                {
                    if (midinotes[i].Number < midinotes[i - 1].Number)
                    {
                        throw new System.ArgumentException("Chord notes not in increasing order by number");
                    }
                }
                endtime = Math.Max(endtime, midinotes[i].EndTime);
            }

            notedata     = CreateNoteData(midinotes, key, time);
            accidsymbols = CreateAccidSymbols(notedata, clef);


            /* Find out how many stems we need (1 or 2) */
            NoteDuration dur1   = notedata[0].duration;
            NoteDuration dur2   = dur1;
            int          change = -1;

            for (i = 0; i < notedata.Length; i++)
            {
                dur2 = notedata[i].duration;
                if (dur1 != dur2)
                {
                    change = i;
                    break;
                }
            }

            if (dur1 != dur2)
            {
                /* We have notes with different durations.  So we will need
                 * two stems.  The first stem points down, and contains the
                 * bottom note up to the note with the different duration.
                 *
                 * The second stem points up, and contains the note with the
                 * different duration up to the top note.
                 */
                hastwostems = true;
                stem1       = new Stem(notedata[0].whitenote,
                                       notedata[change - 1].whitenote,
                                       dur1,
                                       Stem.Down,
                                       NotesOverlap(notedata, 0, change)
                                       );

                stem2 = new Stem(notedata[change].whitenote,
                                 notedata[notedata.Length - 1].whitenote,
                                 dur2,
                                 Stem.Up,
                                 NotesOverlap(notedata, change, notedata.Length)
                                 );
            }
            else
            {
                /* All notes have the same duration, so we only need one stem. */
                int direction = StemDirection(notedata[0].whitenote,
                                              notedata[notedata.Length - 1].whitenote,
                                              clef);

                stem1 = new Stem(notedata[0].whitenote,
                                 notedata[notedata.Length - 1].whitenote,
                                 dur1,
                                 direction,
                                 NotesOverlap(notedata, 0, notedata.Length)
                                 );
                stem2 = null;
            }

            /* For whole notes, no stem is drawn. */
            if (dur1 == NoteDuration.Whole)
            {
                stem1 = null;
            }
            if (dur2 == NoteDuration.Whole)
            {
                stem2 = null;
            }

            width = MinWidth;
        }
Пример #22
0
        /** Given the raw midi notes (the note number and duration in pulses),
         * calculate the following note data:
         * - The white key
         * - The accidental (if any)
         * - The note duration (half, quarter, eighth, etc)
         * - The side it should be drawn (left or side)
         * By default, notes are drawn on the left side.  However, if two notes
         * overlap (like A and B) you cannot draw the next note directly above it.
         * Instead you must shift one of the notes to the right.
         *
         * The KeySignature is used to determine the white key and accidental.
         * The TimeSignature is used to determine the duration.
         */
        private static NoteData[] CreateNoteData(List<MidiNote> midinotes, KeySignature key,
                              TimeSignature time)
        {
            int len = midinotes.Count;
            NoteData[] notedata = new NoteData[len];

            for (int i = 0; i < len; i++) {
            MidiNote midi = midinotes[i];
            notedata[i] = new NoteData();
            notedata[i].number = midi.Number;
            notedata[i].leftside = true;
            notedata[i].whitenote = key.GetWhiteNote(midi.Number);
            notedata[i].duration = time.GetNoteDuration(midi.EndTime - midi.StartTime);
            notedata[i].accid = key.GetAccidental(midi.Number, midi.StartTime / time.Measure);

            if (i > 0 && (notedata[i].whitenote.Dist(notedata[i-1].whitenote) == 1)) {
                /* This note (notedata[i]) overlaps with the previous note.
                 * Change the side of this note.
                 */

                if (notedata[i-1].leftside) {
                    notedata[i].leftside = false;
                } else {
                    notedata[i].leftside = true;
                }
            } else {
                notedata[i].leftside = true;
            }
            }
            return notedata;
        }
Пример #23
0
        public void init(MidiFile file, MidiOptions options, List <MidiTrack> tracks)
        {
            zoom     = 1.0f;
            filename = file.FileName;

            SetColors(options.colors, options.shadeColor, options.shade2Color);
            pen = new Pen(Color.Black, 1);

            SetNoteSize(options.largeNoteSize);
            scrollVert      = options.scrollVert;
            showNoteLetters = options.showNoteLetters;
            TimeSignature time = file.Time;

            if (options.time != null)
            {
                time = options.time;
            }
            if (options.key == -1)
            {
                mainkey = GetKeySignature(tracks);
            }
            else
            {
                mainkey = new KeySignature(options.key);
            }

            numtracks = tracks.Count;

            int lastStart = file.EndTime() + options.shifttime;

            /* Create all the music symbols (notes, rests, vertical bars, and
             * clef changes).  The symbols variable contains a list of music
             * symbols for each track.  The list does not include the left-side
             * Clef and key signature symbols.  Those can only be calculated
             * when we create the staffs.
             */
            List <MusicSymbol>[] symbols = new List <MusicSymbol> [numtracks];
            for (int tracknum = 0; tracknum < numtracks; tracknum++)
            {
                MidiTrack          track  = tracks[tracknum];
                ClefMeasures       clefs  = new ClefMeasures(track.Notes, time.Measure);
                List <ChordSymbol> chords = CreateChords(track.Notes, mainkey, time, clefs);
                symbols[tracknum] = CreateSymbols(chords, clefs, time, lastStart);
            }

            List <LyricSymbol>[] lyrics = null;
            if (options.showLyrics)
            {
                lyrics = GetLyrics(tracks);
            }

            /* Vertically align the music symbols */
            SymbolWidths widths = new SymbolWidths(symbols, lyrics);

            AlignSymbols(symbols, widths, options);

            staffs = CreateStaffs(symbols, mainkey, options, time.Measure);
            CreateAllBeamedChords(symbols, time);
            if (lyrics != null)
            {
                AddLyricsToStaffs(staffs, lyrics);
            }

            /* After making chord pairs, the stem directions can change,
             * which affects the staff height.  Re-calculate the staff height.
             */
            foreach (Staff staff in staffs)
            {
                staff.CalculateHeight();
            }

            BackColor = Color.White;

            SetZoom(1.0f);
        }