Staff, Bar, Note, Rest

The core hierarchy beneath Score. These objects are created automatically by load_musicxml() and can also be constructed programmatically.

PyLute showing Staff, Bar, Note, Clef and other notation elements
Staff, Bar, Note, Rest, Clef, KeySignature, TimeSignature, and Brace all visible here.

Staff

A single staff — five horizontal lines and a list of bars. Instantiated by Score. Access via score.staffs[index].

PropertyTypeDescription
barslist[Bar]All bars on this staff, in order.
clef_typestr"treble" or "bass".
spaceintPixel distance between adjacent staff lines. Derived from the font size.
positionlist[int][x, y] pixel origin of this staff on the current surface.
lengthintStaff width in pixels (equals Score.length).
scoreScoreBack-reference to the owning Score.

Bar

A single measure. Holds events (notes and rests), tracks beat capacity, and handles beam grouping for eighth/sixteenth notes. Access via staff.bars[index].

PropertyTypeDescription
eventslist[Note | Rest]All note and rest events in chronological order.
indexintZero-based bar index within the staff.
timetuple[int, int]Local time signature (always equal to the score's time signature).
bar_start_xfloatPixel X of the bar's left barline within the system.
barspacefloatAllocated pixel width for this bar.
spaceAvailable() → float

Returns remaining beat capacity in this bar as a float (0.0 = full). One whole note = 1.0 unit of capacity. A 4/4 bar starts at 1.0 and decreases as events are added.

bar = score.staffs[0].bars[0]
print(bar.spaceAvailable())  # 1.0 (empty 4/4 bar)

score.addNote("half", "C4")
print(bar.spaceAvailable())  # 0.5

score.addNote("half", "E4")
print(bar.spaceAvailable())  # 0.0 (bar full)

Note

A single notated pitch. Instantiated by Score.addNote(). Access via bar.events.

class Note
Note(ntype="whole", pitch="C4", staff=None)
PropertyTypeDescription
typestrDuration type string, e.g. "quarter".
pitchstrScientific pitch notation, e.g. "F#4".
colortuple | NoneRGB color override for the notehead. None uses the default (black). Set to a color tuple to highlight a note: note.color = (220, 60, 60).
beamedboolSet to True by the bar renderer when this note is part of a beam group. Read-only in practice.
offsetfloatFractional horizontal position within the bar (0.0 = start, 1.0 = end). Computed by Bar.calculateMetrics().

Highlight a note

Set note.color to an RGB tuple to draw attention to specific notes — for example, highlighting the currently played key in Pianove.

bar = score.staffs[0].bars[0]
note = bar.events[0]
note.color = (52, 199, 89)   # green highlight
# Returns to normal on next frame if you reset it:
note.color = None

Rest

A rest symbol. Instantiated by Score.addRest(). Accepts the same duration types as Note. Rests have no pitch and render as standard music notation rest glyphs (half rest, quarter rest, etc.) using the Bravura font.

Rest(rtype="quarter", staff=None)
PropertyTypeDescription
typestrDuration type string.
offsetfloatHorizontal position within the bar (computed by Bar).

Clef, KeySignature, TimeSignature, Brace

These elements are created automatically by Score. You generally do not interact with them directly. They are listed here for completeness.

Clef

Clef(ctype="treble", staff=None)
# ctype: "treble" or "bass"
# Accessed via: score.clefs[staff_index]

KeySignature

KeySignature(accidentals=0, staff=None)
# accidentals: -7 (7 flats) to +7 (7 sharps). 0 = C major.
# Accessed via: score.key_signatures[staff_index]

The width property returns the pixel width of the rendered key signature — used internally to compute score.frontal_offset.

TimeSignature

TimeSignature(staff=None)
# Reads time from score.time
# Accessed via: score.time_signatures[staff_index]

Brace

Brace(staves: list[Staff], surface: pygame.Surface)
# Created automatically when staffs > 1
# Accessed via: score.brace

The brace renders the vertical curly bracket connecting two staves (standard for piano notation). Call score.brace.reset() if you update the staff layout manually.

Chord

Chord(
    notes: list[Note],   # two or more Note objects at the same rhythmic position
    staff=None,
)

Groups two or more Note objects that sound simultaneously. Notes are automatically sorted by pitch (highest first) and adjacent semitones are offset horizontally to prevent notehead overlap. A single stem is shared across all notes, with stem direction determined by the median pitch.

In practice Chord objects are created automatically by load_musicxml(). For programmatic use, add a Chord to a bar the same way as a single note:

from pylute import Note, Chord, Score

score = Score(surface=screen, staffs=1, bars=4, key="C", time=(4, 4))
bar = score.staffs[0].bars[0]

chord = Chord([Note("quarter", "C4"), Note("quarter", "E4"), Note("quarter", "G4")])
bar.notes.append(chord)
score.draw()

The color attribute on a Chord overrides the colour of all its noteheads. Individual notes retain their own color when Chord.color is None.

Tuplet

Tuplet(
    notes: list[Note],
    actual: int = 3,    # how many notes are played…
    normal: int = 2,    # …in the time of this many
    staff=None,
)

Wraps a group of notes that are to be played in irregular time — most commonly a triplet (actual=3, normal=2: 3 notes in the time of 2). A bracket with the tuplet number is drawn above or below the group. As with Chord, tuplets are built automatically from MusicXML; direct construction looks like this:

from pylute import Note, Tuplet, Score

score = Score(surface=screen, staffs=1, bars=2, key="C", time=(4, 4))
bar = score.staffs[0].bars[0]

# Triplet: 3 eighth notes in the time of 2
triplet = Tuplet(
    notes=[Note("eighth", "C4"), Note("eighth", "E4"), Note("eighth", "G4")],
    actual=3,
    normal=2,
)
bar.notes.append(triplet)
total_duration() → float

Returns the actual beat duration of the tuplet group. For a standard triplet of eighth notes this is 1.0 (one quarter-note beat).

Dynamic

Dynamic(
    mark: str,           # "pp", "p", "mp", "mf", "f", "ff", "fff", "ffff",
                         # "ppp", "pppp", "sf", "sfz", "sfp", "rfz", "fz", "fp"
    staff=None,
    above: bool = False, # True = render above the staff, False = below (default)
)

Renders a SMuFL dynamic glyph from the bundled music font. Dynamics are attached to a bar's notes list alongside notes and rests; the bar renderer positions them at the correct x offset before calling draw().

from pylute import Note, Dynamic, Score

score = Score(surface=screen, staffs=1, bars=4, key="C", time=(4, 4))
bar = score.staffs[0].bars[0]

bar.notes.append(Note("quarter", "C4"))
bar.notes.append(Dynamic("mf"))       # appears under the first note
bar.notes.append(Note("quarter", "E4"))

Articulation

Articulation(
    mark: str,           # "staccato", "accent", "tenuto", "marcato",
                         # "fermata", "staccatissimo", "stress", "unstress"
    staff=None,
    above: bool = True,  # True = above notehead (default), False = below
)

Renders an articulation glyph above or below a notehead. Like Dynamic, articulations live in a bar's notes list and are positioned automatically. Articulation and Dynamic objects found in MusicXML are attached to their notes automatically by load_musicxml().

from pylute import Note, Articulation, Score

score = Score(surface=screen, staffs=1, bars=4, key="C", time=(4, 4))
bar = score.staffs[0].bars[0]

bar.notes.append(Note("quarter", "C4"))
bar.notes.append(Articulation("staccato"))   # dot above the C4
bar.notes.append(Note("quarter", "E4"))
bar.notes.append(Articulation("accent"))     # accent on E4