def repeat_to_duration(self, duration): r'''Repeat payload expression to duration. :: >>> result = \ ... payload_expression.repeat_to_duration(Duration(13, 16)) :: >>> print(format(result)) musicexpressiontools.IterablePayloadExpression( payload=( durationtools.Division(4, 16), durationtools.Division(2, 16), durationtools.Division(4, 16), durationtools.Division(2, 16), durationtools.Division(1, 16), ), ) Returns newly constructed payload expression. ''' if not mathtools.all_are_numbers(self.payload): payload = [durationtools.Division(_) for _ in self.payload] else: payload = self.payload payload = sequencetools.repeat_sequence_to_weight(payload, duration) payload = [durationtools.Division(_) for _ in payload] result = new(self, payload=payload) return result
def _to_divisions(expr, start_offset=None): if isinstance(expr, durationtools.Division): result = durationtools.Division(expr) if start_offset is not None: result._start_offset = start_offset start_offset += result.duration elif isinstance(expr, mathtools.NonreducedFraction): result = durationtools.Division(expr.pair) if start_offset is not None: result._start_offset = start_offset start_offset += result.duration elif hasattr(expr, 'pair'): result = durationtools.Division(expr.pair) if start_offset is not None: result._start_offset = start_offset start_offset += result.duration elif isinstance(expr, tuple): result = durationtools.Division(expr) if start_offset is not None: result._start_offset = start_offset start_offset += result.duration elif isinstance(expr, (list, sequencetools.Sequence)): result = [] for element in expr: new_element, start_offset = DivisionMaker._to_divisions( element, start_offset=start_offset, ) result.append(new_element) result = type(expr)(result) else: raise TypeError(repr(expr)) return result, start_offset
def __call__(self, divisions=None): r'''Calls rounded ratio division maker on `division`. .. container:: example **Example 1.** Calls maker on nonempty input: :: >>> maker = makertools.SplitByRoundedRatiosDivisionCallback( ... ratios=[mathtools.Ratio([1, 1])], ... ) >>> lists = maker([(7, 4), (6, 4)]) >>> for list_ in lists: ... list_ [Division(4, 4), Division(3, 4)] [Division(3, 4), Division(3, 4)] Returns list of division lists. .. container:: example **Example 2.** Calls maker on empty input: :: >>> maker = makertools.SplitByRoundedRatiosDivisionCallback( ... ratios=[mathtools.Ratio([1, 1])], ... ) >>> maker([]) [] Returns empty list. Returns possibly empty list of division lists. ''' input_divisions = divisions or [] if not input_divisions: return [] output_division_lists = [] ratios = self._get_ratios() for i, input_division in enumerate(input_divisions): input_division = durationtools.Division(input_division) ratio = ratios[i] numerators = mathtools.partition_integer_by_ratio( input_division.numerator, ratio, ) output_division_list = [ durationtools.Division( numerator, input_division.denominator, ) for numerator in numerators ] output_division_lists.append(output_division_list) return output_division_lists
def _coerce_divisions(self, divisions): nonreduced_fractions = [] for division in divisions: if hasattr(division, 'time_signature'): nonreduced_fraction = durationtools.Division( division.time_signature.pair ) else: nonreduced_fraction = durationtools.Division(division) nonreduced_fractions.append(nonreduced_fraction) return nonreduced_fractions
def __call__(self, divisions=None): r'''Calls beat division maker on `divisions`. .. container:: example **Example 1.** Calls maker on nonempty input: :: >>> maker = makertools.BeatDivisionMaker( ... depths=[1], ... ) >>> lists = maker([(7, 4), (6, 4)]) >>> for list_ in lists: ... list_ [Division(3, 4), Division(2, 4), Division(2, 4)] [Division(3, 4), Division(3, 4)] Returns list of division lists. .. container:: example **Example 2.** Calls maker on empty input: :: >>> maker = makertools.BeatDivisionMaker( ... depths=[1], ... ) >>> lists = maker([]) >>> lists [] Returns empty list. Returns (possibly empty) list of division lists. ''' input_divisions = divisions or [] if not input_divisions: return [] output_division_lists = [] depths = self._get_depths() for i, input_division in enumerate(input_divisions): input_division = durationtools.Division(input_division) depth = depths[i] meter = metertools.Meter(input_division) durations = meter.get_durations_at_depth(depth) denominator = input_division.denominator output_division_list = [ durationtools.Division(_.with_denominator(denominator)) for _ in durations ] output_division_lists.append(output_division_list) return output_division_lists
def _beat_list_to_grouped_beat_list(self, beat_list): assert isinstance(beat_list, (list, tuple)), repr(beat_list) beat_list_ = [] for beat in beat_list: if hasattr(beat, 'duration'): beat = durationtools.Division(beat.duration) else: beat = durationtools.Division(beat) beat_list_.append(beat) beat_list = beat_list_ total_duration = sum(beat_list) total_duration = durationtools.Duration(total_duration) if (total_duration.is_assignable and self.fuse_assignable_total_duration): return [[durationtools.Division(total_duration)]] if self.counts is None: beat_group = list(beat_list) grouped_beat_list = [beat_group] return grouped_beat_list grouped_beat_list = sequencetools.partition_sequence_by_counts( beat_list, counts=self.counts, cyclic=True, overhang=False, ) beats_included = sum([len(_) for _ in grouped_beat_list]) if beats_included == len(beat_list): return grouped_beat_list remainder_length = len(beat_list) - beats_included if self.remainder_direction == Left: grouped_beat_list = sequencetools.partition_sequence_by_counts( beat_list[remainder_length:], counts=self.counts, cyclic=True, overhang=False) remainder = beat_list[:remainder_length] if self.append_remainder: grouped_beat_list[0] = remainder + grouped_beat_list[0] else: grouped_beat_list.insert(0, remainder) else: grouped_beat_list = sequencetools.partition_sequence_by_counts( beat_list[:-remainder_length], counts=self.counts, cyclic=True, overhang=False) remainder = beat_list[-remainder_length:] if self.append_remainder: grouped_beat_list[-1] = grouped_beat_list[-1] + remainder else: grouped_beat_list.append(remainder) return grouped_beat_list
def __init__( self, cyclic=True, pattern=(), pattern_rotation_index=0, remainder=Right, remainder_fuse_threshold=None, ): assert isinstance(cyclic, bool), repr(cyclic) self._cyclic = cyclic pattern = pattern or () pattern_ = [] for division in pattern: division = durationtools.Division(division) pattern_.append(division) pattern = tuple(pattern_) self._pattern = pattern assert remainder in (Left, Right), repr(remainder) self._remainder = remainder assert isinstance(pattern_rotation_index, int) self._pattern_rotation_index = pattern_rotation_index if remainder_fuse_threshold is not None: remainder_fuse_threshold = durationtools.Duration( remainder_fuse_threshold, ) self._remainder_fuse_threshold = remainder_fuse_threshold
def _time_signatures_to_naive_beats(self, time_signatures): naive_beats = [] for time_signature in time_signatures: numerator, denominator = time_signature.pair naive_beats.extend( numerator * [durationtools.Division(1, denominator)]) return naive_beats
def __call__(self, divisions, rotation=None): r'''Calls rhythm-maker. Makes music as a list of selections. Applies cross-division ties (when specified by tie specifier). Other types of ties specified by tie specifier must be applied by child classes. Validates output type. Returns list of selections. ''' divisions = [durationtools.Division(x) for x in divisions] rotation = self._to_tuple(rotation) self._rotation = rotation selections = self._make_music( divisions, rotation, ) self._simplify_tuplets(selections) selections = self._flatten_trivial_tuplets(selections) self._apply_tie_specifier(selections) self._validate_selections(selections) self._validate_tuplets(selections) return selections
def __init__( self, compound_meter_multiplier=durationtools.Multiplier(1), cyclic=True, durations=(), pattern_rotation_index=0, remainder=Right, remainder_fuse_threshold=None, ): compound_meter_multiplier = durationtools.Multiplier( compound_meter_multiplier) self._compound_meter_multiplier = compound_meter_multiplier assert isinstance(cyclic, bool), repr(cyclic) self._cyclic = cyclic durations = durations or () pattern_ = [] for division in durations: division = durationtools.Division(division) pattern_.append(division) durations = tuple(pattern_) self._pattern = durations assert remainder in (Left, Right), repr(remainder) self._remainder = remainder assert isinstance(pattern_rotation_index, int) self._pattern_rotation_index = pattern_rotation_index if remainder_fuse_threshold is not None: remainder_fuse_threshold = durationtools.Duration( remainder_fuse_threshold, ) self._remainder_fuse_threshold = remainder_fuse_threshold self._callbacks = ()
def __init__(self, divisions, start_offset=None, voice_name=None): from experimental.tools import musicexpressiontools assert isinstance(voice_name, (str, type(None))), repr(voice_name) start_offset = start_offset or durationtools.Offset(0) start_offset = durationtools.Offset(start_offset) self._start_offset = start_offset divisions = [durationtools.Division(_) for _ in divisions] self._divisions = divisions self._voice_name = voice_name
def _coerce_divisions(self, divisions): divisions_ = [] for division in divisions: if hasattr(division, 'time_signature'): argument = division.time_signature.pair elif hasattr(division, 'duration'): argument = division.duration else: argument = division division_ = durationtools.Division(argument) divisions_.append(division_) return divisions_
def _make_music(self, divisions, seeds): #assert not seeds, repr(seeds) if seeds is None: seeds = 0 selections = [] divisions = [durationtools.Division(_) for _ in divisions] denominators = datastructuretools.CyclicTuple(self.denominators) extra_counts_per_division = self.extra_counts_per_division or (0,) extra_counts_per_division = datastructuretools.CyclicTuple( extra_counts_per_division ) for i, division in enumerate(divisions, seeds): # not yet extended to work with non-power-of-two divisions assert mathtools.is_positive_integer_power_of_two( division.denominator), repr(division) denominator = denominators[i] extra_count = extra_counts_per_division[i] basic_duration = durationtools.Duration(1, denominator) unprolated_note_count = None if division < 2 * basic_duration: notes = scoretools.make_notes([0], [division]) else: unprolated_note_count = division / basic_duration unprolated_note_count = int(unprolated_note_count) unprolated_note_count = unprolated_note_count or 1 if 0 < extra_count: modulus = unprolated_note_count extra_count = extra_count % modulus elif extra_count < 0: modulus = int(math.ceil(unprolated_note_count / 2.0)) extra_count = abs(extra_count) % modulus extra_count *= -1 note_count = unprolated_note_count + extra_count durations = note_count * [basic_duration] notes = scoretools.make_notes([0], durations) assert all( _.written_duration.denominator == denominator for _ in notes ) tuplet_duration = durationtools.Duration(division) tuplet = scoretools.FixedDurationTuplet( duration=tuplet_duration, music=notes, ) if unprolated_note_count is not None: preferred_denominator = unprolated_note_count tuplet.preferred_denominator = preferred_denominator selection = selectiontools.Selection(tuplet) selections.append(selection) self._apply_beam_specifier(selections) return selections
def __call__(self, expr=None): r'''Makes divisions from `expr`. Pass in `expr` as either a list of divisions or as a list of division lists. Returns either a list of divisions or a list of division lists. ''' expr = expr or [] expr = list(expr) assert isinstance(expr, list), repr(expr) if self._is_flat_list(expr): expr = [durationtools.Division(_) for _ in expr] for callback in self.callbacks: expr = callback(expr) return expr
def evaluate(self): r'''Evaluate division region expression. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools divisions = self.source_expression[:] divisions = [durationtools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def make_time_signatures(self): from experimental.tools import musicexpressiontools if hasattr(self.source_expression, 'evaluate_early'): expression = self.source_expression.evaluate_early() assert isinstance( expression, musicexpressiontools.IterablePayloadExpression) time_signatures = expression.payload else: expression = self.source_expression.evaluate() assert isinstance( expression, musicexpressiontools.IterablePayloadExpression) time_signatures = expression.payload[:] time_signatures = [durationtools.Division(x) for x in time_signatures] if time_signatures: self.root_specification._time_signatures = time_signatures[:] return time_signatures
def evaluate_early(self): r'''Evaluate measure select expression early. Special definition because time signatures can be evaluated without knowing the timespan they occupy. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools time_signatures = self.root_specification.time_signatures[:] time_signatures = [durationtools.Division(x) for x in time_signatures] expression = \ musicexpressiontools.IterablePayloadExpression(time_signatures) expression = self._apply_callbacks(expression) assert isinstance( expression, musicexpressiontools.IterablePayloadExpression) return expression
def evaluate(self): r'''Evaluate literal division region expression. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools divisions = [] for x in self.source_expression: if hasattr(x, 'duration'): x = x.duration division = durationtools.Division(x) divisions.append(division) divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def evaluate(self): r'''Evaluate select expression division region expression. Returns none when nonevaluable. Returns start-positioned division payload expression when evaluable. ''' from experimental.tools import musicexpressiontools expression = self.source_expression.evaluate() if expression is not None: divisions = expression.elements divisions = [durationtools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = \ musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def __and__(self, timespan): r'''Logical AND of payload expression and `timespan`. :: >>> timespan = timespantools.Timespan((1, 16), (5, 16)) >>> result = payload_expression & timespan :: >>> print(format(result)) timespantools.TimespanInventory( [ musicexpressiontools.IterablePayloadExpression( payload=( durationtools.Division(3, 16), durationtools.Division(1, 16), ), ), ] ) Returns newly constructed payload expression. ''' from experimental.tools import musicexpressiontools if not mathtools.all_are_numbers(self.payload): payload = [durationtools.Division(x) for x in self.payload] else: payload = self.payload division_payload_expression = \ musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=payload, start_offset=0, voice_name='dummy voice name') result = division_payload_expression & timespan assert len(result) in (0, 1) if result: divisions = result[0].payload.divisions expression = new(self, payload=divisions) result[0] = expression return result
def evaluate(self): r'''Evaluate measure select expression. Returns none when nonevaluable. Returns start-positioned division payload expression when evaluable. ''' from experimental.tools import musicexpressiontools anchor_timespan = self._evaluate_anchor_timespan() time_relation = self._get_time_relation(anchor_timespan) time_signatures = self.root_specification.time_signatures[:] time_signatures = [durationtools.Division(x) for x in time_signatures] start_offset = self.root_specification.timespan.start_offset expression = \ musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=time_signatures, start_offset=start_offset, ) callback_cache = self.score_specification.interpreter.callback_cache expression = expression.get_elements_that_satisfy_time_relation( time_relation, callback_cache) expression = self._apply_callbacks(expression) #expression._voice_name = self.voice_name return expression
def __call__(self, divisions=None): r'''Calls division-maker on `divisions`. .. container:: example **Example 1.** Division without remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division(1, 4), Division(1, 4), Division(1, 4)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 2.** Division with remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(7, 8)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division(1, 4), Division(1, 4), Division(1, 4), Division(1, 8)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 7/8 c'4 c'4 c'4 c'8 } } Positions remainder at right of output because divison-maker `remainder` defaults to right. .. container:: example **Example 3.** Multiple divisions: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(2, 4), (3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division(1, 4), Division(1, 4)] [Division(1, 4), Division(1, 4), Division(1, 4)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 2/4 c'4 c'4 } { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 4.** No durations: :: >>> maker = makertools.SplitByDurationsDivisionCallback() :: >>> time_signatures = [(6, 32)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division(6, 32)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 6/32 c'8. } } Returns input division unchanged. .. container:: example **Example 5.** Empty input: :: >>> maker = makertools.SplitByDurationsDivisionCallback(durations=[(1, 4)]) >>> maker() [] Returns empty list. Returns possibly empty list of division lists. ''' divisions = divisions or [] if not divisions: return divisions division_lists = [] for i, division in enumerate(divisions): input_division = durationtools.Division(division) input_duration = durationtools.Duration(input_division) input_meter = metertools.Meter(input_division) assert 0 < input_division, repr(input_division) if not self.durations: division_list = [input_division] division_lists.append(division_list) continue if input_meter.is_simple or not self.durations: durations = self.durations[:] elif input_meter.is_compound: multiplier = self.compound_meter_multiplier durations = [ durationtools.Division(multiplier * _) for _ in self.durations ] #division_list = list(self.durations) division_list = list(durations) pattern_rotation_index = self.pattern_rotation_index or 0 pattern_rotation_index *= i division_list = sequencetools.rotate_sequence( division_list, pattern_rotation_index, ) if self.cyclic: division_list = sequencetools.repeat_sequence_to_weight( division_list, input_division, allow_total=Less, ) total_duration = durationtools.Duration(sum(division_list)) if total_duration == input_duration: division_lists.append(division_list) continue if self.remainder is None: message = 'can not fill {} from {} exactly.' #message = message.format(input_division, self.durations) message = message.format(input_division, durations) raise Exception(message) remainder = input_division - total_duration remainder = durationtools.Duration(remainder) remainder = durationtools.Division(remainder) if self.remainder == Left: if self.remainder_fuse_threshold is None: division_list.insert(0, remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[0] + remainder fused_value = durationtools.Division(fused_value) division_list[0] = fused_value else: division_list.insert(0, remainder) elif self.remainder == Right: if self.remainder_fuse_threshold is None: division_list.append(remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[-1] + remainder fused_value = durationtools.Division(fused_value) division_list[-1] = fused_value else: division_list.append(remainder) else: raise ValueError((self.remainder, remainder)) total_duration = durationtools.Duration(sum(division_list)) pair = total_duration, input_duration assert total_duration == input_duration, pair division_lists.append(division_list) for _ in division_lists: assert isinstance(_, list), repr(_) return division_lists
def __call__(self, divisions=None): r'''Calls fuse-by-counts division callback. .. container:: example **Example 1.** Returns divisions unfused: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts() :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> divisions = division_maker(input_divisions) >>> divisions [Division(2, 8), Division(2, 8), Division(4, 8), Division(4, 8), Division(2, 4)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 2/8 c'4 } { c'4 } { \time 4/8 c'2 } { c'2 } { \time 2/4 c'2 } } .. container:: example **Example 2.** Fuses divisions two at a time: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=[2], ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> divisions = division_maker(input_divisions) >>> divisions [Division(4, 8), Division(8, 8), Division(2, 4)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'2 c'1 c'2 } .. container:: example **Example 3a.** Fuses divisions two at a time. Then splits fused divisions by ``3/16`` durations. Remainders to the right: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=[2], ... ) >>> division_maker = division_maker.split_by_durations( ... durations=[Duration(3, 16)], ... remainder=Right, ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> division_lists = division_maker(input_divisions) >>> for division_list in division_lists: ... division_list [Division(3, 16), Division(3, 16), Division(1, 8)] [Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(1, 16)] [Division(3, 16), Division(3, 16), Division(1, 8)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'8. c'8. c'8 c'8. c'8. c'8. c'8. c'8. c'16 c'8. c'8. c'8 } **Example 3b.** Remainders to the left: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=[2], ... ) >>> division_maker = division_maker.split_by_durations( ... durations=[Duration(3, 16)], ... remainder=Left, ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> division_lists = division_maker(input_divisions) >>> for division_list in division_lists: ... division_list [Division(1, 8), Division(3, 16), Division(3, 16)] [Division(1, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16)] [Division(1, 8), Division(3, 16), Division(3, 16)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'8 c'8. c'8. c'16 c'8. c'8. c'8. c'8. c'8. c'8 c'8. c'8. } .. container:: example **Example 4.** Fuses all divisions: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=mathtools.Infinity, ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> divisions = division_maker(input_divisions) >>> divisions [Division(16, 8)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'\breve } .. container:: example **Example 5a.** Fuses all divisions. Then splits fused divisions by ``3/8`` durations: Remainder at right: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=mathtools.Infinity, ... ) >>> division_maker = division_maker.split_by_durations( ... durations=[Duration(3, 16)], ... remainder=Right, ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> division_lists = division_maker(input_divisions) >>> for division_list in division_lists: ... division_list [Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(1, 8)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8 } **Example 5b.** Remainder at left: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=mathtools.Infinity, ... ) >>> division_maker = division_maker.split_by_durations( ... durations=[Duration(3, 16)], ... remainder=Left, ... ) :: >>> input_divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> division_lists = division_maker(input_divisions) >>> for division_list in division_lists: ... division_list [Division(1, 8), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16), Division(3, 16)] :: >>> rhythm_maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = rhythm_maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=input_divisions, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = rhythm_maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { c'8 c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. c'8. } .. container:: example **Example 6.** Empty input: :: >>> input_divisions = [] >>> division_lists = division_maker(input_divisions) >>> for division_list in division_lists: ... division_list Returns list of division lists. ''' divisions = divisions or () divisions = self._coerce_divisions(divisions) if not divisions: pass elif (self.counts == mathtools.Infinity or self.counts == mathtools.Infinity()): divisions = [sum(divisions)] elif self.counts: parts = sequencetools.partition_sequence_by_counts( divisions, self.counts, cyclic=self.cyclic, overhang=True, ) divisions = [sum(_) for _ in parts] divisions = [durationtools.Division(_) for _ in divisions] if self.secondary_division_maker is None: return divisions division_lists = [] for division in divisions: if self.secondary_division_maker is not None: division_list = self.secondary_division_maker([division])[0] else: division_list = [division] division_list = [durationtools.Division(_) for _ in division_list] division_lists.append(division_list) return division_lists
def __call__(self, divisions=None): r'''Calls rounded ratio division maker on `divisions`. .. container:: example **Example 1.** Calls maker on nonempty input: :: >>> maker = makertools.SplitByRoundedRatiosDivisionCallback( ... ratios=[mathtools.Ratio([1, 1])], ... ) >>> lists = maker([(7, 4), (6, 4)]) >>> for list_ in lists: ... list_ [Division((4, 4)), Division((3, 4))] [Division((3, 4)), Division((3, 4))] Returns list of division lists. .. container:: example **Example 2.** Calls maker on empty input: :: >>> maker = makertools.SplitByRoundedRatiosDivisionCallback( ... ratios=[mathtools.Ratio([1, 1])], ... ) >>> maker([]) [] Returns empty list. .. container:: example **Example 3.** Works with start offset: :: >>> maker = makertools.SplitByRoundedRatiosDivisionCallback( ... ratios=[mathtools.Ratio([1, 1])], ... ) :: >>> divisions = [(7, 4), (6, 4)] >>> divisions = [durationtools.Division(_) for _ in divisions] >>> divisions[0]._start_offset = Offset(1, 4) >>> divisions [Division((7, 4), start_offset=Offset(1, 4)), Division((6, 4))] :: >>> division_lists = maker(divisions) >>> len(division_lists) 2 :: >>> for division in division_lists[0]: ... division Division((4, 4), start_offset=Offset(1, 4)) Division((3, 4), start_offset=Offset(5, 4)) :: >>> for division in division_lists[1]: ... division Division((3, 4), start_offset=Offset(2, 1)) Division((3, 4), start_offset=Offset(11, 4)) Returns possibly empty list of division lists. ''' from experimental import makertools divisions = divisions or [] if not divisions: return [] divisions, start_offset = makertools.DivisionMaker._to_divisions( divisions) start_offset = divisions[0].start_offset division_lists = [] ratios = self._get_ratios() for i, division in enumerate(divisions): ratio = ratios[i] numerators = mathtools.partition_integer_by_ratio( division.numerator, ratio, ) division_list = [ durationtools.Division((numerator, division.denominator)) for numerator in numerators ] division_lists.append(division_list) division_lists, start_offset = makertools.DivisionMaker._to_divisions( division_lists, start_offset=start_offset, ) return division_lists
def __call__(self, divisions=None): r'''Calls division-maker on `divisions`. .. container:: example Calls division-maker on division with no remainder: :: >>> maker = makertools.DivisionMaker( ... cyclic=True, ... pattern=[(1, 4)], ... ) >>> lists = maker([(3, 4)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4), Division(1, 4)] .. container:: example Calls division-maker cyclically on each division. Positions remainders to the right of each output list: :: >>> maker = makertools.DivisionMaker( ... pattern=[(1, 4)], ... ) >>> lists = maker([(7, 8)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4), Division(1, 4), Division(1, 8)] Positions remainder at right of output because divison-maker `remainder` defaults to right. .. container:: example Calls division-maker with pattern set to none: :: >>> maker = makertools.DivisionMaker() >>> lists = maker([(6, 32)]) >>> for list_ in lists: ... list_ [Division(6, 32)] Returns input division unchanged. .. container:: example Calls division-maker on nothing: :: >>> maker = makertools.DivisionMaker(pattern=[(1, 4)]) >>> maker() [] Returns empty list. .. container:: example Call division-maker on multiple divisions: :: >>> maker = makertools.DivisionMaker( ... cyclic=True, ... pattern=[(1, 4)], ... ) >>> lists = maker([(2, 4), (3, 4)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4)] [Division(1, 4), Division(1, 4), Division(1, 4)] Returns possibly empty list of division lists. ''' divisions = divisions or [] if not divisions: return [] division_lists = [] for i, division in enumerate(divisions): input_division = durationtools.Division(division) input_duration = durationtools.Duration(input_division) assert 0 < input_division, repr(input_division) if not self.pattern: division_list = [input_division] division_lists.append(division_list) continue division_list = list(self.pattern) pattern_rotation_index = self.pattern_rotation_index or 0 pattern_rotation_index *= i division_list = sequencetools.rotate_sequence( division_list, pattern_rotation_index, ) if self.cyclic: division_list = sequencetools.repeat_sequence_to_weight( division_list, input_division, allow_total=Less, ) total_duration = durationtools.Duration(sum(division_list)) if total_duration == input_duration: division_lists.append(division_list) continue if self.remainder is None: message = 'can not fill {} from {} exactly.' message = message.format(input_division, self.pattern) raise Exception(message) remainder = input_division - total_duration remainder = durationtools.Duration(remainder) remainder = durationtools.Division(remainder) if self.remainder == Left: if self.remainder_fuse_threshold is None: division_list.insert(0, remainder) elif remainder <= self.remainder_fuse_threshold: #division_list[0] += remainder fused_value = division_list[0] + remainder fused_value = durationtools.Division(fused_value) division_list[0] = fused_value else: division_list.insert(0, remainder) elif self.remainder == Right: if self.remainder_fuse_threshold is None: division_list.append(remainder) elif remainder <= self.remainder_fuse_threshold: #division_list[-1] += remainder fused_value = division_list[-1] + remainder fused_value = durationtools.Division(fused_value) division_list[-1] = fused_value else: division_list.append(remainder) else: raise ValueError((self.remainder, remainder)) total_duration = durationtools.Duration(sum(division_list)) pair = total_duration, input_duration assert total_duration == input_duration, pair division_lists.append(division_list) return division_lists