musicalValueOf static method

double musicalValueOf(
  1. MusicalElement element
)

Calculates the rhythmic value occupied by element.

Returns 0.0 for elements that do not occupy musical time (clefs, key signatures, barlines, ...). Tuplets are scaled by their normalNotes / actualNotes ratio.

Grace notes take no time

A Note with isGraceNote == true returns 0.0. A grace note is an ornament: it is printed small, it is drawn, it occupies horizontal WIDTH, but it does not advance the musical clock — it is stolen from the neighbouring note, not added to the bar (Behind Bars p.125; MusicXML <grace> notes carry no <duration> at all, and MEI @grace likewise excludes the note from the layer's rhythmic total).

Measured before this rule existed: a 4/4 bar of four quarters plus two eighth grace notes reported currentMusicalValue = 1.1875 instead of 1.0, and the layout onsets came out [0, 0.125, 0.1875, 0.4375, 0.6875, 0.9375] while MidiMapper (which already skipped grace notes) produced a correct 3840 ticks at 960 ppq. Layout and playback therefore disagreed, and the ADR-002 shared onset grid — the thing that makes a grand staff line up — broke for any staff containing a grace note.

Chord and Tuplet used to be resolved through runtimeType.toString() "to avoid circular imports". Verified: there is no cycle. chord.dart imports musical_element/note/duration/ornament/ dynamic/bounding_box_support and tuplet.dart imports musical_element/ note/rest/chord/time_signature/tuplet_bracket/tuplet_number — neither reaches measure.dart, and nothing in their transitive closure does. The string comparison was also silently wrong for any subclass of Chord or Tuplet, so both are matched with is now.

Implementation

static double musicalValueOf(MusicalElement element) {
  if (element is Note) {
    return element.isGraceNote ? 0.0 : element.duration.realValue;
  } else if (element is Rest) {
    return element.duration.realValue;
  } else if (element is Chord) {
    return element.duration.realValue;
  } else if (element is Tuplet) {
    double tupletValue = 0.0;
    for (final tupletElement in element.elements) {
      tupletValue += musicalValueOf(tupletElement);
    }
    // Apply the tuplet ratio
    if (element.actualNotes > 0) {
      tupletValue = tupletValue * (element.normalNotes / element.actualNotes);
    }
    return tupletValue;
  }
  return 0.0; // Elements without duration (clef, key signature, etc.)
}