/// <summary> /// This function uses a sophisticated algorithm to decide whether flats or sharps are to be used to /// represent the chord. Chords can have naturals and either sharps or flats (but not both). /// The display of naturals is forced if the same notehead height also exists with a sharp or flat. /// (The display of other accidentals are always forced in the Head constructor.) /// Exceptions: This function throws an exception if /// 1) any of the input midiPitches is out of midi range (0..127). /// 2) the midiPitches are not in ascending order /// 3) the midiPitches are not unique. /// The midiPitches argument must be in order of size (ascending), but Heads are created in top-down order. /// </summary> /// <param name="midiPitches"></param> public void SetNoteheadPitches(List<byte> midiPitches) { #region check inputs int previousPitch = -1; foreach(int midiPitch in midiPitches) { Debug.Assert(midiPitch >= 0 && midiPitch <= 127, "midiPitch out of range."); Debug.Assert(midiPitch > previousPitch, "midiPitches must be unique and in ascending order."); previousPitch = midiPitch; } #endregion this.HeadsTopDown.Clear(); bool useSharp = UseSharps(midiPitches, null); // returns false if flats are to be used for(int i = midiPitches.Count - 1; i >= 0; --i) { Head head = new Head(this, midiPitches[i], -1, useSharp); this.HeadsTopDown.Add(head); } for(int i = 0; i < midiPitches.Count; i++) { if(this.HeadsTopDown[i].Alteration == 0) { this.HeadsTopDown[i].DisplayAccidental = DisplayAccidental.suppress; } } for(int i = 1; i < midiPitches.Count; i++) { if(this.HeadsTopDown[i].Pitch == this.HeadsTopDown[i - 1].Pitch) { this.HeadsTopDown[i - 1].DisplayAccidental = DisplayAccidental.force; this.HeadsTopDown[i].DisplayAccidental = DisplayAccidental.force; } } }
/// <summary> /// Head.Alteration is either 0 or 1 (natural or sharp). /// Returns the sharp/flat preference for representing the interval on this head, /// or null if there is no preference. /// </summary> /// <param name="head"></param> /// <param name="interval"></param> /// <returns></returns> private bool?GetUseSharps(Head head, int interval) { M.Assert(interval > 0 && interval < 12); M.Assert(head.Alteration == 0 || head.Alteration == 1); bool?useSharpsOrNull = null; #region Head is A if (head.Pitch[0] == 'A') { if (head.Alteration == 0) // (A) { switch (interval) { case 11: case 9: case 4: useSharpsOrNull = true; break; case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (A#) { switch (interval) { case 11: case 9: case 7: case 4: case 2: useSharpsOrNull = false; break; case 1: useSharpsOrNull = true; break; default: break; } } } #endregion A #region Head is B else if (head.Pitch[0] == 'B') { if (head.Alteration == 0) // (B) { switch (interval) { case 11: case 9: case 7: case 4: case 2: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (B#) { useSharpsOrNull = false; } } #endregion #region Head is C else if (head.Pitch[0] == 'C') { if (head.Alteration == 0) // (C) { switch (interval) { case 10: case 8: case 3: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (C#) { switch (interval) { case 4: useSharpsOrNull = false; break; case 10: case 8: case 3: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is C #region Head is D else if (head.Pitch[0] == 'D') { if (head.Alteration == 0) // (D) { switch (interval) { case 11: case 4: useSharpsOrNull = true; break; case 8: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (D#) { switch (interval) { case 11: case 9: case 4: case 2: useSharpsOrNull = false; break; case 8: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is D #region Head is E else if (head.Pitch[0] == 'E') { if (head.Alteration == 0) // (E) { switch (interval) { case 11: case 9: case 4: case 2: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (E#) { useSharpsOrNull = false; } } #endregion Head is E #region Head is F else if (head.Pitch[0] == 'F') { if (head.Alteration == 0) // (F) { switch (interval) { case 10: case 8: case 5: case 3: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (F#) { switch (interval) { case 10: case 8: case 5: case 3: case 1: useSharpsOrNull = true; break; case 11: // should really be G-flat, but I dont like G-flats! //useSharpsOrNull = false; break; default: break; } } } #endregion Head is F #region Head is G else if (head.Pitch[0] == 'G') { if (head.Alteration == 0) // (G) { switch (interval) { case 8: case 3: case 1: useSharpsOrNull = false; break; case 11: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (G#) { switch (interval) { case 11: case 9: case 4: useSharpsOrNull = false; break; case 8: case 3: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is G return(useSharpsOrNull); }
/// <summary> /// Returns true if sharps are to be used to represent the midiPitches, /// or false if flats are to be used. /// </summary> /// <param name="midiPitches"></param> /// <returns></returns> internal bool UseSharps(List <byte> midiPitches, List <byte> midiVelocities) { for (int i = 0; i < midiPitches.Count; i++) { M.Assert(midiPitches[i] >= 0 && midiPitches[i] < 128); } bool useSharps = true; List <int> midiIntervals = new List <int>(); for (int i = 1; i < midiPitches.Count; i++) { midiIntervals.Add(midiPitches[i] - midiPitches[i - 1]); } List <int> collapsedIntervals = new List <int>(); foreach (int midiInterval in midiIntervals) { collapsedIntervals.Add(midiInterval % 12); } // look in the following order (I dont like augmented seconds at all)! int[] preferredIntervals = { 3, 7, 1, 2, 4, 5, 6, 8, 9, 10, 11 }; bool? useSharpsOrNull = null; foreach (int interval in preferredIntervals) { int index = 0; if (collapsedIntervals.Contains(interval)) { foreach (int value in collapsedIntervals) { if (value == interval) { break; } index++; } // index is now both the index of the "most preferred" interval // and the index of the lower midiPitch of the "most preferred" interval. Head head = null; M.Assert(midiVelocities != null); // April 2020 (InputChordSymbols no longer exist) if (midiVelocities != null) { head = new Head(null, midiPitches[index], midiVelocities[index], true); } else { //head = new Head(null, midiPitches[index], -1, true); // a Head in an InputChordSymbol } // head is either natural or sharp. int preferredInterval = collapsedIntervals[index]; useSharpsOrNull = GetUseSharps(head, preferredInterval); } if (useSharpsOrNull != null) { useSharps = (bool)useSharpsOrNull; break; } } return(useSharps); }
/// <summary> /// Returns true if sharps are to be used to represent the midiPitches, /// or false if flats are to be used. /// </summary> /// <param name="midiPitches"></param> /// <returns></returns> internal bool UseSharps(List<byte> midiPitches, List<byte> midiVelocities) { for(int i = 0; i < midiPitches.Count; i++) { Debug.Assert(midiPitches[i] >= 0 && midiPitches[i] < 128); } bool useSharps = true; List<int> midiIntervals = new List<int>(); for(int i = 1; i < midiPitches.Count; i++) { midiIntervals.Add(midiPitches[i] - midiPitches[i - 1]); } List<int> collapsedIntervals = new List<int>(); foreach(int midiInterval in midiIntervals) { collapsedIntervals.Add(midiInterval % 12); } // look in the following order (I dont like augmented seconds at all)! int[] preferredIntervals = { 3, 7, 1, 2, 4, 5, 6, 8, 9, 10, 11 }; bool? useSharpsOrNull = null; foreach(int interval in preferredIntervals) { int index = 0; if(collapsedIntervals.Contains(interval)) { foreach(int value in collapsedIntervals) { if(value == interval) break; index++; } // index is now both the index of the "most preferred" interval // and the index of the lower midiPitch of the "most preferred" interval. Head head = null; if(midiVelocities != null) { head = new Head(null, midiPitches[index], midiVelocities[index], true); } else { head = new Head(null, midiPitches[index], -1, true); // a Head in an InputChordSymbol } // head is either natural or sharp. int preferredInterval = collapsedIntervals[index]; useSharpsOrNull = GetUseSharps(head, preferredInterval); } if(useSharpsOrNull != null) { useSharps = (bool)useSharpsOrNull; break; } } return useSharps; }
/// <summary> /// Head.Alteration is either 0 or 1 (natural or sharp). /// Returns the sharp/flat preference for representing the interval on this head, /// or null if there is no preference. /// </summary> /// <param name="head"></param> /// <param name="interval"></param> /// <returns></returns> private bool? GetUseSharps(Head head, int interval) { Debug.Assert(interval > 0 && interval < 12); Debug.Assert(head.Alteration == 0 || head.Alteration == 1); bool? useSharpsOrNull = null; #region Head is A if(head.Pitch[0] == 'A') { if(head.Alteration == 0) // (A) { switch(interval) { case 11: case 9: case 4: useSharpsOrNull = true; break; case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (A#) { switch(interval) { case 11: case 9: case 7: case 4: case 2: useSharpsOrNull = false; break; case 1: useSharpsOrNull = true; break; default: break; } } } #endregion A #region Head is B else if(head.Pitch[0] == 'B') { if(head.Alteration == 0) // (B) { switch(interval) { case 11: case 9: case 7: case 4: case 2: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (B#) { useSharpsOrNull = false; } } #endregion #region Head is C else if(head.Pitch[0] == 'C') { if(head.Alteration == 0) // (C) { switch(interval) { case 10: case 8: case 3: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (C#) { switch(interval) { case 4: useSharpsOrNull = false; break; case 10: case 8: case 3: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is C #region Head is D else if(head.Pitch[0] == 'D') { if(head.Alteration == 0) // (D) { switch(interval) { case 11: case 4: useSharpsOrNull = true; break; case 8: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (D#) { switch(interval) { case 11: case 9: case 4: case 2: useSharpsOrNull = false; break; case 8: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is D #region Head is E else if(head.Pitch[0] == 'E') { if(head.Alteration == 0) // (E) { switch(interval) { case 11: case 9: case 4: case 2: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (E#) { useSharpsOrNull = false; } } #endregion Head is E #region Head is F else if(head.Pitch[0] == 'F') { if(head.Alteration == 0) // (F) { switch(interval) { case 10: case 8: case 5: case 3: case 1: useSharpsOrNull = false; break; default: break; } } else // Alteration == 1 (F#) { switch(interval) { case 10: case 8: case 5: case 3: case 1: useSharpsOrNull = true; break; case 11: // should really be G-flat, but I dont like G-flats! //useSharpsOrNull = false; break; default: break; } } } #endregion Head is F #region Head is G else if(head.Pitch[0] == 'G') { if(head.Alteration == 0) // (G) { switch(interval) { case 8: case 3: case 1: useSharpsOrNull = false; break; case 11: useSharpsOrNull = true; break; default: break; } } else // Alteration == 1 (G#) { switch(interval) { case 11: case 9: case 4: useSharpsOrNull = false; break; case 8: case 3: case 1: useSharpsOrNull = true; break; default: break; } } } #endregion Head is G return useSharpsOrNull; }
/// <summary> /// Accidentals /// </summary> /// <param name="name"></param> /// <returns></returns> private string GetClichtCharacterString(Head head) { string cLichtCharacterString = null; switch(head.Alteration) { case -1: cLichtCharacterString = "b"; break; case 0: cLichtCharacterString = "n"; break; case 1: cLichtCharacterString = "#"; break; default: Debug.Assert(false, "unknown accidental type"); break; } return cLichtCharacterString; }
public CLichtCharacterMetrics(Head head, float fontHeight) : base() { _objectType = GetClichtCharacterString(head); Debug.Assert(_objectType != null); Metrics m = CLichtFontMetrics.CLichtGlyphBoundingBoxesDictPX[_objectType]; _originY = 0; _top = m.Top * fontHeight; _bottom = m.Bottom * fontHeight; // move so that Left = 0. _left = 0; _right = (m.Right - m.Left) * fontHeight; _originX = -m.Left * fontHeight; _fontHeight = fontHeight; }
public AccidentalMetrics(Head head, float fontHeight, float gap) : base(head, fontHeight) { float verticalPadding = gap / 5; _top -= verticalPadding; _bottom += verticalPadding; switch(_objectType) { case "b": _left -= gap * 0.2F; _right += gap * 0.2F; break; case "n": _left -= gap * 0.2F; _right += gap * 0.2F; break; case "#": _left -= gap * 0.1F; _right += gap * 0.1F; break; } if(head != null && head.ColorAttribute != null) { _colorAttribute = head.ColorAttribute; } }
public HeadMetrics(ChordSymbol chord, Head head, float gapVBPX) : base(chord.DurationClass, false, chord.FontHeight) { Move((Left - Right) / 2F, 0F); // centre horizontally float horizontalPadding = chord.FontHeight * 0.04F; _leftStemX = _left; _rightStemX = _right; _left -= horizontalPadding; _right += horizontalPadding; if(head != null && head.ColorAttribute != null) { _colorAttribute = head.ColorAttribute; } }
public AccidentalMetrics(Head head, float fontHeight, float gap) : base(head, fontHeight) { float verticalPadding = gap / 5; _top -= verticalPadding; _bottom += verticalPadding; switch(_objectType) { case "b": _left -= gap * 0.2F; _right += gap * 0.2F; break; case "n": _left -= gap * 0.2F; _right += gap * 0.2F; break; case "#": _left -= gap * 0.1F; _right += gap * 0.1F; break; //case "#": // _left -= gap * 0.05F; // _right += gap * 0.05F; // break; } }