Skip to content

v3.17

Compare
Choose a tag to compare
@trevorbaca trevorbaca released this 25 Apr 22:13
· 73 commits to main since this release
v3.17

Abjad 3.17 works with Python 3.10 (and 3.11) and LilyPond 2.23.6 (and later).

#1522. CHANGED. Cleaned up context initializers. This forces keyword parameters to be keyword-only:

OLD:

abjad.Score(
    components=None,
    lilypond_type: str = "Score",
    simultaneous: bool = True,
    name: str | None = None,
    tag: _tag.Tag | None = None,
    *,
    language: str = "english",
)

abjad.Staff(
    components=None,
    lilypond_type: str = "Staff",
    simultaneous: bool = False,
    name: str | None = None,
    tag: _tag.Tag | None = None,
    *,
    language: str = "english",
)

abjad.StaffGroup(
    components=None,
    lilypond_type: str = "StaffGroup",
    simultaneous: bool = True,
    name: str | None = None,
    tag: _tag.Tag | None = None,
    *,
    language: str = "english",
)

abjad.Voice(
    components=None,
    lilypond_type: str = "Voice",
    simultaneous: bool = False,
    name: str | None = None,
    tag: _tag.Tag | None = None,
    *,
    language: str = "english",
)

NEW:

abjad.Score(
    components=None,
    *,
    language: str = "english",
    lilypond_type: str = "Score",
    name: str | None = None,
    simultaneous: bool = True,
    tag: _tag.Tag | None = None,
)

abjad.Staff(
    components=None,
    *,
    language: str = "english",
    lilypond_type: str = "Staff",
    name: str | None = None,
    simultaneous: bool = False,
    tag: _tag.Tag | None = None,
)

abjad.StaffGroup(
    components=None,
    *,
    language: str = "english",
    lilypond_type: str = "StaffGroup",
    name: str | None = None,
    simultaneous: bool = True,
    tag: _tag.Tag | None = None,
)

abjad.Voice(
    components=None,
    *,
    language: str = "english",
    lilypond_type: str = "Voice",
    name: str | None = None,
    simultaneous: bool = False,
    tag: _tag.Tag | None = None,
)

#1523. CHANGED. Container input must now be flat:

OLD. Nested lists of components were allowed as container input through Abjad 3.16:

abjad.Staff(
    [abjad.Note("c'4"), [abjad.Note("d'4")]]
)

NEW. Starting in Abjad 3.17, container input must be a flat list of components:

abjad.Staff(
    [abjad.Note("c'4"), abjad.Note("d'4")]
)

#1525 CHANGED abjad.illustrators.selection() to abjad.illustrators.components():

OLD: abjad.illustrators.selection()
NEW: abjad.illustrators.components()

OLD: abjad.illustrators.selection_to_score_markup_string()
NEW: abjad.illustrators.components_to_score_markup_string()

#1527. Cleaned up abjad.on_beat_grace_container():

CHANGED. Changed abjad.on_beat_grace_container() parameter names:

OLD: abjad.on_beat_grace_container(..., anchor_voice_number=2)
NEW: abjad.on_beat_grace_container(..., nongrace_polyphony_command=r"\voiceTwo")

OLD: abjad.on_beat_grace_container(..., grace_voice_number=1)
NEW: abjad.on_beat_grace_container(..., grace_polyphony_command=r"\voiceOne")

OLD: abjad.on_beat_grace_container(..., font_size=-3)
NEW: abjad.on_beat_grace_container(..., grace_font_size=-3)

OLD: abjad.on_beat_grace_container(..., leaf_duration=None)
NEW: abjad.on_beat_grace_container(..., grace_leaf_duration=None)

CHANGED. Changed abjad.activate(), abjad.deactivate() return types:

OLD:
    * both functions returned (text, count) pair when skipped=False
    * both functions returned (text, count, skipped) triple when skipped=True

NEW:
    * both functions return (text, count, skipped) triple

NEW. Added abjad.parse(..., tag=None) keyword.

NEW. Added abjad.wf.check_overlapping_beams().

NEW. Added abjad.wf.check_beamed_lone_notes().

BUGFIX. Taught abjad.ForbidUpdate to update indicators on entrance. Indicators need to be updated after swap; context manager updates indicators before forbidding further updates:

>>> staff = abjad.Staff(r"\times 1/1 { c'4 d' }")
>>> abjad.attach(abjad.Clef("alto"), staff[0][0])
>>> container = abjad.Container()
>>> abjad.mutate.swap(staff[0], container)
>>> with abjad.ForbidUpdate(staff):
...     for note in staff[0]:
...         print(note)
...         print(abjad.get.effective(note, abjad.Clef))
...
Note("c'4")
Clef(name='alto', hide=False)
Note("d'4")
Clef(name='alto', hide=False)

Users encountered this bug (up to Abjad 3.16) only if using abjad.ForbidContext immediately after some type of score mutatation, like in the example above.

#1528. NEW. Added abjad.VoiceNumber indicator:

Set n to 1, 2, 3, 4 or none. Models LilyPond \voiceOne, \voiceTwo, \voiceThree, \voiceFour, \oneVoice commands.

def make_staff():
    staff = abjad.Staff()
    voice_1 = abjad.Voice("g'8 a' b' c''")
    command = abjad.VoiceNumber(n=1)
    abjad.attach(command, voice_1[0])
    voice_2 = abjad.Voice("e'8 f' g' a'")
    command = abjad.VoiceNumber(n=2)
    abjad.attach(command, voice_2[0])
    container = abjad.Container([voice_1, voice_2], simultaneous=True)
    staff.append(container)
    voice = abjad.Voice("c''4 a'")
    command = abjad.VoiceNumber()
    abjad.attach(command, voice[0])
    staff.append(voice)

>>> staff = make_staff()
>>> string = abjad.lilypond(staff)
>>> print(string)
\new Staff
{
    <<
        \new Voice
        {
            \voiceOne
            g'8
            a'8
            b'8
            c''8
        }
        \new Voice
        {
            \voiceTwo
            e'8
            f'8
            g'8
            a'8
        }
    >>
    \new Voice
    {
        \oneVoice
        c''4
        a'4
    }
}

The abjad.VoiceNumber indicator is contexted at the level of abjad.Voice. Use abjad.get.effective() to get the abjad.VoiceNumber indicator in effect for any leaf:

>>> for leaf in abjad.select.leaves(staff):
...     command = abjad.get.effective(leaf, abjad.VoiceNumber)
...     print(f"{leaf}, {command}")
Note("g'8"), VoiceNumber(n=1)
Note("a'8"), VoiceNumber(n=1)
Note("b'8"), VoiceNumber(n=1)
Note("c''8"), VoiceNumber(n=1)
Note("e'8"), VoiceNumber(n=2)
Note("f'8"), VoiceNumber(n=2)
Note("g'8"), VoiceNumber(n=2)
Note("a'8"), VoiceNumber(n=2)
Note("c''4"), VoiceNumber(n=None)
Note("a'4"), VoiceNumber(n=None)

#1529. CHANGED abjad.LilyPondLiteral format site from "opening" to "before":

Abjad leaves should only format "before" and "after" sites. (Though Abjad containers can format "before", "after", "opening" and "closing" sites.) Because abjad.LilyPondLiteral objects almost always get attached to leaves (instead of containers), it makes sense to change the default format site of LilyPond literals from (container-like) "opening" to (leaf-like) "before".

#1534. CHANGED abjad.TimeSignature context from "Staff" to "Score":

Through Abjad 3.16, time signature application was most commonly abjad.attach(time_signature, note, context="Score").

Beginning in Abjad 3.17, abjad.attach(time_signature, note) binds to the score context automatically.

#1537. Made abjad.TimeSignature, abjad.MetronomeMark initializers strict. That is, these initializers no longer coerce input:

Intialize metronome marks like this:

OLD:

    * abjad.MetronomeMark(abjad.Duration(1, 4), 72)
    * abjad.MetronomeMark((1, 4), 72)

NEW:

    * abjad.MetronomeMark(abjad.Duration(1, 4), 72)

Initialize time signatures like this:

OLD:

    * abjad.TimeSignature(pair)
    * abjad.TimeSignature(duration)
    * abjad.TimeSignature(time_signature)

NEW:

    * abjad.TimeSignature(pair)

CHANGED. Moved LilyPond stop-hairpin \! from articulations site to spanner-stops site in LilyPond output.

#1540. Cleaned up indicators:

CHANGED. The "site" argument to abjad.LilyPondLiteral is now keyword-only:

OLD: abjad.LilyPondLiteral(..., site)
NEW: abjad.LilyPondLiteral(..., *, site="before")

CHANGED. Made abjad.MetricModulation into frozen dataclass.

CHANGED. Renamed abjad.StaffChange parameter:

OLD: abjad.StaffChange.staff
NEW: abjad.StaffChange.staff_name

REMOVED remove_ly keyword from these:

* abjad.persist.as_midi()
* abjad.persist.as_pdf()
* abjad.persist.as_png()

#1542. Fixed abjad.beam(..., stemlet_length=None) formatting:

EXAMPLE.

    >>> voice = abjad.Voice("c'8 d' e' f'")
    >>> abjad.beam(voice[:], stemlet_length=0.75)
    >>> string = abjad.lilypond(voice)

OLD:

    >>> print(string)
    \new Voice
    {
        \override Staff.Stem.stemlet-length = 0.75
        c'8
        [
        d'8
        e'8
        \revert Staff.Stem.stemlet-length
        f'8
        ]
    }

NEW:

    >>> print(string)
    \new Voice
    {
        \override Staff.Stem.stemlet-length = 0.75
        c'8
        [
        d'8
        e'8
        f'8
        ]
        \revert Staff.Stem.stemlet-length
    }

CHANGED. abjad.illustrators.make_piano_score() now includes explicit voices:

OLD:

    >>> score = abjad.illustrators.make_piano_score()
    >>> string = abjad.lilypond(score)
    >>> print(string)
    \context Score = "Score"
    <<
        \context PianoStaff = "Piano_Staff"
        <<
            \context Staff = "Treble_Staff"
            {
            }
            \context Staff = "Bass_Staff"
            {
            }
        >>
    >>

NEW:

    >>> score = abjad.illustrators.make_piano_score()
    >>> string = abjad.lilypond(score)
    >>> print(string)
    \context Score = "Score"
    <<
        \context PianoStaff = "Piano_Staff"
        <<
            \context Staff = "Treble_Staff"
            {
                \context Voice = "Treble_Voice"
                {
                }
            }
            \context Staff = "Bass_Staff"
            {
                \context Voice = "Bass_Voice"
                {
                }
            }
        >>
    >>

#1543. Added an explicit abjad.Voice context to the output created by abjad.illustrators.components():

EXAMPLE:

def main():
    components = [abjad.Note("c'4")]
    lilypond_file = abjad.illustrators.components(components)
    string = abjad.lilypond(lilypond_file["Score"])
    print(string)

OLD:

\context Score = "Score"
<<
    \context Staff = "Staff"
    {
        \time 1/4
        c'4
    }
>>

NEW:

\context Score = "Score"
<<
    \context Staff = "Staff"
    {
        \context Voice = "Voice"
        {
            \time 1/4
            c'4
        }
    }
>>