def _duration_and_possible_denominators_to_time_signature( duration, denominators=None, factor=None, ): # check input duration = durationtools.Duration(duration) if denominators is not None: if factor is not None: denominators = [ d for d in denominators if factor in mathtools.factors(d) ] for desired_denominator in sorted(denominators): nonreduced_fraction = mathtools.NonreducedFraction(duration) candidate_pair = \ nonreduced_fraction.with_denominator(desired_denominator) if candidate_pair.denominator == desired_denominator: return indicatortools.TimeSignature(candidate_pair) if factor is not None: if factor in mathtools.factors(duration.denominator): return indicatortools.TimeSignature(duration) else: time_signature_numerator = factor * duration.numerator time_signature_denominator = factor * duration.denominator return indicatortools.TimeSignature( (time_signature_numerator, time_signature_denominator)) else: return indicatortools.TimeSignature(duration)
def _least_common_multiple_helper(m, n): from abjad.tools import mathtools # check input if not isinstance(m, int): raise TypeError if not isinstance(n, int): raise TypeError # find factors of m and n factors_m = mathtools.factors(m) factors_n = mathtools.factors(n) # remove duplicated shared factors for x in factors_m: try: factors_n.remove(x) except ValueError: pass # calculate product of shared factors result = 1 for x in factors_m + factors_n: result *= x # return product return result
def _least_common_multiple_helper(m, n): from abjad.tools import mathtools assert isinstance(m, int), repr(m) assert isinstance(n, int), repr(n) factors_m = mathtools.factors(m) factors_n = mathtools.factors(n) for x in factors_m: try: factors_n.remove(x) except ValueError: pass result = 1 for x in factors_m + factors_n: result *= x return result
def _group_nonreduced_fractions_by_implied_prolation(durations): durations = [ mathtools.NonreducedFraction(duration) for duration in durations ] assert 0 < len(durations) group = [durations[0]] result = [group] for d in durations[1:]: d_f = set(mathtools.factors(d.denominator)) d_f.discard(2) gd_f = set(mathtools.factors(group[0].denominator)) gd_f.discard(2) if d_f == gd_f: group.append(d) else: group = [d] result.append(group) return result
def is_dotted_integer(expr): """True when `expr` is equivalent to a positive integer and can be written with zero or more dots. :: >>> for expr in range(16): ... print '%s\t%s' % (expr, mathtools.is_dotted_integer(expr)) ... 0 False 1 False 2 False 3 True 4 False 5 False 6 True 7 True 8 False 9 False 10 False 11 False 12 True 13 False 14 True 15 True Otherwise false. Returns boolean. Integer `n` qualifies as dotted when ``abs(n)`` is of the form ``2**j * (2**k - 1)`` with integers ``0 <= j``, ``2 < k``. """ from abjad.tools import mathtools if expr == 0: return False non_two_product = 1 non_two_factors = [d for d in mathtools.factors(expr) if not d == 2] for non_two_factor in non_two_factors: non_two_product *= non_two_factor x = math.log(abs(non_two_product) + 1, 2) result = 1 < abs(non_two_product) and int(x) == x return result
def is_dotted_integer(expr): '''Is true when `expr` is equivalent to a positive integer and can be written with zero or more dots. :: >>> for expr in range(16): ... print('%s\t%s' % (expr, mathtools.is_dotted_integer(expr))) ... 0 False 1 False 2 False 3 True 4 False 5 False 6 True 7 True 8 False 9 False 10 False 11 False 12 True 13 False 14 True 15 True Otherwise false. Returns true or false. Integer `n` qualifies as dotted when ``abs(n)`` is of the form ``2**j * (2**k - 1)`` with integers ``0 <= j``, ``2 < k``. ''' from abjad.tools import mathtools if expr == 0: return False non_two_product = 1 non_two_factors = [d for d in mathtools.factors(expr) if not d == 2] for non_two_factor in non_two_factors: non_two_product *= non_two_factor x = math.log(abs(non_two_product) + 1, 2) result = 1 < abs(non_two_product) and int(x) == x return result
def are_relatively_prime(expr): '''Is true when `expr` is a sequence comprising zero or more numbers, all of which are relatively prime. :: >>> mathtools.are_relatively_prime([13, 14, 15]) True Otherwise false: :: >>> mathtools.are_relatively_prime([13, 14, 15, 16]) False Returns true when `expr` is an empty sequence: :: >>> mathtools.are_relatively_prime([]) True Returns false when `expr` is nonsensical type: :: >>> mathtools.are_relatively_prime('foo') False Returns boolean. ''' from abjad.tools import mathtools try: all_factors = set([]) for number in expr: current_factors = mathtools.factors(number) current_factors.remove(1) current_factors = set(current_factors) if all_factors & current_factors: return False all_factors.update(current_factors) return True # TODO: remove unqualified except except: return False
def are_relatively_prime(numbers_): '''Is true when `numbers_` is a sequence comprising zero or more numbers, all of which are relatively prime. :: >>> mathtools.are_relatively_prime([13, 14, 15]) True Otherwise false: :: >>> mathtools.are_relatively_prime([13, 14, 15, 16]) False Returns true when `numbers_` is an empty sequence: :: >>> mathtools.are_relatively_prime([]) True Returns false when `numbers_` is nonsensical type: :: >>> mathtools.are_relatively_prime('foo') False Returns boolean. ''' from abjad.tools import mathtools if not all(isinstance(_, numbers.Number) for _ in numbers_): return False all_factors = set([]) for number in numbers_: current_factors = mathtools.factors(number) current_factors = set(current_factors) if all_factors & current_factors: return False all_factors.update(current_factors) return True
def are_relatively_prime(argument): '''Is true when `argument` is an iterable collection of relative primes. Otherwise false. .. container:: example >>> abjad.mathtools.are_relatively_prime([13, 14, 15]) True >>> abjad.mathtools.are_relatively_prime([13, 14, 15, 16]) False >>> abjad.mathtools.are_relatively_prime('text') False .. container:: example Returns true when `argument` is empty: >>> abjad.mathtools.are_relatively_prime([]) True Returns true or false. ''' from abjad.tools import mathtools if not isinstance(argument, collections.Iterable): return False if not all(isinstance(_, numbers.Number) for _ in argument): return False all_factors = set([]) for number in argument: current_factors = mathtools.factors(number) current_factors = set(current_factors) if all_factors & current_factors: return False all_factors.update(current_factors) return True
def __init__( self, arg=None, decrease_durations_monotonically=True, preferred_boundary_depth=None, ): arg = arg or (4, 4) assert isinstance(preferred_boundary_depth, (int, type(None))) self._preferred_boundary_depth = preferred_boundary_depth def recurse( node, factors, denominator, decrease_durations_monotonically, ): if factors: factor, factors = factors[0], factors[1:] preprolated_duration = \ node.preprolated_duration.__div__(factor) #if factor in (2, 3, 4, 5): if factor in (2, 3, 4): if factors: for _ in range(factor): child = rhythmtreetools.RhythmTreeContainer( preprolated_duration=preprolated_duration) node.append(child) recurse( child, factors, denominator, decrease_durations_monotonically, ) else: for _ in range(factor): node.append( rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator))) else: parts = [3] total = 3 while total < factor: if decrease_durations_monotonically: parts.append(2) else: parts.insert(0, 2) total += 2 for part in parts: grouping = rhythmtreetools.RhythmTreeContainer( preprolated_duration=part * preprolated_duration) if factors: for _ in range(part): child = rhythmtreetools.RhythmTreeContainer( preprolated_duration=preprolated_duration) grouping.append(child) recurse( child, factors, denominator, decrease_durations_monotonically, ) else: for _ in range(part): grouping.append( rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator))) node.append(grouping) else: node.extend([ rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator)) for _ in range(node.preprolated_duration.numerator) ]) decrease_durations_monotonically = \ bool(decrease_durations_monotonically) if isinstance(arg, type(self)): root = arg.root_node numerator, denominator = arg.numerator, arg.denominator decrease_durations_monotonically = \ arg.decrease_durations_monotonically elif isinstance(arg, (str, rhythmtreetools.RhythmTreeContainer)): if isinstance(arg, str): parsed = rhythmtreetools.RhythmTreeParser()(arg) assert len(parsed) == 1 root = parsed[0] else: root = arg for node in root.nodes: assert node.prolation == 1 numerator = root.preprolated_duration.numerator denominator = root.preprolated_duration.denominator elif (isinstance(arg, (tuple, scoretools.Measure)) or (hasattr(arg, 'numerator') and hasattr(arg, 'denominator'))): if isinstance(arg, tuple): fraction = mathtools.NonreducedFraction(arg) elif isinstance(arg, scoretools.Measure): time_signature = arg._get_effective( indicatortools.TimeSignature) fraction = mathtools.NonreducedFraction( time_signature.numerator, time_signature.denominator) else: fraction = mathtools.NonreducedFraction( arg.numerator, arg.denominator) numerator, denominator = fraction.numerator, fraction.denominator factors = mathtools.factors(numerator) # group two nested levels of 2s into a 4 if 1 < len(factors) and factors[0] == factors[1] == 2: factors[0:2] = [4] root = rhythmtreetools.RhythmTreeContainer( preprolated_duration=fraction) recurse( root, factors, denominator, decrease_durations_monotonically, ) else: message = 'can not initialize {}: {!r}.' message = message.format(type(self).__name__, arg) raise ValueError(message) self._root_node = root self._numerator = numerator self._denominator = denominator self._decrease_durations_monotonically = \ decrease_durations_monotonically
def multiply_with_cross_cancelation(self, multiplier): '''Multiplies nonreduced fraction by `expr` with cross-cancelation. :: >>> fraction = mathtools.NonreducedFraction(4, 8) :: >>> fraction.multiply_with_cross_cancelation((2, 3)) NonreducedFraction(4, 12) :: >>> fraction.multiply_with_cross_cancelation((4, 1)) NonreducedFraction(4, 2) :: >>> fraction.multiply_with_cross_cancelation((3, 5)) NonreducedFraction(12, 40) :: >>> fraction.multiply_with_cross_cancelation((6, 5)) NonreducedFraction(12, 20) :: >>> fraction = mathtools.NonreducedFraction(5, 6) >>> fraction.multiply_with_cross_cancelation((6, 5)) NonreducedFraction(1, 1) Returns nonreduced fraction. ''' from abjad.tools import durationtools from abjad.tools import mathtools multiplier = durationtools.Multiplier(multiplier) self_numerator_factors = mathtools.factors(self.numerator) multiplier_denominator_factors = mathtools.factors( multiplier.denominator) for factor in multiplier_denominator_factors[:]: if factor in self_numerator_factors: self_numerator_factors.remove(factor) multiplier_denominator_factors.remove(factor) self_denominator_factors = mathtools.factors(self.denominator) multiplier_numerator_factors = mathtools.factors(multiplier.numerator) for factor in multiplier_numerator_factors[:]: if factor in self_denominator_factors: self_denominator_factors.remove(factor) multiplier_numerator_factors.remove(factor) result_numerator_factors = self_numerator_factors + \ multiplier_numerator_factors result_denominator_factors = self_denominator_factors + \ multiplier_denominator_factors result_numerator = 1 for factor in result_numerator_factors: result_numerator *= factor result_denominator = 1 for factor in result_denominator_factors: result_denominator *= factor return NonreducedFraction(result_numerator, result_denominator)
def test_mathtools_factors_04(): t = mathtools.factors(4) assert t == [1, 2, 2]
def make_notes(pitches, durations, decrease_durations_monotonically=True): r'''Make notes according to `pitches` and `durations`. Cycle through `pitches` when the length of `pitches` is less than the length of `durations`: :: >>> notetools.make_notes([0], [(1, 16), (1, 8), (1, 8)]) Selection(Note("c'16"), Note("c'8"), Note("c'8")) Cycle through `durations` when the length of `durations` is less than the length of `pitches`: :: >>> notetools.make_notes([0, 2, 4, 5, 7], [(1, 16), (1, 8), (1, 8)]) Selection(Note("c'16"), Note("d'8"), Note("e'8"), Note("f'16"), Note("g'8")) Create ad hoc tuplets for nonassignable durations: :: >>> notetools.make_notes([0], [(1, 16), (1, 12), (1, 8)]) Selection(Note("c'16"), Tuplet(2/3, [c'8]), Note("c'8")) Set ``decrease_durations_monotonically=True`` to express tied values in decreasing duration: :: >>> notetools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=True, ... ) Selection(Note("c'2."), Note("c'16")) Set ``decrease_durations_monotonically=False`` to express tied values in increasing duration: :: >>> notetools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=False, ... ) Selection(Note("c'16"), Note("c'2.")) Set `pitches` to a single pitch or a sequence of pitches. Set `durations` to a single duration or a list of durations. Returns list of newly constructed notes. ''' from abjad.tools import leaftools from abjad.tools import notetools from abjad.tools import selectiontools from abjad.tools import tuplettools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] duration_pairs = [durationtools.Duration(duration) for duration in durations] # set lists of pitches and duration pairs to the same length size = max(len(duration_pairs), len(pitches)) duration_pairs = \ sequencetools.repeat_sequence_to_length(duration_pairs, size) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration durations = Duration._group_nonreduced_fractions_by_implied_prolation( duration_pairs) def _make_unprolated_notes( pitches, durations, decrease_durations_monotonically=decrease_durations_monotonically, ): assert len(pitches) == len(durations) result = [] for pitch, duration in zip(pitches, durations): result.extend(leaftools.make_tied_leaf( notetools.Note, duration, pitches=pitch, decrease_durations_monotonically=decrease_durations_monotonically, )) return result result = [] for duration in durations: # get factors in denominator of duration group duration other than 1, 2. factors = set(mathtools.factors(duration[0].denominator)) 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, decrease_durations_monotonically=decrease_durations_monotonically)) else: # compute prolation denominator = duration[0].denominator numerator = mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / fractions.Fraction(*multiplier) duration = [ratio * durationtools.Duration(d) for d in duration] ns = _make_unprolated_notes(ps, duration, decrease_durations_monotonically=decrease_durations_monotonically) t = tuplettools.Tuplet(multiplier, ns) result.append(t) # return result result = selectiontools.Selection(result) return result
def _split_by_duration( self, duration, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import scoretools from abjad.tools import selectiontools # check input duration = durationtools.Duration(duration) assert 0 <= duration, repr(duration) # if zero duration then return empty list and self if duration == 0: return [], self # get split point score offset global_split_point = self._get_timespan().start_offset + duration # get any duration-crossing descendents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) # get any duration-crossing measure descendents measures = [ x for x in duration_crossing_descendants if isinstance(x, scoretools.Measure) ] # if we must split a power-of-two measure at non-power-of-two # split point then go ahead and transform the power-of-two measure # to non-power-of-two equivalent now; # code that crawls and splits later on will be happier if len(measures) == 1: measure = measures[0] split_point_in_measure = \ global_split_point - measure._get_timespan().start_offset if measure.has_non_power_of_two_denominator: if not measure.implied_prolation == \ split_point_in_measure.implied_prolation: raise NotImplementedError elif not mathtools.is_nonnegative_integer_power_of_two( split_point_in_measure.denominator): non_power_of_two_factors = mathtools.remove_powers_of_two( split_point_in_measure.denominator) non_power_of_two_factors = mathtools.factors( non_power_of_two_factors) non_power_of_two_product = 1 for non_power_of_two_factor in non_power_of_two_factors: non_power_of_two_product *= non_power_of_two_factor scoretools.scale_measure_denominator_and_adjust_measure_contents( measure, non_power_of_two_product) # rederive duration crosses with possibly new measure contents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) elif 1 < len(measures): message = 'measures can not nest.' raise Exception(message) # any duration-crossing leaf will be at end of list bottom = duration_crossing_descendants[-1] did_split_leaf = False # if split point necessitates leaf split if isinstance(bottom, scoretools.Leaf): assert isinstance(bottom, scoretools.Leaf) did_split_leaf = True split_point_in_bottom = \ global_split_point - bottom._get_timespan().start_offset left_list, right_list = bottom._split_by_duration( split_point_in_bottom, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, use_messiaen_style_ties=use_messiaen_style_ties, ) right = right_list[0] leaf_right_of_split = right leaf_left_of_split = left_list[-1] duration_crossing_containers = duration_crossing_descendants[:-1] if not len(duration_crossing_containers): return left_list, right_list # if split point falls between leaves # then find leaf to immediate right of split point # in order to start upward crawl through duration-crossing containers else: duration_crossing_containers = duration_crossing_descendants[:] for leaf in iterate(bottom).by_class(scoretools.Leaf): if leaf._get_timespan().start_offset == global_split_point: leaf_right_of_split = leaf leaf_left_of_split = leaf_right_of_split._get_leaf(-1) break else: message = 'can not split empty container {!r}.' message = message.format(bottom) raise Exception(message) # find component to right of split that is also immediate child of # last duration-crossing container for component in \ leaf_right_of_split._get_parentage(include_self=True): if component._parent is duration_crossing_containers[-1]: highest_level_component_right_of_split = component break else: message = 'should we be able to get here?' raise ValueError(message) # crawl back up through duration-crossing containers and # fracture spanners if requested if fracture_spanners: start_offset = leaf_right_of_split._get_timespan().start_offset for parent in leaf_right_of_split._get_parentage(): if parent._get_timespan().start_offset == start_offset: for spanner in parent._get_spanners(): index = spanner._index(parent) spanner._fracture(index, direction=Left) if parent is component: break # crawl back up through duration-crossing containers and split each previous = highest_level_component_right_of_split for duration_crossing_container in \ reversed(duration_crossing_containers): assert isinstance( duration_crossing_container, scoretools.Container) i = duration_crossing_container.index(previous) left, right = duration_crossing_container._split_at_index( i, fracture_spanners=fracture_spanners, ) previous = right # NOTE: If logical tie here is convenience, then fusing is good. # If logical tie here is user-given, then fusing is less good. # Maybe later model difference between user logical ties and not. left_logical_tie = leaf_left_of_split._get_logical_tie() right_logical_tie = leaf_right_of_split._get_logical_tie() left_logical_tie._fuse_leaves_by_immediate_parent() right_logical_tie._fuse_leaves_by_immediate_parent() # reapply tie here if crawl above killed tie applied to leaves if did_split_leaf: if (tie_split_notes and isinstance(leaf_left_of_split, scoretools.Note)): if (leaf_left_of_split._get_parentage().root is leaf_right_of_split._get_parentage().root): leaves_around_split = \ (leaf_left_of_split, leaf_right_of_split) selection = selectiontools.ContiguousSelection( leaves_around_split) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return pair of left and right list-wrapped halves of container return ([left], [right])
def test_mathtools_factors_06(): t = mathtools.factors(12) assert t == [1, 2, 2, 3]
def make_notes(pitches, durations, decrease_durations_monotonically=True): r'''Make notes according to `pitches` and `durations`. Cycle through `pitches` when the length of `pitches` is less than the length of `durations`: :: >>> scoretools.make_notes([0], [(1, 16), (1, 8), (1, 8)]) Selection(Note("c'16"), Note("c'8"), Note("c'8")) Cycle through `durations` when the length of `durations` is less than the length of `pitches`: :: >>> scoretools.make_notes([0, 2, 4, 5, 7], [(1, 16), (1, 8), (1, 8)]) Selection(Note("c'16"), Note("d'8"), Note("e'8"), Note("f'16"), Note("g'8")) Create ad hoc tuplets for nonassignable durations: :: >>> scoretools.make_notes([0], [(1, 16), (1, 12), (1, 8)]) Selection(Note("c'16"), Tuplet(Multiplier(2, 3), "c'8"), Note("c'8")) Set ``decrease_durations_monotonically=True`` to express tied values in decreasing duration: :: >>> scoretools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=True, ... ) Selection(Note("c'2."), Note("c'16")) Set ``decrease_durations_monotonically=False`` to express tied values in increasing duration: :: >>> scoretools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=False, ... ) Selection(Note("c'16"), Note("c'2.")) Set `pitches` to a single pitch or a sequence of pitches. Set `durations` to a single duration or a list of durations. Returns selection. ''' from abjad.tools import scoretools from abjad.tools import selectiontools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] nonreduced_fractions = [mathtools.NonreducedFraction(_) for _ in durations] size = max(len(nonreduced_fractions), len(pitches)) nonreduced_fractions = sequencetools.repeat_sequence_to_length( nonreduced_fractions, size, ) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration durations = Duration._group_nonreduced_fractions_by_implied_prolation( nonreduced_fractions) def _make_unprolated_notes( pitches, durations, decrease_durations_monotonically=decrease_durations_monotonically, ): assert len(pitches) == len(durations) result = [] for pitch, duration in zip(pitches, durations): result.extend( scoretools.make_tied_leaf( scoretools.Note, duration, pitches=pitch, decrease_durations_monotonically= decrease_durations_monotonically, )) return result result = [] for duration in durations: # get factors in denominator of duration group duration other than 1, 2. factors = set(mathtools.factors(duration[0].denominator)) 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, decrease_durations_monotonically= decrease_durations_monotonically)) else: # compute prolation denominator = duration[0].denominator numerator = mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / fractions.Fraction(*multiplier) duration = [ratio * durationtools.Duration(d) for d in duration] ns = _make_unprolated_notes(ps, duration, decrease_durations_monotonically= decrease_durations_monotonically) t = scoretools.Tuplet(multiplier, ns) result.append(t) # return result result = selectiontools.Selection(result) return result
def duration_and_possible_denominators_to_time_signature( duration, denominators=None, factor=None, ): r'''Make new time signature equal to `duration`: :: >>> timesignaturetools.duration_and_possible_denominators_to_time_signature( ... Duration(3, 2)) TimeSignatureMark((3, 2)) Make new time signature equal to `duration` with denominator equal to the first possible element in `denominators`: :: >>> timesignaturetools.duration_and_possible_denominators_to_time_signature( ... Duration(3, 2), denominators=[5, 6, 7, 8]) TimeSignatureMark((9, 6)) Make new time signature equal to `duration` with denominator divisible by `factor`: :: >>> timesignaturetools.duration_and_possible_denominators_to_time_signature( ... Duration(3, 2), factor=5) TimeSignatureMark((15, 10)) .. note:: possibly divide this into two separate functions? Returns new time signature. ''' # check input duration = durationtools.Duration(duration) if denominators is not None: if factor is not None: denominators = [ d for d in denominators if factor in mathtools.factors(d) ] for desired_denominator in sorted(denominators): nonreduced_fraction = mathtools.NonreducedFraction(duration) candidate_pair = \ nonreduced_fraction.with_denominator(desired_denominator) if candidate_pair.denominator == desired_denominator: return contexttools.TimeSignatureMark(candidate_pair) if factor is not None: if factor in mathtools.factors(duration.denominator): return contexttools.TimeSignatureMark(duration) else: time_signature_numerator = factor * duration.numerator time_signature_denominator = factor * duration.denominator return contexttools.TimeSignatureMark( (time_signature_numerator, time_signature_denominator)) else: return contexttools.TimeSignatureMark(duration)
def make_leaves( pitches, durations, decrease_durations_monotonically=True, tie_rests=False, forbidden_written_duration=None, metrical_hiearchy=None, ): r'''Make leaves. .. container:: example **Example 1.** Integer and string elements in `pitches` result in notes: :: >>> pitches = [2, 4, 'F#5', 'G#5'] >>> duration = Duration(1, 4) >>> leaves = leaftools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { d'4 e'4 fs''4 gs''4 } .. container:: example **Example 2.** Tuple elements in `pitches` result in chords: :: >>> pitches = [(0, 2, 4), ('F#5', 'G#5', 'A#5')] >>> duration = Duration(1, 2) >>> leaves = leaftools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { <c' d' e'>2 <fs'' gs'' as''>2 } .. container:: example **Example 3.** None-valued elements in `pitches` result in rests: :: >>> pitches = 4 * [None] >>> durations = [Duration(1, 4)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = stafftools.RhythmicStaff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new RhythmicStaff { r4 r4 r4 r4 } .. container:: example **Example 4.** You can mix and match values passed to `pitches`: :: >>> pitches = [(0, 2, 4), None, 'C#5', 'D#5'] >>> durations = [Duration(1, 4)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { <c' d' e'>4 r4 cs''4 ds''4 } .. container:: example **Example 5.** Read `pitches` cyclically when the length of `pitches` is less than the length of `durations`: :: >>> pitches = ['C5'] >>> durations = 2 * [Duration(3, 8), Duration(1, 8)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { c''4. c''8 c''4. c''8 } .. container:: example **Example 6.** Read `durations` cyclically when the length of `durations` is less than the length of `pitches`: :: >>> pitches = "c'' d'' e'' f''" >>> durations = [Duration(1, 4)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { c''4 d''4 e''4 f''4 } .. container:: example **Example 7.** Elements in `durations` with non-power-of-two denominators result in tuplet-nested leaves: :: >>> pitches = ['D5'] >>> durations = [Duration(1, 3), Duration(1, 3), Duration(1, 3)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { d''2 d''2 d''2 } } .. container:: example **Example 8.** Set `decrease_durations_monotonically` to true to return nonassignable durations tied from greatest to least: :: >>> pitches = ['D#5'] >>> durations = [Duration(13, 16)] >>> leaves = leaftools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> time_signature = contexttools.TimeSignatureMark((13, 16)) >>> time_signature = attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 13/16 ds''2. ~ ds''16 } .. container:: example **Example 9.** Set `decrease_durations_monotonically` to false to return nonassignable durations tied from least to greatest: :: >>> pitches = ['E5'] >>> durations = [Duration(13, 16)] >>> leaves = leaftools.make_leaves( ... pitches, ... durations, ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = contexttools.TimeSignatureMark((13, 16)) >>> time_signature = attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 13/16 e''16 ~ e''2. } .. container:: example **Example 10.** Set `tie_rests` to true to return tied rests for nonassignable durations. Note that LilyPond does not engrave ties between rests: :: >>> pitches = [None] >>> durations = [Duration(5, 8)] >>> leaves = leaftools.make_leaves( ... pitches, ... durations, ... tie_rests=True, ... ) >>> staff = stafftools.RhythmicStaff(leaves) >>> time_signature = contexttools.TimeSignatureMark((5, 8)) >>> time_signature = attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new RhythmicStaff { \time 5/8 r2 ~ r8 } .. container:: example **Example 11.** Set `forbidden_written_duration` to avoid notes greater than or equal to a certain written duration: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = leaftools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... ) >>> staff = Staff(leaves) >>> time_signature = contexttools.TimeSignatureMark((5, 4)) >>> time_signature = attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/4 f'4 ~ f'4 ~ f'8 g'4 ~ g'4 ~ g'8 } .. container:: example **Example 12.** You may set `forbidden_written_duration` and `decrease_durations_monotonically` together: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = leaftools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = contexttools.TimeSignatureMark((5, 4)) >>> time_siganture = attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/4 f'8 ~ f'4 ~ f'4 g'8 ~ g'4 ~ g'4 } Returns selection of unincorporated leaves. ''' from abjad.tools import scoretools from abjad.tools import notetools from abjad.tools import resttools from abjad.tools import tuplettools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] # make duration pairs duration_pairs = [durationtools.Duration(duration) for duration in durations] # set lists of pitches and duration pairs to the same length size = max(len(duration_pairs), len(pitches)) duration_pairs = \ sequencetools.repeat_sequence_to_length(duration_pairs, size) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration duration_groups = \ Duration._group_nonreduced_fractions_by_implied_prolation( duration_pairs) result = [] for duration_group in duration_groups: # get factors in denominator of duration group other than 1, 2. factors = set(mathtools.factors(duration_group[0].denominator)) 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, decrease_durations_monotonically=decrease_durations_monotonically, forbidden_written_duration=forbidden_written_duration, tie_rests=tie_rests, ) result.extend(leaves) else: # compute tuplet prolation denominator = duration_group[0].denominator numerator = \ mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / durationtools.Duration(*multiplier) duration_group = [ratio * durationtools.Duration(duration) for duration in duration_group] # make tuplet leaves tuplet_leaves = [] for pitch, duration in zip(current_pitches, duration_group): leaves = _make_leaf_on_pitch( pitch, duration, decrease_durations_monotonically=decrease_durations_monotonically, ) tuplet_leaves.extend(leaves) tuplet = tuplettools.Tuplet(multiplier, tuplet_leaves) result.append(tuplet) result = selectiontools.Selection(result) return result
def test_mathtools_factors_05(): t = mathtools.factors(6) assert t == [1, 2, 3]
def make_leaves( pitches, durations, decrease_durations_monotonically=True, forbidden_written_duration=None, is_diminution=True, metrical_hiearchy=None, use_messiaen_style_ties=False, use_multimeasure_rests=False, ): r'''Makes leaves. .. container:: example **Example 1.** Integer and string elements in `pitches` result in notes: :: >>> pitches = [2, 4, 'F#5', 'G#5'] >>> duration = Duration(1, 4) >>> leaves = scoretools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { d'4 e'4 fs''4 gs''4 } .. container:: example **Example 2.** Tuple elements in `pitches` result in chords: :: >>> pitches = [(0, 2, 4), ('F#5', 'G#5', 'A#5')] >>> duration = Duration(1, 2) >>> leaves = scoretools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { <c' d' e'>2 <fs'' gs'' as''>2 } .. container:: example **Example 3.** None-valued elements in `pitches` result in rests: :: >>> pitches = 4 * [None] >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> staff.context_name = 'RhythmicStaff' >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new RhythmicStaff { r4 r4 r4 r4 } .. container:: example **Example 4.** You can mix and match values passed to `pitches`: :: >>> pitches = [(0, 2, 4), None, 'C#5', 'D#5'] >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { <c' d' e'>4 r4 cs''4 ds''4 } .. container:: example **Example 5.** Read `pitches` cyclically when the length of `pitches` is less than the length of `durations`: :: >>> pitches = ['C5'] >>> durations = 2 * [Duration(3, 8), Duration(1, 8)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { c''4. c''8 c''4. c''8 } .. container:: example **Example 6.** Read `durations` cyclically when the length of `durations` is less than the length of `pitches`: :: >>> pitches = "c'' d'' e'' f''" >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { c''4 d''4 e''4 f''4 } .. container:: example **Example 7.** Elements in `durations` with non-power-of-two denominators result in tuplet-nested leaves: :: >>> pitches = ['D5'] >>> durations = [Duration(1, 3), Duration(1, 3), Duration(1, 3)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { d''2 d''2 d''2 } } .. container:: example **Example 8.** Set `decrease_durations_monotonically` to true to return nonassignable durations tied from greatest to least: :: >>> pitches = ['D#5'] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((13, 16)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 13/16 ds''2. ~ ds''16 } .. container:: example **Example 9.** Set `decrease_durations_monotonically` to false to return nonassignable durations tied from least to greatest: :: >>> pitches = ['E5'] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((13, 16)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 13/16 e''16 ~ e''2. } .. container:: example **Example 10.** Set `forbidden_written_duration` to avoid notes greater than or equal to a certain written duration: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 4)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/4 f'4 ~ f'4 ~ f'8 g'4 ~ g'4 ~ g'8 } .. container:: example **Example 11.** You may set `forbidden_written_duration` and `decrease_durations_monotonically` together: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 4)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/4 f'8 ~ f'4 ~ f'4 g'8 ~ g'4 ~ g'4 } .. container:: example **Example 12.** Set `is_diminution` to true to produce diminished tuplets: :: >>> pitches = "f'" >>> durations = [Duration(5, 14)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... is_diminution=True ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 14)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/14 \tweak edge-height #'(0.7 . 0) \times 4/7 { f'2 ~ f'8 } } This is default behavior. .. container:: example **Example 13.** Set `is_diminution` to false to produce agumented tuplets: :: >>> pitches = "f'" >>> durations = [Duration(5, 14)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... is_diminution=False ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 14)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \time 5/14 \tweak text #tuplet-number::calc-fraction-text \tweak edge-height #'(0.7 . 0) \times 8/7 { f'4 ~ f'16 } } .. container:: example **Example 14.** None-valued elements in `pitches` result in multimeasure rests when the multimeasure rest keyword is set: :: >>> pitches = [None] >>> durations = [Duration(3, 8), Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... use_multimeasure_rests=True, ... ) >>> leaves Selection(MultimeasureRest('R1 * 3/8'), MultimeasureRest('R1 * 5/8')) :: >>> staff = Staff([ ... Measure((3, 8), [leaves[0]]), ... Measure((5, 8), [leaves[1]]), ... ]) >>> staff.context_name = 'RhythmicStaff' >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new RhythmicStaff { { \time 3/8 R1 * 3/8 } { \time 5/8 R1 * 5/8 } } .. container:: example **Example 15.** Uses Messiaen-style ties: :: >>> pitches = [0] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... use_messiaen_style_ties=True, ... ) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { c'2. c'16 \repeatTie } .. container:: example **Example 16.** Works with numbered pitch-class: :: >>> pitches = [pitchtools.NumberedPitchClass(6)] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... ) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { fs'2. ~ fs'16 } Returns selection of leaves. ''' from abjad.tools import scoretools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] nonreduced_fractions = [mathtools.NonreducedFraction(_) for _ in durations] size = max(len(nonreduced_fractions), len(pitches)) nonreduced_fractions = sequencetools.repeat_sequence_to_length( nonreduced_fractions, size, ) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration duration_groups = \ Duration._group_nonreduced_fractions_by_implied_prolation( nonreduced_fractions) result = [] for duration_group in duration_groups: # get factors in denominator of duration group other than 1, 2. factors = set(mathtools.factors(duration_group[0].denominator)) 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, decrease_durations_monotonically=decrease_durations_monotonically, forbidden_written_duration=forbidden_written_duration, use_multimeasure_rests=use_multimeasure_rests, use_messiaen_style_ties=use_messiaen_style_ties, ) result.extend(leaves) else: # compute tuplet prolation denominator = duration_group[0].denominator numerator = mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / durationtools.Duration(*multiplier) duration_group = [ratio * durationtools.Duration(duration) for duration in duration_group] # make tuplet leaves tuplet_leaves = [] for pitch, duration in zip(current_pitches, duration_group): leaves = _make_leaf_on_pitch( pitch, duration, decrease_durations_monotonically=\ decrease_durations_monotonically, use_multimeasure_rests=use_multimeasure_rests, use_messiaen_style_ties=use_messiaen_style_ties, ) tuplet_leaves.extend(leaves) tuplet = scoretools.Tuplet(multiplier, tuplet_leaves) if is_diminution and not tuplet.is_diminution: tuplet.toggle_prolation() elif not is_diminution and tuplet.is_diminution: tuplet.toggle_prolation() result.append(tuplet) result = selectiontools.Selection(result) return result
def test_mathtools_factors_03(): t = mathtools.factors(3) assert t == [1, 3]
def multiply_with_cross_cancelation(self, multiplier): '''Multiplies nonreduced fraction by `expr` with cross-cancelation. :: >>> fraction = mathtools.NonreducedFraction(4, 8) :: >>> fraction.multiply_with_cross_cancelation((2, 3)) NonreducedFraction(4, 12) :: >>> fraction.multiply_with_cross_cancelation((4, 1)) NonreducedFraction(4, 2) :: >>> fraction.multiply_with_cross_cancelation((3, 5)) NonreducedFraction(12, 40) :: >>> fraction.multiply_with_cross_cancelation((6, 5)) NonreducedFraction(12, 20) :: >>> fraction = mathtools.NonreducedFraction(5, 6) >>> fraction.multiply_with_cross_cancelation((6, 5)) NonreducedFraction(1, 1) Returns nonreduced fraction. ''' from abjad.tools import durationtools from abjad.tools import mathtools multiplier = durationtools.Multiplier(multiplier) self_numerator_factors = mathtools.factors(self.numerator) multiplier_denominator_factors = mathtools.factors( multiplier.denominator) for factor in multiplier_denominator_factors[:]: if factor in self_numerator_factors: self_numerator_factors.remove(factor) multiplier_denominator_factors.remove(factor) self_denominator_factors = mathtools.factors(self.denominator) multiplier_numerator_factors = mathtools.factors(multiplier.numerator) for factor in multiplier_numerator_factors[:]: if factor in self_denominator_factors: self_denominator_factors.remove(factor) multiplier_numerator_factors.remove(factor) result_numerator_factors = self_numerator_factors + \ multiplier_numerator_factors result_denominator_factors = self_denominator_factors + \ multiplier_denominator_factors result_numerator = 1 for factor in result_numerator_factors: result_numerator *= factor result_denominator = 1 for factor in result_denominator_factors: result_denominator *= factor pair = (result_numerator, result_denominator) return self._from_pair(pair)
def make_leaves( pitches, durations, decrease_durations_monotonically=True, forbidden_written_duration=None, is_diminution=True, metrical_hiearchy=None, use_messiaen_style_ties=False, use_multimeasure_rests=False, ): r'''Makes leaves. .. container:: example **Example 1.** Integer and string elements in `pitches` result in notes: :: >>> pitches = [2, 4, 'F#5', 'G#5'] >>> duration = Duration(1, 4) >>> leaves = scoretools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { d'4 e'4 fs''4 gs''4 } .. container:: example **Example 2.** Tuple elements in `pitches` result in chords: :: >>> pitches = [(0, 2, 4), ('F#5', 'G#5', 'A#5')] >>> duration = Duration(1, 2) >>> leaves = scoretools.make_leaves(pitches, duration) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { <c' d' e'>2 <fs'' gs'' as''>2 } .. container:: example **Example 3.** None-valued elements in `pitches` result in rests: :: >>> pitches = 4 * [None] >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> staff.context_name = 'RhythmicStaff' >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new RhythmicStaff { r4 r4 r4 r4 } .. container:: example **Example 4.** You can mix and match values passed to `pitches`: :: >>> pitches = [(0, 2, 4), None, 'C#5', 'D#5'] >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { <c' d' e'>4 r4 cs''4 ds''4 } .. container:: example **Example 5.** Read `pitches` cyclically when the length of `pitches` is less than the length of `durations`: :: >>> pitches = ['C5'] >>> durations = 2 * [Duration(3, 8), Duration(1, 8)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c''4. c''8 c''4. c''8 } .. container:: example **Example 6.** Read `durations` cyclically when the length of `durations` is less than the length of `pitches`: :: >>> pitches = "c'' d'' e'' f''" >>> durations = [Duration(1, 4)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c''4 d''4 e''4 f''4 } .. container:: example **Example 7.** Elements in `durations` with non-power-of-two denominators result in tuplet-nested leaves: :: >>> pitches = ['D5'] >>> durations = [Duration(1, 3), Duration(1, 3), Duration(1, 3)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \times 2/3 { d''2 d''2 d''2 } } .. container:: example **Example 8.** Set `decrease_durations_monotonically` to true to return nonassignable durations tied from greatest to least: :: >>> pitches = ['D#5'] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves(pitches, durations) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((13, 16)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 13/16 ds''2. ~ ds''16 } .. container:: example **Example 9.** Set `decrease_durations_monotonically` to false to return nonassignable durations tied from least to greatest: :: >>> pitches = ['E5'] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((13, 16)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 13/16 e''16 ~ e''2. } .. container:: example **Example 10.** Set `forbidden_written_duration` to avoid notes greater than or equal to a certain written duration: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 4)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 5/4 f'4 ~ f'4 ~ f'8 g'4 ~ g'4 ~ g'8 } .. container:: example **Example 11.** You may set `forbidden_written_duration` and `decrease_durations_monotonically` together: :: >>> pitches = "f' g'" >>> durations = [Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... forbidden_written_duration=Duration(1, 2), ... decrease_durations_monotonically=False, ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 4)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 5/4 f'8 ~ f'4 ~ f'4 g'8 ~ g'4 ~ g'4 } .. container:: example **Example 12.** Set `is_diminution` to true to produce diminished tuplets: :: >>> pitches = "f'" >>> durations = [Duration(5, 14)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... is_diminution=True ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 14)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 5/14 \tweak #'edge-height #'(0.7 . 0) \times 4/7 { f'2 ~ f'8 } } This is default behavior. .. container:: example **Example 13.** Set `is_diminution` to false to produce agumented tuplets: :: >>> pitches = "f'" >>> durations = [Duration(5, 14)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... is_diminution=False ... ) >>> staff = Staff(leaves) >>> time_signature = TimeSignature((5, 14)) >>> attach(time_signature, staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { \time 5/14 \tweak #'text #tuplet-number::calc-fraction-text \tweak #'edge-height #'(0.7 . 0) \times 8/7 { f'4 ~ f'16 } } .. container:: example **Example 14.** None-valued elements in `pitches` result in multimeasure rests when the multimeasure rest keyword is set: :: >>> pitches = [None] >>> durations = [Duration(3, 8), Duration(5, 8)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... use_multimeasure_rests=True, ... ) >>> leaves Selection(MultimeasureRest('R1 * 3/8'), MultimeasureRest('R1 * 5/8')) :: >>> staff = Staff([ ... Measure((3, 8), [leaves[0]]), ... Measure((5, 8), [leaves[1]]), ... ]) >>> staff.context_name = 'RhythmicStaff' >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new RhythmicStaff { { \time 3/8 R1 * 3/8 } { \time 5/8 R1 * 5/8 } } .. container:: example **Example 15.** Uses Messiaen-style ties: :: >>> pitches = [0] >>> durations = [Duration(13, 16)] >>> leaves = scoretools.make_leaves( ... pitches, ... durations, ... use_messiaen_style_ties=True, ... ) >>> staff = Staff(leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'2. c'16 \repeatTie } Returns selection of leaves. ''' from abjad.tools import scoretools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] nonreduced_fractions = [mathtools.NonreducedFraction(_) for _ in durations] size = max(len(nonreduced_fractions), len(pitches)) nonreduced_fractions = sequencetools.repeat_sequence_to_length( nonreduced_fractions, size, ) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration duration_groups = \ Duration._group_nonreduced_fractions_by_implied_prolation( nonreduced_fractions) result = [] for duration_group in duration_groups: # get factors in denominator of duration group other than 1, 2. factors = set(mathtools.factors(duration_group[0].denominator)) 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, decrease_durations_monotonically= decrease_durations_monotonically, forbidden_written_duration=forbidden_written_duration, use_multimeasure_rests=use_multimeasure_rests, use_messiaen_style_ties=use_messiaen_style_ties, ) result.extend(leaves) else: # compute tuplet prolation denominator = duration_group[0].denominator numerator = mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / durationtools.Duration(*multiplier) duration_group = [ ratio * durationtools.Duration(duration) for duration in duration_group ] # make tuplet leaves tuplet_leaves = [] for pitch, duration in zip(current_pitches, duration_group): leaves = _make_leaf_on_pitch( pitch, duration, decrease_durations_monotonically=\ decrease_durations_monotonically, use_multimeasure_rests=use_multimeasure_rests, use_messiaen_style_ties=use_messiaen_style_ties, ) tuplet_leaves.extend(leaves) tuplet = scoretools.Tuplet(multiplier, tuplet_leaves) if is_diminution and not tuplet.is_diminution: tuplet.toggle_prolation() elif not is_diminution and tuplet.is_diminution: tuplet.toggle_prolation() result.append(tuplet) result = selectiontools.Selection(result) return result
def __init__(self, arg=None, decrease_durations_monotonically=True): arg = arg or (4, 4) def recurse( node, factors, denominator, decrease_durations_monotonically): if factors: factor, factors = factors[0], factors[1:] preprolated_duration = node.preprolated_duration / factor if factor in (2, 3, 4): if factors: for _ in range(factor): child = rhythmtreetools.RhythmTreeContainer( preprolated_duration=preprolated_duration) node.append(child) recurse( child, factors, denominator, decrease_durations_monotonically, ) else: for _ in range(factor): node.append( rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator))) else: parts = [3] total = 3 while total < factor: if decrease_durations_monotonically: parts.append(2) else: parts.insert(0, 2) total += 2 for part in parts: grouping = rhythmtreetools.RhythmTreeContainer( preprolated_duration=part * preprolated_duration) if factors: for _ in range(part): child = rhythmtreetools.RhythmTreeContainer( preprolated_duration=preprolated_duration) grouping.append(child) recurse( child, factors, denominator, decrease_durations_monotonically, ) else: for _ in range(part): grouping.append( rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator))) node.append(grouping) else: node.extend([rhythmtreetools.RhythmTreeLeaf( preprolated_duration=(1, denominator)) for _ in range(node.preprolated_duration.numerator)]) decrease_durations_monotonically = \ bool(decrease_durations_monotonically) if isinstance(arg, type(self)): root = arg.root_node numerator, denominator = arg.numerator, arg.denominator decrease_durations_monotonically = \ arg.decrease_durations_monotonically elif isinstance(arg, (str, rhythmtreetools.RhythmTreeContainer)): if isinstance(arg, str): parsed = rhythmtreetools.RhythmTreeParser()(arg) assert len(parsed) == 1 root = parsed[0] else: root = arg for node in root.nodes: assert node.prolation == 1 numerator = root.preprolated_duration.numerator denominator = root.preprolated_duration.denominator elif isinstance(arg, (tuple, scoretools.Measure)) or \ (hasattr(arg, 'numerator') and hasattr(arg, 'denominator')): if isinstance(arg, tuple): fraction = mathtools.NonreducedFraction(arg) elif isinstance(arg, scoretools.Measure): time_signature = arg._get_effective( indicatortools.TimeSignature) fraction = mathtools.NonreducedFraction( time_signature.numerator, time_signature.denominator) else: fraction = mathtools.NonreducedFraction( arg.numerator, arg.denominator) numerator, denominator = fraction.numerator, fraction.denominator factors = mathtools.factors(numerator)[1:] # group two nested levels of 2s into a 4 if 1 < len(factors) and factors[0] == factors[1] == 2: factors[0:2] = [4] root = rhythmtreetools.RhythmTreeContainer( preprolated_duration=fraction) recurse( root, factors, denominator, decrease_durations_monotonically, ) else: message = 'can not initialize {}: {!r}.' message = message.format(type(self).__name__, arg) raise ValueError(message) self._root_node = root self._numerator = numerator self._denominator = denominator self._decrease_durations_monotonically = \ decrease_durations_monotonically
def _split_by_duration( self, duration, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import scoretools from abjad.tools import selectiontools # check input duration = durationtools.Duration(duration) assert 0 <= duration, repr(duration) # if zero duration then return empty list and self if duration == 0: return [], self # get split point score offset global_split_point = self._get_timespan().start_offset + duration # get any duration-crossing descendents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) # get any duration-crossing measure descendents measures = [ x for x in duration_crossing_descendants if isinstance(x, scoretools.Measure) ] # if we must split a power-of-two measure at non-power-of-two # split point then go ahead and transform the power-of-two measure # to non-power-of-two equivalent now; # code that crawls and splits later on will be happier if len(measures) == 1: measure = measures[0] split_point_in_measure = \ global_split_point - measure._get_timespan().start_offset if measure.has_non_power_of_two_denominator: if not measure.implied_prolation == \ split_point_in_measure.implied_prolation: raise NotImplementedError elif not mathtools.is_nonnegative_integer_power_of_two( split_point_in_measure.denominator): non_power_of_two_factors = mathtools.remove_powers_of_two( split_point_in_measure.denominator) non_power_of_two_factors = mathtools.factors( non_power_of_two_factors) non_power_of_two_product = 1 for non_power_of_two_factor in non_power_of_two_factors: non_power_of_two_product *= non_power_of_two_factor scoretools.scale_measure_denominator_and_adjust_measure_contents( measure, non_power_of_two_product) # rederive duration crosses with possibly new measure contents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) elif 1 < len(measures): message = 'measures can not nest.' raise Exception(message) # any duration-crossing leaf will be at end of list bottom = duration_crossing_descendants[-1] did_split_leaf = False # if split point necessitates leaf split if isinstance(bottom, scoretools.Leaf): assert isinstance(bottom, scoretools.Leaf) did_split_leaf = True split_point_in_bottom = \ global_split_point - bottom._get_timespan().start_offset left_list, right_list = bottom._split_by_duration( split_point_in_bottom, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, use_messiaen_style_ties=use_messiaen_style_ties, ) right = right_list[0] leaf_right_of_split = right leaf_left_of_split = left_list[-1] duration_crossing_containers = duration_crossing_descendants[:-1] if not len(duration_crossing_containers): return left_list, right_list # if split point falls between leaves # then find leaf to immediate right of split point # in order to start upward crawl through duration-crossing containers else: duration_crossing_containers = duration_crossing_descendants[:] for leaf in iterate(bottom).by_class(scoretools.Leaf): if leaf._get_timespan().start_offset == global_split_point: leaf_right_of_split = leaf leaf_left_of_split = leaf_right_of_split._get_leaf(-1) break else: message = 'can not split empty container {!r}.' message = message.format(bottom) raise Exception(message) # find component to right of split that is also immediate child of # last duration-crossing container for component in \ leaf_right_of_split._get_parentage(include_self=True): if component._parent is duration_crossing_containers[-1]: highest_level_component_right_of_split = component break else: message = 'should we be able to get here?' raise ValueError(message) # crawl back up through duration-crossing containers and # fracture spanners if requested if fracture_spanners: start_offset = leaf_right_of_split._get_timespan().start_offset for parent in leaf_right_of_split._get_parentage(): if parent._get_timespan().start_offset == start_offset: for spanner in parent._get_spanners(): index = spanner._index(parent) spanner._fracture(index, direction=Left) if parent is component: break # crawl back up through duration-crossing containers and split each previous = highest_level_component_right_of_split for duration_crossing_container in \ reversed(duration_crossing_containers): assert isinstance(duration_crossing_container, scoretools.Container) i = duration_crossing_container.index(previous) left, right = duration_crossing_container._split_at_index( i, fracture_spanners=fracture_spanners, ) previous = right # NOTE: If logical tie here is convenience, then fusing is good. # If logical tie here is user-given, then fusing is less good. # Maybe later model difference between user logical ties and not. left_logical_tie = leaf_left_of_split._get_logical_tie() right_logical_tie = leaf_right_of_split._get_logical_tie() left_logical_tie._fuse_leaves_by_immediate_parent() right_logical_tie._fuse_leaves_by_immediate_parent() # reapply tie here if crawl above killed tie applied to leaves if did_split_leaf: if (tie_split_notes and isinstance(leaf_left_of_split, scoretools.Note)): if (leaf_left_of_split._get_parentage().root is leaf_right_of_split._get_parentage().root): leaves_around_split = \ (leaf_left_of_split, leaf_right_of_split) selection = selectiontools.ContiguousSelection( leaves_around_split) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return pair of left and right list-wrapped halves of container return ([left], [right])
def make_notes( pitches, durations, decrease_durations_monotonically=True, use_messiaen_style_ties=False, ): r'''Makes notes according to `pitches` and `durations`. .. container:: example **Example 1.** Cycles through `pitches` when the length of `pitches` is less than the length of `durations`: :: >>> notes = scoretools.make_notes([0], [(1, 16), (1, 8), (1, 8)]) >>> notes Selection(Note("c'16"), Note("c'8"), Note("c'8")) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'16 c'8 c'8 } .. container:: example **Example 2.** Cycles through `durations` when the length of `durations` is less than the length of `pitches`: :: >>> notes = scoretools.make_notes( ... [0, 2, 4, 5, 7], ... [(1, 16), (1, 8), (1, 8)], ... ) >>> notes Selection(Note("c'16"), Note("d'8"), Note("e'8"), Note("f'16"), Note("g'8")) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'16 d'8 e'8 f'16 g'8 } .. container:: example **Example 3.** Creates ad hoc tuplets for nonassignable durations: :: >>> notes = scoretools.make_notes([0], [(1, 16), (1, 12), (1, 8)]) >>> notes Selection(Note("c'16"), Tuplet(Multiplier(2, 3), "c'8"), Note("c'8")) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'16 \tweak #'edge-height #'(0.7 . 0) \times 2/3 { c'8 } c'8 } .. container:: example **Example 4.** Set ``decrease_durations_monotonically=True`` to express tied values in decreasing duration: :: >>> notes = scoretools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=True, ... ) >>> notes Selection(Note("c'2."), Note("c'16")) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'2. ~ c'16 } .. container:: example **Example 5.** Set ``decrease_durations_monotonically=False`` to express tied values in increasing duration: :: >>> notes = scoretools.make_notes( ... [0], ... [(13, 16)], ... decrease_durations_monotonically=False, ... ) >>> notes Selection(Note("c'16"), Note("c'2.")) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'16 ~ c'2. } .. container:: example **Example 6.** Uses Messiaen-style ties: :: >>> notes = scoretools.make_notes( ... [0], ... [(13, 16)], ... use_messiaen_style_ties=True, ... ) >>> staff = Staff(notes) :: >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { c'2. c'16 \repeatTie } Set `pitches` to a single pitch or a sequence of pitches. Set `durations` to a single duration or a list of durations. Returns selection. ''' from abjad.tools import scoretools from abjad.tools import selectiontools if isinstance(pitches, str): pitches = pitches.split() if not isinstance(pitches, list): pitches = [pitches] if isinstance(durations, (numbers.Number, tuple)): durations = [durations] nonreduced_fractions = [mathtools.NonreducedFraction(_) for _ in durations] size = max(len(nonreduced_fractions), len(pitches)) nonreduced_fractions = sequencetools.repeat_sequence_to_length( nonreduced_fractions, size, ) pitches = sequencetools.repeat_sequence_to_length(pitches, size) Duration = durationtools.Duration durations = Duration._group_nonreduced_fractions_by_implied_prolation( nonreduced_fractions) def _make_unprolated_notes( pitches, durations, decrease_durations_monotonically=decrease_durations_monotonically, use_messiaen_style_ties=False, ): assert len(pitches) == len(durations) result = [] for pitch, duration in zip(pitches, durations): result.extend( scoretools.make_tied_leaf( scoretools.Note, duration, pitches=pitch, decrease_durations_monotonically=decrease_durations_monotonically, use_messiaen_style_ties=use_messiaen_style_ties, ) ) return result result = [] for duration in durations: # get factors in denominator of duration group duration not 1 or 2 factors = set(mathtools.factors(duration[0].denominator)) 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, decrease_durations_monotonically=decrease_durations_monotonically, use_messiaen_style_ties=use_messiaen_style_ties, ) ) else: # compute prolation denominator = duration[0].denominator numerator = mathtools.greatest_power_of_two_less_equal(denominator) multiplier = (numerator, denominator) ratio = 1 / fractions.Fraction(*multiplier) duration = [ratio * durationtools.Duration(d) for d in duration] ns = _make_unprolated_notes( ps, duration, decrease_durations_monotonically=decrease_durations_monotonically, use_messiaen_style_ties=use_messiaen_style_ties, ) t = scoretools.Tuplet(multiplier, ns) result.append(t) # return result result = selectiontools.Selection(result) return result
def test_mathtools_factors_02(): t = mathtools.factors(2) assert t == [1, 2]