Source code for tsmakers.DependentTimespanMaker

import collections

import abjad

from .PerformedTimespan import PerformedTimespan
from .TimespanMaker import TimespanMaker


[docs]class DependentTimespanMaker(TimespanMaker): r"""A dependent timespan-maker. .. container:: example >>> timespan_maker = tsmakers.DependentTimespanMaker( ... include_inner_starts=True, ... include_inner_stops=True, ... voice_names=( ... 'A', ... ), ... ) >>> print(abjad.storage(timespan_maker)) tsmakers.DependentTimespanMaker( include_inner_starts=True, include_inner_stops=True, voice_names=('A',), ) .. container:: example >>> timespan_list = abjad.TimespanList([ ... tsmakers.PerformedTimespan( ... voice_name='A', ... start_offset=(1, 4), ... stop_offset=(1, 1), ... ), ... tsmakers.PerformedTimespan( ... voice_name='A', ... start_offset=(3, 4), ... stop_offset=(3, 2), ... ), ... ]) >>> music_specifiers = { ... 'B': None, ... 'C': None, ... } >>> target_timespan = abjad.Timespan((1, 2), (2, 1)) >>> timespan_list = timespan_maker( ... music_specifiers=music_specifiers, ... target_timespan=target_timespan, ... timespan_list=timespan_list, ... ) >>> ts_list = abjad.TimespanList( ... [ ... abjad.AnnotatedTimespan( ... start_offset=_.start_offset, ... stop_offset=_.stop_offset, ... annotation=_.voice_name, ... ) ... for _ in timespan_list ... ] ... ) >>> abjad.show(ts_list, scale=0.5, key="annotation") # doctest: +SKIP .. docs:: >>> print(abjad.storage(timespan_list)) abjad.TimespanList( [ tsmakers.PerformedTimespan( start_offset=abjad.Offset((1, 4)), stop_offset=abjad.Offset((1, 1)), voice_name='A', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((1, 2)), stop_offset=abjad.Offset((3, 4)), voice_name='B', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((1, 2)), stop_offset=abjad.Offset((3, 4)), voice_name='C', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((3, 4)), stop_offset=abjad.Offset((1, 1)), voice_name='B', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((3, 4)), stop_offset=abjad.Offset((1, 1)), voice_name='C', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((3, 4)), stop_offset=abjad.Offset((3, 2)), voice_name='A', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((1, 1)), stop_offset=abjad.Offset((3, 2)), voice_name='B', ), tsmakers.PerformedTimespan( start_offset=abjad.Offset((1, 1)), stop_offset=abjad.Offset((3, 2)), voice_name='C', ), ] ) """ ### CLASS VARIABLES ### __slots__ = ( "_hysteresis", "_include_inner_starts", "_include_inner_stops", "_inspect_music", "_labels", "_rotation_indices", "_voice_names", ) ### INITIALIZER ### def __init__( self, hysteresis=None, include_inner_starts=None, include_inner_stops=None, inspect_music=None, labels=None, division_masks=None, padding=None, rotation_indices=None, seed=None, timespan_specifier=None, voice_names=None, ): TimespanMaker.__init__( self, division_masks=division_masks, padding=padding, seed=seed, timespan_specifier=timespan_specifier, ) if hysteresis is not None: hysteresis = abjad.Duration(hysteresis) assert 0 < hysteresis self._hysteresis = hysteresis if include_inner_starts is not None: include_inner_starts = bool(include_inner_starts) self._include_inner_starts = include_inner_starts if include_inner_stops is not None: include_inner_stops = bool(include_inner_stops) self._include_inner_stops = include_inner_stops if inspect_music is not None: inspect_music = bool(inspect_music) self._inspect_music = inspect_music if rotation_indices is not None: if not isinstance(rotation_indices, collections.Sequence): rotation_indices = int(rotation_indices) rotation_indices = (rotation_indices,) rotation_indices = tuple(rotation_indices) self._rotation_indices = rotation_indices if labels is not None: if isinstance(labels, str): labels = (labels,) labels = tuple(str(_) for _ in labels) self._labels = labels if voice_names is not None: voice_names = tuple(voice_names) self._voice_names = voice_names ### PRIVATE METHODS ### def _collect_preexisting_timespans( self, target_timespan=None, timespan_list=None, ): preexisting_timespans = abjad.TimespanList() for timespan in timespan_list: if not isinstance(timespan, PerformedTimespan): continue if self.voice_names and timespan.voice_name not in self.voice_names: continue if not self.labels: pass elif ( not hasattr(timespan, "music_specifier") or not timespan.music_specifier or not timespan.music_specifier.labels ): continue elif not any( label in timespan.music_specifier.labels for label in self.labels ): continue preexisting_timespans.append(timespan) if self.inspect_music and timespan.music: outer_start_offset = timespan.start_offset inner_start_offset = abjad.get.timespan(timespan.music).start_offset assert inner_start_offset == 0 for division in timespan.music: division_timespan = abjad.get.timespan(division) division_timespan = division_timespan.translate(outer_start_offset) preexisting_timespans.append(division_timespan) preexisting_timespans & target_timespan return preexisting_timespans def _partition_preexisting_timespans(self, timespans): shards = timespans.partition(include_tangent_timespans=True) if not self.hysteresis or not shards: return shards coalesced_shards = [shards[0]] for shard in shards[1:]: last_stop = coalesced_shards[-1].stop_offset this_start = shard.start_offset gap = this_start - last_stop if self.hysteresis <= gap: coalesced_shards.append(shard) else: coalesced_shards[-1].extend(shard) coalesced_shards[-1].sort() return coalesced_shards def _make_timespans( self, layer=None, music_specifiers=None, target_timespan=None, timespan_list=None, ): new_timespans = abjad.TimespanList() if not self.voice_names and not self.labels: return new_timespans rotation_indices = self.rotation_indices or (0,) rotation_indices = abjad.CyclicTuple(rotation_indices) context_counter = collections.Counter() preexisting_timespans = self._collect_preexisting_timespans( target_timespan=target_timespan, timespan_list=timespan_list, ) partitioned_timespans = self._partition_preexisting_timespans( preexisting_timespans ) for group_index, group in enumerate(partitioned_timespans): rotation_index = rotation_indices[group_index] offsets = set() offsets.add(group.start_offset) offsets.add(group.stop_offset) for timespan in group: if self.include_inner_starts: offsets.add(timespan.start_offset) if self.include_inner_stops: offsets.add(timespan.stop_offset) offsets = tuple(sorted(offsets)) durations = abjad.Sequence(abjad.math.difference_series(offsets)) durations = durations.rotate(rotation_index) start_offset = offsets[0] for context_name, music_specifier in music_specifiers.items(): context_seed = context_counter[context_name] timespans = music_specifier( durations=durations, layer=layer, division_masks=self.division_masks, padding=self.padding, seed=context_seed, start_offset=start_offset, timespan_specifier=self.timespan_specifier, voice_name=context_name, ) context_counter[context_name] += 1 new_timespans.extend(timespans) return new_timespans ### PUBLIC PROPERTIES ### @property def hysteresis(self): return self._hysteresis @property def include_inner_starts(self): return self._include_inner_starts @property def include_inner_stops(self): return self._include_inner_stops @property def inspect_music(self): return self._inspect_music @property def is_dependent(self): return True @property def labels(self): return self._labels @property def rotation_indices(self): return self._rotation_indices @property def voice_names(self): return self._voice_names