BringStemsCloser(ChordSymbol[] chords) { Stem firstStem = chords[0].Stem; Stem lastStem = chords[1].Stem; /* If we're connecting a dotted 8th to a 16th, increase * the stem end of the dotted eighth. */ if (firstStem.Duration == NoteDuration.DottedEighth && lastStem.Duration == NoteDuration.Sixteenth) { if (firstStem.Direction == Stem.Up) { firstStem.End = firstStem.End.Add(2); } else { firstStem.End = firstStem.End.Add(-2); } } /* Bring the stem ends closer together */ int distance = Math.Abs(firstStem.End.Dist(lastStem.End)); if (firstStem.Direction == Stem.Up) { if (WhiteNote.Max(firstStem.End, lastStem.End) == firstStem.End) { lastStem.End = lastStem.End.Add(distance / 2); } else { firstStem.End = firstStem.End.Add(distance / 2); } } else { if (WhiteNote.Min(firstStem.End, lastStem.End) == firstStem.End) { lastStem.End = lastStem.End.Add(-distance / 2); } else { firstStem.End = firstStem.End.Add(-distance / 2); } } }
void CreateBeam(ChordSymbol[] chords, int spacing) { Stem firstStem = chords[0].Stem; Stem lastStem = chords[chords.Length - 1].Stem; /* Calculate the new stem direction */ int newdirection = -1; foreach (ChordSymbol chord in chords) { if (chord.HasTwoStems) { newdirection = chord.Stem.Direction; break; } } if (newdirection == -1) { WhiteNote note1; WhiteNote note2; note1 = (firstStem.Direction == Stem.Up ? firstStem.Top : firstStem.Bottom); note2 = (lastStem.Direction == Stem.Up ? lastStem.Top : lastStem.Bottom); newdirection = StemDirection(note1, note2, chords[0].Clef); } foreach (ChordSymbol chord in chords) { chord.Stem.Direction = newdirection; } if (chords.Length == 2) { BringStemsCloser(chords); } else { LineUpStemEnds(chords); } firstStem.SetPair(lastStem, spacing); for (int i = 1; i < chords.Length; i++) { chords[i].Stem.Receiver = true; } }
/** Create a new stem. The top note, bottom note, and direction are * needed for drawing the vertical line of the stem. The duration is * needed to draw the tail of the stem. The overlap boolean is true * if the notes in the chord overlap. If the notes overlap, the * stem must be drawn on the right side. */ public Stem(WhiteNote bottom, WhiteNote top, NoteDuration duration, int direction, bool overlap) { this.top = top; this.bottom = bottom; this.duration = duration; this.direction = direction; this.notesoverlap = overlap; if (direction == Up || notesoverlap) { side = RightSide; } else { side = LeftSide; } end = CalculateEnd(); pair = null; width_to_pair = 0; receiver_in_pair = false; }
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); }
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; }
LineUpStemEnds(ChordSymbol[] chords) { Stem firstStem = chords[0].Stem; Stem lastStem = chords[chords.Length - 1].Stem; Stem middleStem = chords[1].Stem; if (firstStem.Direction == Stem.Up) { /* Find the highest stem. The beam will either: * - Slant downwards (first stem is highest) * - Slant upwards (last stem is highest) * - Be straight (middle stem is highest) */ WhiteNote top = firstStem.End; foreach (ChordSymbol chord in chords) { top = WhiteNote.Max(top, chord.Stem.End); } if (top == firstStem.End && top.Dist(lastStem.End) >= 2) { firstStem.End = top; middleStem.End = top.Add(-1); lastStem.End = top.Add(-2); } else if (top == lastStem.End && top.Dist(firstStem.End) >= 2) { firstStem.End = top.Add(-2); middleStem.End = top.Add(-1); lastStem.End = top; } else { firstStem.End = top; middleStem.End = top; lastStem.End = top; } } else { /* Find the bottommost stem. The beam will either: * - Slant upwards (first stem is lowest) * - Slant downwards (last stem is lowest) * - Be straight (middle stem is highest) */ WhiteNote bottom = firstStem.End; foreach (ChordSymbol chord in chords) { bottom = WhiteNote.Min(bottom, chord.Stem.End); } if (bottom == firstStem.End && lastStem.End.Dist(bottom) >= 2) { middleStem.End = bottom.Add(1); lastStem.End = bottom.Add(2); } else if (bottom == lastStem.End && firstStem.End.Dist(bottom) >= 2) { middleStem.End = bottom.Add(1); firstStem.End = bottom.Add(2); } else { firstStem.End = bottom; middleStem.End = bottom; lastStem.End = bottom; } } /* All middle stems have the same end */ for (int i = 1; i < chords.Length - 1; i++) { Stem stem = chords[i].Stem; stem.End = middleStem.End; } }
/** Pair this stem with another Chord. Instead of drawing a curvy tail, * this stem will now have to draw a beam to the given stem pair. The * width (in pixels) to this stem pair is passed as argument. */ public void SetPair(Stem pair, int width_to_pair) { this.pair = pair; this.width_to_pair = width_to_pair; }