Source code for abjad.makers

import collections
import fractions
import math
import numbers

from . import _getlib
from . import duration as _duration
from . import exceptions as _exceptions
from . import math as _math
from . import pitch as _pitch
from . import score as _score
from . import sequence as _sequence
from . import spanners as _spanners
from . import tag as _tag
from . import typings as _typings


def _group_by_implied_prolation(durations):
    pairs = []
    for duration in durations:
        if isinstance(duration, tuple):
            pairs.append(duration)
        else:
            pairs.append(duration.pair)
    durations = pairs
    assert 0 < len(durations)
    group = [durations[0]]
    result = [group]
    for d in durations[1:]:
        d_f = set(_math.factors(d[1]))
        d_f.discard(2)
        gd_f = set(_math.factors(group[0][1]))
        gd_f.discard(2)
        if d_f == gd_f:
            group.append(d)
        else:
            group = [d]
            result.append(group)
    return result


def _make_leaf_on_pitch(
    pitch,
    duration,
    *,
    increase_monotonic=None,
    forbidden_note_duration=None,
    forbidden_rest_duration=None,
    skips_instead_of_rests=None,
    tag=None,
    use_multimeasure_rests=None,
):
    note_prototype = (
        numbers.Number,
        str,
        _pitch.NamedPitch,
        _pitch.NumberedPitch,
        _pitch.PitchClass,
    )
    chord_prototype = (tuple, list)
    rest_prototype = (type(None),)
    if isinstance(pitch, note_prototype):
        leaves = _make_tied_leaf(
            _score.Note,
            duration,
            increase_monotonic=increase_monotonic,
            forbidden_duration=forbidden_note_duration,
            pitches=pitch,
            tag=tag,
        )
    elif isinstance(pitch, chord_prototype):
        leaves = _make_tied_leaf(
            _score.Chord,
            duration,
            increase_monotonic=increase_monotonic,
            forbidden_duration=forbidden_note_duration,
            pitches=pitch,
            tag=tag,
        )
    elif isinstance(pitch, rest_prototype) and skips_instead_of_rests:
        leaves = _make_tied_leaf(
            _score.Skip,
            duration,
            increase_monotonic=increase_monotonic,
            forbidden_duration=forbidden_rest_duration,
            pitches=None,
            tag=tag,
        )
    elif isinstance(pitch, rest_prototype) and not use_multimeasure_rests:
        leaves = _make_tied_leaf(
            _score.Rest,
            duration,
            increase_monotonic=increase_monotonic,
            forbidden_duration=forbidden_rest_duration,
            pitches=None,
            tag=tag,
        )
    elif isinstance(pitch, rest_prototype) and use_multimeasure_rests:
        multimeasure_rest = _score.MultimeasureRest((1), tag=tag)
        multimeasure_rest.multiplier = duration
        leaves = (multimeasure_rest,)
    else:
        raise ValueError(f"unknown pitch: {pitch!r}.")
    return leaves


def _make_tied_leaf(
    class_,
    duration,
    increase_monotonic=None,
    forbidden_duration=None,
    multiplier=None,
    pitches=None,
    tag=None,
    tie_parts=True,
):
    duration = _duration.Duration(duration)
    duration_pair = duration.pair
    if forbidden_duration is not None:
        assert forbidden_duration.is_assignable
        assert forbidden_duration.numerator == 1
    # find preferred numerator of written durations if necessary
    if forbidden_duration is not None and forbidden_duration <= fractions.Fraction(
        *duration_pair
    ):
        denominators = [
            2 * forbidden_duration.denominator,
            duration_pair[1],
        ]
        denominator = _math.least_common_multiple(*denominators)
        pair = _duration.with_denominator(forbidden_duration, denominator)
        forbidden_numerator = pair[0]
        assert forbidden_numerator % 2 == 0
        preferred_numerator = forbidden_numerator / 2
        pair = _duration.with_denominator(duration_pair, denominator)
        duration_pair = pair
    # make written duration numerators
    numerators = []
    parts = _math.partition_integer_into_canonic_parts(duration_pair[0])
    if forbidden_duration is not None and fractions.Fraction(
        forbidden_duration
    ) <= fractions.Fraction(*duration_pair):
        for part in parts:
            if forbidden_numerator <= part:
                better_parts = _partition_less_than_double(part, preferred_numerator)
                numerators.extend(better_parts)
            else:
                numerators.append(part)
    else:
        numerators = parts
    # reverse numerators if necessary
    if increase_monotonic:
        numerators = list(reversed(numerators))
    # make one leaf per written duration
    result = []
    for numerator in numerators:
        written_duration = _duration.Duration(numerator, duration_pair[1])
        if pitches is not None:
            arguments = (pitches, written_duration)
        else:
            arguments = (written_duration,)
        result.append(class_(*arguments, multiplier=multiplier, tag=tag))
    # tie if required
    if tie_parts and 1 < len(result):
        if not issubclass(class_, (_score.Rest, _score.Skip)):
            _spanners.tie(result)
    return result


def _make_unprolated_notes(pitches, durations, increase_monotonic=None, tag=None):
    assert len(pitches) == len(durations)
    result = []
    for pitch, duration in zip(pitches, durations):
        result.extend(
            _make_tied_leaf(
                _score.Note,
                duration,
                pitches=pitch,
                increase_monotonic=increase_monotonic,
                tag=tag,
            )
        )
    return result


def _partition_less_than_double(n, m):
    assert _math.is_positive_integer_equivalent_number(n)
    assert _math.is_positive_integer_equivalent_number(m)
    n, m = int(n), int(m)
    result = []
    current_value = n
    double_m = 2 * m
    while double_m <= current_value:
        result.append(m)
        current_value -= m
    result.append(current_value)
    return tuple(result)


[docs]def make_leaves( pitches, durations, *, forbidden_note_duration: _typings.Duration | None = None, forbidden_rest_duration: _typings.Duration | None = None, skips_instead_of_rests: bool = False, increase_monotonic: bool = False, tag: _tag.Tag | None = None, use_multimeasure_rests: bool = False, ): r""" Makes leaves from ``pitches`` and ``durations``. .. container:: example Integer and string elements in ``pitches`` result in notes: >>> pitches = [2, 4, "F#5", "G#5"] >>> duration = abjad.Duration(1, 4) >>> leaves = abjad.makers.make_leaves(pitches, duration) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { d'4 e'4 fs''4 gs''4 } .. container:: example Tuple elements in ``pitches`` result in chords: >>> pitches = [(0, 2, 4), ("F#5", "G#5", "A#5")] >>> duration = abjad.Duration(1, 2) >>> leaves = abjad.makers.make_leaves(pitches, duration) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { <c' d' e'>2 <fs'' gs'' as''>2 } .. container:: example None-valued elements in ``pitches`` result in rests: >>> pitches = 4 * [None] >>> durations = [abjad.Duration(1, 4)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves, lilypond_type="RhythmicStaff") >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new RhythmicStaff { r4 r4 r4 r4 } .. container:: example You can mix and match values passed to ``pitches``: >>> pitches = [(0, 2, 4), None, "C#5", "D#5"] >>> durations = [abjad.Duration(1, 4)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { <c' d' e'>4 r4 cs''4 ds''4 } .. container:: example Works with segments: >>> pitches = "e'' ef'' d'' df'' c''" >>> durations = [abjad.Duration(1, 4)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { e''4 ef''4 d''4 df''4 c''4 } .. container:: example Reads ``pitches`` cyclically when the length of ``pitches`` is less than the length of ``durations``: >>> pitches = ["C5"] >>> durations = 2 * [abjad.Duration(3, 8), abjad.Duration(1, 8)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c''4. c''8 c''4. c''8 } .. container:: example Reads ``durations`` cyclically when the length of ``durations`` is less than the length of ``pitches``: >>> pitches = "c'' d'' e'' f''" >>> durations = [abjad.Duration(1, 4)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c''4 d''4 e''4 f''4 } .. container:: example Elements in ``durations`` with non-power-of-two denominators result in tuplet-nested leaves: >>> pitches = ["D5"] >>> durations = 3 * [abjad.Duration(1, 3)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \times 2/3 { d''2 d''2 d''2 } } .. container:: example Set ``increase_monotonic`` to false to return nonassignable durations tied from greatest to least: >>> pitches = ["D#5"] >>> durations = [abjad.Duration(13, 16)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> time_signature = abjad.TimeSignature((13, 16)) >>> abjad.attach(time_signature, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \time 13/16 ds''2. ~ ds''16 } .. container:: example Set ``increase_monotonic`` to true to return nonassignable durations tied from least to greatest: >>> pitches = ["E5"] >>> durations = [abjad.Duration(13, 16)] >>> leaves = abjad.makers.make_leaves(pitches, durations, increase_monotonic=True) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> time_signature = abjad.TimeSignature((13, 16)) >>> abjad.attach(time_signature, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \time 13/16 e''16 ~ e''2. } .. container:: example Set ``forbidden_note_duration`` to avoid notes greater than or equal to a certain written duration: >>> pitches = "f' g'" >>> durations = [abjad.Duration(5, 8)] >>> leaves = abjad.makers.make_leaves(pitches, durations, ... forbidden_note_duration=abjad.Duration(1, 2)) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> time_signature = abjad.TimeSignature((5, 4)) >>> abjad.attach(time_signature, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \time 5/4 f'4 ~ f'4 ~ f'8 g'4 ~ g'4 ~ g'8 } .. container:: example You may set ``forbidden_note_duration`` and ``increase_monotonic`` together: >>> pitches = "f' g'" >>> durations = [abjad.Duration(5, 8)] >>> leaves = abjad.makers.make_leaves( ... pitches, durations, ... forbidden_note_duration=abjad.Duration(1, 2), ... increase_monotonic=True, ... ) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> time_signature = abjad.TimeSignature((5, 4)) >>> abjad.attach(time_signature, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \time 5/4 f'8 ~ f'4 ~ f'4 g'8 ~ g'4 ~ g'4 } .. container:: example Produces diminished tuplets: >>> pitches = "f'" >>> durations = [abjad.Duration(5, 14)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> time_signature = abjad.TimeSignature((5, 14)) >>> leaf = abjad.get.leaf(staff, 0) >>> abjad.attach(time_signature, leaf) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { \tweak edge-height #'(0.7 . 0) \times 8/14 { #(ly:expect-warning "strange time signature found") \time 5/14 f'2 ~ f'8 } } This is default behavior. .. container:: example None-valued elements in ``pitches`` result in multimeasure rests when the multimeasure rest keyword is set: >>> pitches = [None] >>> durations = [abjad.Duration(3, 8), abjad.Duration(5, 8)] >>> leaves = abjad.makers.make_leaves(pitches, durations, ... use_multimeasure_rests=True) >>> leaves [MultimeasureRest('R1 * 3/8'), MultimeasureRest('R1 * 5/8')] >>> staff = abjad.Staff(leaves, lilypond_type="RhythmicStaff") >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 8)), leaves[0]) >>> abjad.attach(abjad.TimeSignature((5, 8)), leaves[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new RhythmicStaff { \time 3/8 R1 * 3/8 \time 5/8 R1 * 5/8 } .. container:: example Works with numbered pitch-class: >>> pitches = [abjad.NumberedPitchClass(6)] >>> durations = [abjad.Duration(13, 16)] >>> leaves = abjad.makers.make_leaves(pitches, durations) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { fs'2. ~ fs'16 } .. container:: example Makes skips instead of rests: >>> pitches = [None] >>> durations = [abjad.Duration(13, 16)] >>> abjad.makers.make_leaves(pitches, durations, skips_instead_of_rests=True) [Skip('s2.'), Skip('s16')] .. container:: example Integer and string elements in ``pitches`` result in notes: >>> tag = abjad.Tag("leaf_maker") >>> pitches = [2, 4, "F#5", "G#5"] >>> duration = abjad.Duration(1, 4) >>> leaves = abjad.makers.make_leaves(pitches, duration, tag=tag) >>> staff = abjad.Staff(leaves) >>> score = abjad.Score([staff], name="Score") >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff, tags=True) >>> print(string) \new Staff { %! leaf_maker d'4 %! leaf_maker e'4 %! leaf_maker fs''4 %! leaf_maker gs''4 } """ if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, collections.abc.Iterable): pitches = [pitches] if isinstance(durations, numbers.Number | tuple): durations = [durations] if forbidden_note_duration is not None: forbidden_note_duration = _duration.Duration(forbidden_note_duration) if forbidden_rest_duration is not None: forbidden_rest_duration = _duration.Duration(forbidden_rest_duration) nonreduced_fractions = [_duration.Duration(_) for _ in durations] size = max(len(nonreduced_fractions), len(pitches)) nonreduced_fractions = _sequence.repeat_to_length(nonreduced_fractions, size) pitches = _sequence.repeat_to_length(pitches, size) duration_groups = _group_by_implied_prolation(nonreduced_fractions) result: list[_score.Tuplet | _score.Leaf] = [] for duration_group in duration_groups: factors_ = _math.factors(duration_group[0][1]) factors = set(factors_) factors.discard(1) factors.discard(2) current_pitches = pitches[0 : len(duration_group)] pitches = pitches[len(duration_group) :] if len(factors) == 0: for pitch, duration in zip(current_pitches, duration_group): leaves = _make_leaf_on_pitch( pitch, duration, increase_monotonic=increase_monotonic, forbidden_note_duration=forbidden_note_duration, forbidden_rest_duration=forbidden_rest_duration, skips_instead_of_rests=skips_instead_of_rests, tag=tag, use_multimeasure_rests=use_multimeasure_rests, ) result.extend(leaves) else: denominator = duration_group[0][1] numerator = _math.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / _duration.Duration(*multiplier) duration_group = [ ratio * _duration.Duration(duration) for duration in duration_group ] tuplet_leaves: list[_score.Leaf] = [] for pitch, duration in zip(current_pitches, duration_group): leaves = _make_leaf_on_pitch( pitch, duration, increase_monotonic=increase_monotonic, skips_instead_of_rests=skips_instead_of_rests, tag=tag, use_multimeasure_rests=use_multimeasure_rests, ) tuplet_leaves.extend(leaves) tuplet = _score.Tuplet(multiplier, tuplet_leaves) result.append(tuplet) return result
[docs]def make_notes( pitches, durations, *, increase_monotonic: bool = False, tag: _tag.Tag | None = None ) -> list[_score.Note | _score.Tuplet]: r""" Makes notes from ``pitches`` and ``durations``. Set ``pitches`` to a single pitch or a sequence of pitches. Set ``durations`` to a single duration or a list of durations. Cycles through ``pitches`` when the length of ``pitches`` is less than the length of ``durations``: .. container:: example >>> pitches = [0] >>> durations = [(1, 16), (1, 8), (1, 8)] >>> notes = abjad.makers.make_notes(pitches, durations) >>> staff = abjad.Staff(notes) >>> score = abjad.Score([staff], name="Score") >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c'16 c'8 c'8 } .. container:: example Cycles through ``durations`` when the length of ``durations`` is less than the length of ``pitches``: >>> pitches = [0, 2, 4, 5, 7] >>> durations = [(1, 16), (1, 8), (1, 8)] >>> notes = abjad.makers.make_notes(pitches, durations) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c'16 d'8 e'8 f'16 g'8 } .. container:: example Creates ad hoc tuplets for nonassignable durations: >>> pitches = [0] >>> durations = [(1, 16), (1, 12), (1, 8)] >>> notes = abjad.makers.make_notes(pitches, durations) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c'16 \tweak edge-height #'(0.7 . 0) \times 2/3 { c'8 } c'8 } .. container:: example Tied values are written in decreasing duration by default: >>> pitches = [0] >>> durations = [(13, 16)] >>> notes = abjad.makers.make_notes(pitches, durations) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c'2. ~ c'16 } Set ``increase_monotonic=True`` to express tied values in increasing duration: >>> pitches = [0] >>> durations = [(13, 16)] >>> notes = abjad.makers.make_notes(pitches, durations, increase_monotonic=True) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { c'16 ~ c'2. } .. container:: example Works with pitch segments: >>> pitches = abjad.PitchSegment([-2, -1.5, 6, 7, -1.5, 7]) >>> durations = [(1, 8)] >>> notes = abjad.makers.make_notes(pitches, durations) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff) >>> print(string) \new Staff { bf8 bqf8 fs'8 g'8 bqf8 g'8 } .. container:: example Tag output like this: >>> pitches = [0] >>> durations = [(1, 16), (1, 8), (1, 8)] >>> tag = abjad.Tag("note_maker") >>> notes = abjad.makers.make_notes(pitches, durations, tag=tag) >>> staff = abjad.Staff(notes) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff, tags=True) >>> print(string) \new Staff { %! note_maker c'16 %! note_maker c'8 %! note_maker c'8 } """ if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, collections.abc.Iterable): pitches = [pitches] if isinstance(durations, numbers.Number | tuple): durations = [durations] pairs = [] for duration in durations: if isinstance(duration, tuple): pairs.append(duration) elif isinstance(duration, int): pair = (duration, 1) pairs.append(pair) else: pairs.append(duration.pair) size = max(len(pairs), len(pitches)) pairs = _sequence.repeat_to_length(pairs, size) pitches = _sequence.repeat_to_length(pitches, size) durations = _group_by_implied_prolation(pairs) result: list[_score.Note | _score.Tuplet] = [] for duration in durations: factors = set(_math.factors(duration[0][1])) factors.discard(1) factors.discard(2) ps = pitches[0 : len(duration)] pitches = pitches[len(duration) :] if len(factors) == 0: result.extend( _make_unprolated_notes( ps, duration, increase_monotonic=increase_monotonic, tag=tag, ) ) else: denominator = duration[0][1] numerator = _math.greatest_power_of_two_less_equal(denominator) multiplier = _duration.Duration(numerator, denominator) ratio = multiplier.reciprocal duration = [ratio * _duration.Duration(d) for d in duration] ns = _make_unprolated_notes( ps, duration, increase_monotonic=increase_monotonic, tag=tag, ) tuplet = _score.Tuplet(multiplier.pair, ns) result.append(tuplet) return result
[docs]def tuplet_from_duration_and_ratio( duration, ratio: tuple[int, ...], *, increase_monotonic: bool = False, tag: _tag.Tag | None = None, ) -> _score.Tuplet: r""" Makes tuplet from ``duration`` and ``ratio``. Makes tupletted leaves strictly without dots when all ``ratio`` equal ``1``: .. container:: example >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, 1, 1, -1, -1), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 4/5 { \time 3/16 c'32. c'32. c'32. r32. r32. } Allows tupletted leaves to return with dots when some ``ratio`` do not equal ``1``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, -2, -2, 3, 3), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 6/11 { \time 3/16 c'32 r16 r16 c'16. c'16. } Interprets nonassignable ``ratio`` according to ``increase_monotonic``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (5, -1, 5), ... increase_monotonic=True, ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 8/11 { \time 3/16 c'16... r64. c'16... } .. container:: example Makes augmented tuplet from ``duration`` and ``ratio`` and encourages dots: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, 1, 1, -1, -1), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 4/5 { \time 3/16 c'32. c'32. c'32. r32. r32. } Interprets nonassignable ``ratio`` according to ``increase_monotonic``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (5, -1, 5), ... increase_monotonic=True, ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 8/11 { \time 3/16 c'16... r64. c'16... } .. container:: example Makes diminished tuplet from ``duration`` and nonzero integer ``ratio``. Makes tupletted leaves strictly without dots when all ``ratio`` equal ``1``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, 1, 1, -1, -1), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 4/5 { \time 3/16 c'32. c'32. c'32. r32. r32. } Allows tupletted leaves to return with dots when some ``ratio`` do not equal ``1``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, -2, -2, 3, 3), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 6/11 { \time 3/16 c'32 r16 r16 c'16. c'16. } Interprets nonassignable ``ratio`` according to ``increase_monotonic``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (5, -1, 5), ... increase_monotonic=True, ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 8/11 { \time 3/16 c'16... r64. c'16... } .. container:: example Makes diminished tuplet from ``duration`` and ``ratio`` and encourages dots: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (1, 1, 1, -1, -1), ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 4/5 { \time 3/16 c'32. c'32. c'32. r32. r32. } Interprets nonassignable ``ratio`` according to ``direction``: >>> tuplet = abjad.makers.tuplet_from_duration_and_ratio( ... abjad.Duration(3, 16), ... (5, -1, 5), ... increase_monotonic=True, ... ) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 8/11 { \time 3/16 c'16... r64. c'16... } Reduces ``ratio`` relative to each other. Interprets negative ``ratio`` as rests. """ duration = _duration.Duration(duration) assert isinstance(ratio, tuple), repr(ratio) basic_prolated_duration = duration / _math.weight(ratio) basic_written_duration = basic_prolated_duration.equal_or_greater_assignable written_durations = [x * basic_written_duration for x in ratio] notes: list[_score.Leaf | _score.Tuplet] try: notes = [ _score.Note(0, x, tag=tag) if 0 < x else _score.Rest(abs(x), tag=tag) for x in written_durations ] except _exceptions.AssignabilityError: denominator = duration.denominator note_durations = [_duration.Duration(x, denominator) for x in ratio] pitches = [None if note_duration < 0 else 0 for note_duration in note_durations] leaf_durations = [abs(note_duration) for note_duration in note_durations] notes = make_leaves( pitches, leaf_durations, increase_monotonic=increase_monotonic, tag=tag, ) tuplet = _score.Tuplet.from_duration(duration, notes, tag=tag) tuplet.normalize_multiplier() return tuplet
[docs]def tuplet_from_leaf_and_ratio( leaf: _score.Leaf, ratio: tuple[int, ...] ) -> _score.Tuplet: r""" Makes tuplet from ``leaf`` and ``ratio``. >>> note = abjad.Note("c'8.") .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1,)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 3/16 c'8. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 3/16 c'16 c'8 } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \times 4/5 { \time 3/16 c'32. c'16. c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 3/4 { \time 3/16 c'32 c'16 c'16 c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3, 3)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 6/11 { \time 3/16 c'32 c'16 c'16 c'16. c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3, 3, 4)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \times 4/5 { \time 3/16 c'64 c'32 c'32 c'32. c'32. c'16 } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1,)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 3/16 c'8. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 3/16 c'16 c'8 } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \times 4/5 { \time 3/16 c'32. c'16. c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 3/4 { \time 3/16 c'32 c'16 c'16 c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3, 3)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 6/11 { \time 3/16 c'32 c'16 c'16 c'16. c'16. } .. container:: example >>> tuplet = abjad.makers.tuplet_from_leaf_and_ratio(note, (1, 2, 2, 3, 3, 4)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((3, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(tuplet) >>> print(string) \times 4/5 { \time 3/16 c'64 c'32 c'32 c'32. c'32. c'16 } """ assert isinstance(ratio, tuple), repr(ratio) target_duration = leaf.written_duration basic_prolated_duration = target_duration / sum(ratio) basic_written_duration = basic_prolated_duration.equal_or_greater_assignable written_durations = [_ * basic_written_duration for _ in ratio] notes: list[_score.Note | _score.Tuplet] try: notes = [_score.Note(0, x) for x in written_durations] except _exceptions.AssignabilityError: denominator = target_duration.denominator note_durations = [_duration.Duration(_, denominator) for _ in ratio] notes = make_notes(0, note_durations) contents_duration = _getlib._get_duration(notes) multiplier = target_duration / contents_duration tuplet = _score.Tuplet(_duration.pair(multiplier), notes) tuplet.normalize_multiplier() return tuplet
[docs]def tuplet_from_ratio_and_pair( ratio: tuple, fraction: tuple, *, tag: _tag.Tag | None = None, ) -> _score.Tuplet: r""" Makes tuplet from nonreduced ``ratio`` and nonreduced ``fraction``. .. container:: example >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1,), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 7/16 c'4.. } >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1, 2), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 7/6 { \time 7/16 c'8 c'4 } >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1, 2, 4), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 1/1 { \time 7/16 c'16 c'8 c'4 } >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1, 2, 4, 1), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 7/8 { \time 7/16 c'16 c'8 c'4 c'16 } >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1, 2, 4, 1, 2), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 7/10 { \time 7/16 c'16 c'8 c'4 c'16 c'8 } >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((1, 2, 4, 1, 2, 4), (7, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \times 1/2 { \time 7/16 c'16 c'8 c'4 c'16 c'8 c'4 } Works with nonassignable rests: >>> tuplet = abjad.makers.tuplet_from_ratio_and_pair((11, -5), (5, 16)) >>> staff = abjad.Staff( ... [tuplet], ... lilypond_type="RhythmicStaff", ... ) >>> score = abjad.Score([staff], name="Score") >>> abjad.attach(abjad.TimeSignature((7, 16)), tuplet[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> string = abjad.lilypond(staff[0]) >>> print(string) \tweak text #tuplet-number::calc-fraction-text \times 5/8 { \time 7/16 c'4 ~ c'16. r8 r32 } Interprets ``d`` as tuplet denominator. """ if not isinstance(ratio, tuple): raise ValueError(f"must be tuple, not {ratio!r}.") if not isinstance(fraction, tuple): raise ValueError(f"must be pair, not {fraction!r}.") numerator, denominator = fraction duration = _duration.Duration(fraction) if len(ratio) == 1: if 0 < ratio[0]: try: note = _score.Note(0, duration, tag=tag) duration = note._get_duration() tuplet = _score.Tuplet.from_duration(duration, [note], tag=tag) return tuplet except _exceptions.AssignabilityError: notes = make_notes(0, duration, tag=tag) duration = _getlib._get_duration(notes) return _score.Tuplet.from_duration(duration, notes, tag=tag) elif ratio[0] < 0: try: rest = _score.Rest(duration, tag=tag) duration = rest._get_duration() return _score.Tuplet.from_duration(duration, [rest], tag=tag) except _exceptions.AssignabilityError: rests = make_leaves([None], duration, tag=tag) duration = _getlib._get_duration(rests) return _score.Tuplet.from_duration(duration, rests, tag=tag) else: raise ValueError("no divide zero values.") else: exponent = int(math.log(_math.weight(ratio), 2) - math.log(numerator, 2)) denominator = int(denominator * 2**exponent) components: list[_score.Leaf | _score.Tuplet] = [] for x in ratio: if not x: raise ValueError("no divide zero values.") if 0 < x: try: note = _score.Note(0, (x, denominator), tag=tag) components.append(note) except _exceptions.AssignabilityError: notes = make_notes(0, (x, denominator), tag=tag) components.extend(notes) else: try: rest = _score.Rest((-x, denominator), tag=tag) components.append(rest) except _exceptions.AssignabilityError: rests = make_leaves(None, (-x, denominator), tag=tag) components.extend(rests) return _score.Tuplet.from_duration(duration, components, tag=tag)