import collections
import abjad
from abjadext import rmakers
from .Cursor import Cursor
from .PerformedTimespan import PerformedTimespan
from .SilentTimespan import SilentTimespan
from .TimespanMaker import TimespanMaker
[docs]class BoundaryTimespanMaker(TimespanMaker):
r"""
A boundary timespan-maker.
.. container:: example
>>> timespan_maker = tsmakers.BoundaryTimespanMaker(
... start_talea=rmakers.Talea(
... counts=[1],
... denominator=2,
... ),
... stop_talea=rmakers.Talea(
... counts=[1],
... denominator=4,
... ),
... voice_names=('A', 'B'),
... )
>>> print(abjad.storage(timespan_maker))
tsmakers.BoundaryTimespanMaker(
start_talea=rmakers.Talea(
[1],
2
),
stop_talea=rmakers.Talea(
[1],
4
),
start_anchor=Left,
stop_anchor=Left,
voice_names=('A', 'B'),
)
.. container:: example
>>> timespan_list = abjad.TimespanList([
... tsmakers.PerformedTimespan(
... start_offset=0,
... stop_offset=1,
... voice_name='A',
... ),
... tsmakers.PerformedTimespan(
... start_offset=(1, 2),
... stop_offset=(3, 2),
... voice_name='B',
... ),
... tsmakers.PerformedTimespan(
... start_offset=3,
... stop_offset=4,
... voice_name='B',
... ),
... ])
>>> music_specifiers = {'C': None}
>>> target_timespan = abjad.Timespan(0, 10)
>>> 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((0, 1)),
stop_offset=abjad.Offset((1, 2)),
voice_name='C',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((0, 1)),
stop_offset=abjad.Offset((1, 1)),
voice_name='A',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((1, 2)),
stop_offset=abjad.Offset((3, 2)),
voice_name='B',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((3, 2)),
stop_offset=abjad.Offset((7, 4)),
voice_name='C',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((3, 1)),
stop_offset=abjad.Offset((7, 2)),
voice_name='C',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((3, 1)),
stop_offset=abjad.Offset((4, 1)),
voice_name='B',
),
tsmakers.PerformedTimespan(
start_offset=abjad.Offset((4, 1)),
stop_offset=abjad.Offset((17, 4)),
voice_name='C',
),
]
)
"""
### CLASS VARIABLES ###
__slots__ = (
"_labels",
"_start_anchor",
"_start_talea",
"_start_groupings",
"_stop_anchor",
"_stop_talea",
"_stop_groupings",
"_voice_names",
)
### INITIALIZER ###
def __init__(
self,
start_talea=None,
stop_talea=None,
start_groupings=None,
stop_groupings=None,
start_anchor=abjad.Left,
stop_anchor=abjad.Left,
labels=None,
division_masks=None,
padding=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 start_talea is not None:
if not isinstance(start_talea, rmakers.Talea):
start_duration = abjad.Duration(start_talea)
counts = [start_duration.numerator]
denominator = start_duration.denominator
start_talea = rmakers.Talea(
counts=counts,
denominator=denominator,
)
assert isinstance(start_talea, rmakers.Talea)
assert start_talea.counts
assert all(0 < x for x in start_talea.counts)
self._start_talea = start_talea
if start_groupings is not None:
if not isinstance(start_groupings, collections.Sequence):
start_groupings = (start_groupings,)
start_groupings = tuple(int(x) for x in start_groupings)
assert len(start_groupings)
assert all(0 < x for x in start_groupings)
self._start_groupings = start_groupings
if stop_talea is not None:
if not isinstance(stop_talea, rmakers.Talea):
stop_duration = abjad.Duration(stop_talea)
counts = [stop_duration.numerator]
denominator = stop_duration.denominator
stop_talea = rmakers.Talea(
counts=counts,
denominator=denominator,
)
assert isinstance(stop_talea, rmakers.Talea)
assert stop_talea.counts
assert all(0 < x for x in stop_talea.counts)
self._stop_talea = stop_talea
if stop_groupings is not None:
if not isinstance(stop_groupings, collections.Sequence):
stop_groupings = (stop_groupings,)
stop_groupings = tuple(int(x) for x in stop_groupings)
assert len(stop_groupings)
assert all(0 < x for x in stop_groupings)
self._stop_groupings = stop_groupings
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
assert start_anchor in (abjad.Left, abjad.Right)
self._start_anchor = start_anchor
assert stop_anchor in (abjad.Left, abjad.Right)
self._stop_anchor = stop_anchor
### PRIVATE METHODS ###
def _collect_preexisting_timespans(
self,
target_timespan=None,
timespan_list=None,
):
preexisting_timespans = abjad.TimespanList()
for timespan in timespan_list:
assert isinstance(
timespan,
(
PerformedTimespan,
SilentTimespan,
),
)
if isinstance(timespan, SilentTimespan):
continue
if self.voice_names and timespan.voice_name not in self.voice_names:
continue
if self.labels:
if not timespan.music_specifier:
continue
music_specifier_labels = timespan.music_specifier.labels or ()
for label in self.labels:
if label in music_specifier_labels:
preexisting_timespans.append(timespan)
break
else:
preexisting_timespans.append(timespan)
preexisting_timespans & target_timespan
return preexisting_timespans
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
start_talea = self.start_talea
if start_talea is None:
start_talea = rmakers.Talea((0,), 1)
start_talea = Cursor(start_talea)
start_groupings = self.start_groupings
if start_groupings is None:
start_groupings = (1,)
start_groupings = Cursor(start_groupings)
stop_talea = self.stop_talea
if stop_talea is None:
stop_talea = rmakers.Talea((0,), 1)
stop_talea = Cursor(stop_talea)
stop_groupings = self.stop_groupings
if stop_groupings is None:
stop_groupings = (1,)
stop_groupings = Cursor(stop_groupings)
if self.seed:
if self.seed < 0:
for _ in range(abs(self.seed)):
start_talea.backtrack()
start_groupings.backtrack()
stop_talea.backtrack()
stop_groupings.backtrack()
else:
next(start_talea)
next(start_groupings)
next(stop_talea)
next(stop_groupings)
context_counter = collections.Counter()
preexisting_timespans = self._collect_preexisting_timespans(
target_timespan=target_timespan,
timespan_list=timespan_list,
)
new_timespan_mapping = {}
for group_index, group in enumerate(preexisting_timespans.partition(True)):
for context_name, music_specifier in music_specifiers.items():
if context_name not in new_timespan_mapping:
continue
new_timespan_mapping[context_name] - group.timespan
for context_name, music_specifier in music_specifiers.items():
if context_name not in new_timespan_mapping:
new_timespan_mapping[context_name] = abjad.TimespanList()
context_seed = context_counter[context_name]
start_durations = []
for _ in range(next(start_groupings)):
start_durations.append(next(start_talea))
stop_durations = []
for _ in range(next(stop_groupings)):
stop_durations.append(next(stop_talea))
start_timespans, stop_timespans = (), ()
if start_durations:
group_start = group.start_offset
if self.start_anchor is abjad.Right:
# print('!!!', float(group_start), float(group_start -
# sum(start_durations)))
group_start -= sum(start_durations)
start_timespans = music_specifier(
durations=start_durations,
layer=layer,
division_masks=self.division_masks,
padding=self.padding,
seed=context_seed,
start_offset=group_start,
timespan_specifier=self.timespan_specifier,
voice_name=context_name,
)
context_counter[context_name] += 1
if stop_durations:
group_stop = group.stop_offset
if self.stop_anchor is abjad.Right:
group_stop -= sum(stop_durations)
stop_timespans = music_specifier(
durations=stop_durations,
layer=layer,
division_masks=self.division_masks,
padding=self.padding,
seed=context_seed,
start_offset=group_stop,
timespan_specifier=self.timespan_specifier,
voice_name=context_name,
)
context_counter[context_name] += 1
# if start_timespans and stop_timespans:
# start_timespans & group.timespan
new_timespan_mapping[context_name].extend(start_timespans)
new_timespan_mapping[context_name].extend(stop_timespans)
for context_name, timespans in new_timespan_mapping.items():
timespans.compute_logical_or()
new_timespans.extend(timespans)
return new_timespans
### PUBLIC PROPERTIES ###
@property
def labels(self):
return self._labels
@property
def start_anchor(self):
return self._start_anchor
@property
def start_talea(self):
return self._start_talea
@property
def stop_anchor(self):
return self._stop_anchor
@property
def stop_talea(self):
return self._stop_talea
@property
def start_groupings(self):
return self._start_groupings
@property
def stop_groupings(self):
return self._stop_groupings
@property
def voice_names(self):
return self._voice_names