"""
Tools for modeling IRCAM-style rhythm trees.
"""
import fractions
import uqbar.containers
import uqbar.graphs
from . import duration as _duration
from . import makers as _makers
from . import math as _math
from . import mutate as _mutate
from . import score as _score
from . import sequence as _sequence
from . import spanners as _spanners
from .parsers.base import Parser
[docs]class RhythmTreeMixin:
"""
Abstract rhythm-tree node.
"""
### CLASS VARIABLES ###
_is_abstract = True
_state_flag_names: tuple[str, ...] = ("_offsets_are_current",)
### INITIALIZER ###
def __init__(self, preprolated_duration=_duration.Duration(1)):
self._duration = 0
self._offset = _duration.Offset(0)
self._offsets_are_current = False
self.preprolated_duration = preprolated_duration
### PRIVATE METHODS ###
def _depthwise_inventory(self):
def recurse(node):
if node.depth not in inventory:
inventory[node.depth] = []
inventory[node.depth].append(node)
if getattr(node, "children", None) is not None:
for child in node.children:
recurse(child)
inventory = {}
recurse(self)
return inventory
def _get_fraction_string(self):
n, d = self.pair
if d == 1:
string = str(n)
else:
string = f"{n}/{d}"
return string
def _update_offsets_of_entire_tree(self):
def recurse(container, current_offset):
container._offset = current_offset
container._offsets_are_current = True
for child in container:
if getattr(child, "children", None) is not None:
current_offset = recurse(child, current_offset)
else:
child._offset = current_offset
child._offsets_are_current = True
# current_offset += child.duration
current_offset += _duration.Duration(child.duration)
return current_offset
offset = _duration.Offset(0)
root = self.root
if root is None:
root = self
if root is self and not hasattr(self, "children"):
self._offset = offset
self._offsets_are_current = True
else:
recurse(root, offset)
def _update_offsets_of_entire_tree_if_necessary(self):
if not self._get_node_state_flags()["_offsets_are_current"]:
self._update_offsets_of_entire_tree()
### PUBLIC PROPERTIES ###
@property
def duration(self) -> _duration.Duration | tuple[int, int]:
"""
Gets node duration.
.. container:: example
>>> string = '(1 ((1 (1 1)) (1 (1 1))))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> tree.duration
Duration(1, 1)
>>> tree[1].duration
Duration(1, 2)
>>> tree[1][1].duration
Duration(1, 4)
"""
# return self.prolation * self.preprolated_duration
# return self.prolation * _duration.Duration(self.preprolated_duration)
if isinstance(self.preprolated_duration, tuple):
n = self.prolation.numerator * self.preprolated_duration[0]
d = self.prolation.denominator * self.preprolated_duration[1]
pair = (n, d)
return pair
else:
assert isinstance(self.preprolated_duration, _duration.Duration)
duration = self.prolation * self.preprolated_duration
return duration
@property
def pair(self) -> tuple[int, int]:
"""
Gets preprolated duration as pair.
"""
if isinstance(self.preprolated_duration, tuple):
pair = self.preprolated_duration
else:
assert isinstance(self.preprolated_duration, _duration.Duration)
pair = self.preprolated_duration.pair
return pair
@property
def parentage_ratios(self):
"""
A sequence describing the relative durations of the nodes in a node's improper
parentage.
The first item in the sequence is the preprolated_duration of the root node, and
subsequent items are pairs of the preprolated duration of the next node in the
parentage and the total preprolated_duration of that node and its siblings:
.. container:: example
>>> a = abjad.rhythmtrees.RhythmTreeContainer(preprolated_duration=abjad.Duration(1))
>>> b = abjad.rhythmtrees.RhythmTreeContainer(preprolated_duration=abjad.Duration(2))
>>> c = abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(3))
>>> d = abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(4))
>>> e = abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(5))
>>> a.extend([b, c])
>>> b.extend([d, e])
>>> a.parentage_ratios
(Duration(1, 1),)
>>> for item in b.parentage_ratios:
... item
Duration(1, 1)
(Duration(2, 1), Duration(5, 1))
>>> for item in c.parentage_ratios:
... item
Duration(1, 1)
(Duration(3, 1), Duration(5, 1))
>>> for item in d.parentage_ratios:
... item
Duration(1, 1)
(Duration(2, 1), Duration(5, 1))
(Duration(4, 1), Duration(9, 1))
>>> for item in e.parentage_ratios:
... item
Duration(1, 1)
(Duration(2, 1), Duration(5, 1))
(Duration(5, 1), Duration(9, 1))
Returns tuple.
"""
result = []
node = self
while node.parent is not None:
result.append(
(
node.preprolated_duration,
node.parent._get_contents_duration(),
)
)
node = node.parent
result.append(node.preprolated_duration)
return tuple(reversed(result))
@property
def preprolated_duration(self) -> _duration.Duration:
"""
Gets node duration in pulses.
.. container:: example
>>> node = abjad.rhythmtrees.RhythmTreeLeaf(abjad.Duration(1))
>>> node.preprolated_duration
Duration(1, 1)
>>> node.preprolated_duration = abjad.Duration(2)
>>> node.preprolated_duration
Duration(2, 1)
>>> node = abjad.rhythmtrees.RhythmTreeLeaf((2, 4))
>>> node.preprolated_duration
(2, 4)
"""
return self._duration
@preprolated_duration.setter
def preprolated_duration(self, argument):
assert isinstance(argument, tuple | _duration.Duration), repr(argument)
if isinstance(argument, tuple):
argument = argument
assert 0 < fractions.Fraction(*argument)
else:
assert isinstance(argument, _duration.Duration)
assert 0 < argument
self._duration = argument
self._mark_entire_tree_for_later_update()
@property
def pretty_rtm_format(self):
"""
Gets pretty-printed RTM format of node.
.. container:: example
>>> string = '(1 ((1 (1 1)) (1 (1 1))))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> print(tree.pretty_rtm_format)
(1 (
(1 (
1
1))
(1 (
1
1))))
Returns string.
"""
return "\n".join(self._pretty_rtm_format_pieces())
@property
def prolation(self) -> fractions.Fraction:
"""
Gets node prolation.
"""
return _math.cumulative_products(self.prolations)[-1]
@property
def prolations(self):
"""
Prolations of rhythm tree node.
"""
prolations = [fractions.Fraction(1)]
pairs = _sequence.nwise(self.parentage)
for child, parent in pairs:
multiplier = fractions.Fraction(
_duration.Duration(parent.preprolated_duration),
parent._get_contents_duration(),
)
prolations.append(multiplier)
return tuple(prolations)
@property
def start_offset(self) -> _duration.Offset:
"""
Gets node start offset.
.. container:: example
>>> string = '(1 ((1 (1 1)) (1 (1 1))))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> tree.start_offset
Offset((0, 1))
>>> tree[1].start_offset
Offset((1, 2))
>>> tree[0][1].start_offset
Offset((1, 4))
"""
self._update_offsets_of_entire_tree_if_necessary()
return self._offset
@property
def stop_offset(self) -> _duration.Offset:
"""
Gets node stop offset.
"""
# return self.start_offset + self.duration
return self.start_offset + _duration.Duration(self.duration)
[docs]class RhythmTreeLeaf(RhythmTreeMixin, uqbar.containers.UniqueTreeNode):
"""
Rhythm-tree leaf.
.. container:: example
Pitched rhythm-tree leaf makes notes:
>>> leaf = abjad.rhythmtrees.RhythmTreeLeaf(
... preprolated_duration=abjad.Duration(5), is_pitched=True
... )
>>> leaf((1, 8))
[Note("c'2"), Note("c'8")]
.. container:: example
Unpitched rhythm-tree leaf makes rests:
>>> leaf = abjad.rhythmtrees.RhythmTreeLeaf(
... preprolated_duration=abjad.Duration(7), is_pitched=False
... )
>>> leaf((1, 16))
[Rest('r4..')]
"""
def __init__(
self, preprolated_duration=_duration.Duration(1), is_pitched=True, name=None
):
uqbar.containers.UniqueTreeNode.__init__(self, name=name)
RhythmTreeMixin.__init__(self, preprolated_duration=preprolated_duration)
self.is_pitched = is_pitched
[docs] def __call__(self, pulse_duration) -> list[_score.Leaf | _score.Tuplet]:
"""
Makes list of leaves and / or tuplets equal to ``pulse_duration``.
"""
pulse_duration = _duration.Duration(pulse_duration)
total_duration = pulse_duration * self.preprolated_duration
if self.is_pitched:
return _makers.make_leaves(0, total_duration)
return _makers.make_leaves([None], total_duration)
[docs] def __graph__(self, **keywords) -> uqbar.graphs.Graph:
"""
Gets Graphviz graph of rhythm tree leaf.
"""
graph = uqbar.graphs.Graph(name="G")
node = uqbar.graphs.Node(
attributes={"label": str(self.preprolated_duration), "shape": "box"}
)
graph.append(node)
return graph
[docs] def __repr__(self) -> str:
"""
Gets interpreter representation of rhythm-tree leaf.
"""
properties = [
f"preprolated_duration={self.preprolated_duration!r}",
f"is_pitched={self.is_pitched!r}",
]
if self.name is not None:
properties.append(f"name={self.name!r}")
properties_string = ", ".join(properties)
return f"{type(self).__name__}({properties_string})"
def _pretty_rtm_format_pieces(self):
# return [str(self.preprolated_duration)]
return [str(self._get_fraction_string())]
@property
def is_pitched(self) -> bool:
"""
Is true when rhythm-tree leaf is pitched.
"""
return self._is_pitched
@is_pitched.setter
def is_pitched(self, argument):
self._is_pitched = bool(argument)
@property
def rtm_format(self) -> str:
"""
Gets RTM format of rhythm tree leaf.
.. container:: example
>>> abjad.rhythmtrees.RhythmTreeLeaf(abjad.Duration(1), is_pitched=True).rtm_format
'1'
>>> abjad.rhythmtrees.RhythmTreeLeaf(abjad.Duration(5), is_pitched=False).rtm_format
'-5'
"""
string = self._get_fraction_string()
if self.is_pitched:
return f"{string!s}"
return f"-{string!s}"
[docs]class RhythmTreeContainer(RhythmTreeMixin, uqbar.containers.UniqueTreeList):
r"""
Rhythm-tree container.
.. container:: example
Initializes rhythm-tree container:
>>> container = abjad.rhythmtrees.RhythmTreeContainer(
... preprolated_duration=abjad.Duration(1),
... children=[],
... )
>>> container
RhythmTreeContainer((1, 1))
.. container:: example
Similar to Abjad containers, ``RhythmTreeContainer`` supports a list interface,
and can be appended, extended, indexed and so forth by other ``RhythmTreeMixin``
subclasses:
>>> leaf_a = abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(1))
>>> leaf_b = abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(2))
>>> container.extend([leaf_a, leaf_b])
>>> for _ in container: _
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeLeaf(preprolated_duration=Duration(2, 1), is_pitched=True)
>>> another_container = abjad.rhythmtrees.RhythmTreeContainer(
... preprolated_duration=abjad.Duration(2))
>>> another_container.append(
... abjad.rhythmtrees.RhythmTreeLeaf(preprolated_duration=abjad.Duration(3)))
>>> another_container.append(container[1])
>>> container.append(another_container)
>>> for _ in container: _
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeContainer((2, 3))
.. container:: example
Call ``RhythmTreeContainer`` with a duration to make tuplets:
>>> components = container((1, 4))
>>> tuplet = components[0]
>>> abjad.show(tuplet) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(tuplet)
>>> print(string)
\times 2/3
{
c'8
\times 4/5
{
c'8.
c'8
}
}
"""
### INITIALIZER ###
def __init__(
self, children=None, preprolated_duration=_duration.Duration(1), name=None
):
uqbar.containers.UniqueTreeList.__init__(self, name=name)
RhythmTreeMixin.__init__(self, preprolated_duration=preprolated_duration)
if isinstance(children, list | str | tuple):
self.extend(children)
elif children is not None:
raise ValueError(f"can not instantiate {type(self)} with {children!r}.")
### SPECIAL METHODS ###
[docs] def __add__(self, argument) -> "RhythmTreeContainer":
r"""
Concatenate containers self and argument. The operation c = a + b returns a new
RhythmTreeContainer c with the content of both a and b, and a
preprolated_duration equal to the sum of the durations of a and b. The operation
is non-commutative: the content of the first operand will be placed before the
content of the second operand:
.. container:: example
>>> a = abjad.rhythmtrees.RhythmTreeParser()('(1 (1 1 1))')[0]
>>> b = abjad.rhythmtrees.RhythmTreeParser()('(2 (3 4))')[0]
>>> c = a + b
>>> c.preprolated_duration
Duration(3, 1)
>>> for _ in c: _
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeLeaf(preprolated_duration=Duration(3, 1), is_pitched=True)
RhythmTreeLeaf(preprolated_duration=Duration(4, 1), is_pitched=True)
"""
if isinstance(argument, str):
argument = RhythmTreeParser()(argument)
assert 1 == len(argument) and isinstance(argument[0], type(self))
argument = argument[0]
container = type(self)(
preprolated_duration=self.preprolated_duration
+ argument.preprolated_duration
)
container.extend(self[:])
container.extend(argument[:])
return container
[docs] def __call__(self, pulse_duration) -> list[_score.Leaf | _score.Tuplet]:
r"""
Makes list of leaves and /or tuplets equal to ``pulse_duration``.
.. container:: example
>>> string = '(1 (1 (2 (1 1 1)) 2))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> components = tree((1, 4))
>>> components
[Tuplet('5:4', "c'16 { 2/3 c'16 c'16 c'16 } c'8")]
>>> staff = abjad.Staff(components)
>>> abjad.show(staff) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(staff)
>>> print(string)
\new Staff
{
\times 4/5
{
c'16
\times 2/3
{
c'16
c'16
c'16
}
c'8
}
}
"""
def recurse(node, tuplet_duration):
basic_prolated_duration = tuplet_duration / node._get_contents_duration()
basic_written_duration = _duration.Duration(
basic_prolated_duration
).equal_or_greater_power_of_two
tuplet = _score.Tuplet((1, 1), [])
for child in node.children:
if isinstance(child, type(self)):
tuplet.extend(
recurse(
child,
child.preprolated_duration * basic_written_duration,
)
)
else:
leaves = child(basic_written_duration)
tuplet.extend(leaves)
if 1 < len(leaves):
_spanners.tie(leaves)
assert fractions.Fraction(*tuplet.multiplier) == 1, repr(tuplet.multiplier)
contents_duration = tuplet._get_duration()
target_duration = tuplet_duration
multiplier = target_duration / contents_duration
tuplet.multiplier = _duration.pair(multiplier)
if fractions.Fraction(*tuplet.multiplier) == 1:
return tuplet[:]
return [tuplet]
pulse_duration = _duration.Duration(pulse_duration)
assert 0 < pulse_duration
result = recurse(
self, pulse_duration * _duration.Duration(self.preprolated_duration)
)
for component in result[:]:
if isinstance(component, _score.Tuplet):
if component.trivial():
_mutate._extract(component)
return result
[docs] def __graph__(self, **keywords) -> uqbar.graphs.Graph:
r"""
Graphs rhythm-tree container.
.. container:: example
>>> string = '(1 (1 (2 (1 1 1)) 2))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> graph = tree.__graph__()
>>> print(format(graph, "graphviz"))
digraph G {
graph [bgcolor=transparent,
truecolor=true];
node_0 [label="1",
shape=triangle];
node_1 [label="1",
shape=box];
node_2 [label="2",
shape=triangle];
node_3 [label="1",
shape=box];
node_4 [label="1",
shape=box];
node_5 [label="1",
shape=box];
node_6 [label="2",
shape=box];
node_0 -> node_1;
node_0 -> node_2;
node_0 -> node_6;
node_2 -> node_3;
node_2 -> node_4;
node_2 -> node_5;
}
>>> abjad.graph(graph) # doctest: +SKIP
"""
graph = uqbar.graphs.Graph(
name="G", attributes={"bgcolor": "transparent", "truecolor": True}
)
node_mapping = {}
nodes = [self]
nodes.extend(self.depth_first())
for node in nodes:
graphviz_node = uqbar.graphs.Node()
graphviz_node.attributes["label"] = str(node.preprolated_duration)
if isinstance(node, type(self)):
graphviz_node.attributes["shape"] = "triangle"
else:
graphviz_node.attributes["shape"] = "box"
graph.append(graphviz_node)
node_mapping[node] = graphviz_node
if node.parent is not None:
uqbar.graphs.Edge().attach(
node_mapping[node.parent], node_mapping[node]
)
return graph
[docs] def __radd__(self, argument) -> "RhythmTreeContainer":
"""
Concatenates containers argument and self.
"""
assert isinstance(argument, type(self))
return argument.__add__(self)
[docs] def __repr__(self) -> str:
"""
Gets interpreter representation of rhythm-tree container.
"""
class_name = type(self).__name__
# numerator, denominator = self.duration.pair
if isinstance(self.duration, tuple):
numerator, denominator = self.duration
else:
assert isinstance(self.duration, _duration.Duration)
numerator, denominator = self.duration.pair
return f"{class_name}(({numerator}, {denominator}))"
### PRIVATE METHODS ###
def _get_contents_duration(self) -> _duration.Duration:
result = sum([_duration.Duration(_.preprolated_duration) for _ in self])
result = _duration.Duration(result)
return result
def _prepare_setitem_multiple(self, expr):
if isinstance(expr, str):
expr = RhythmTreeParser()(expr)
elif isinstance(expr, list) and len(expr) == 1 and isinstance(expr[0], str):
expr = RhythmTreeParser()(expr[0])
return expr
def _prepare_setitem_single(self, expr):
if isinstance(expr, str):
expr = RhythmTreeParser()(expr)[0]
assert len(expr) == 1
expr = expr[0]
return expr
def _pretty_rtm_format_pieces(self):
result = []
result.append(f"({self._get_fraction_string()} (")
for child in self:
result.extend([" " + x for x in child._pretty_rtm_format_pieces()])
result[-1] = result[-1] + "))"
return result
### PUBLIC PROPERTIES ###
@property
def rtm_format(self) -> str:
"""
Gets rhythm-tree container RTM format.
.. container:: example
>>> string = '(1 ((1 (1 1)) (1 (1 1))))'
>>> tree = abjad.rhythmtrees.RhythmTreeParser()(string)[0]
>>> tree.rtm_format
'(1 ((1 (1 1)) (1 (1 1))))'
"""
string = " ".join([x.rtm_format for x in self])
fraction_string = self._get_fraction_string()
return f"({fraction_string} ({string}))"
[docs]class RhythmTreeParser(Parser):
r"""
Rhythm-tree parser.
.. container:: example
Abjad’s rhythm-tree parser parses a micro-language resembling Ircam’s
RTM Lisp syntax, and generates a sequence of rhythm-tree structures.
Composers can maniuplate these structures and then convert them to
Abjad score components.
>>> parser = abjad.rhythmtrees.RhythmTreeParser()
>>> string = '(3 (1 (1 ((2 (1 1 1)) 2 2 1))))'
>>> rhythm_tree_list = parser(string)
>>> rhythm_tree_container = rhythm_tree_list[0]
>>> rhythm_tree_container.rtm_format
'(3 (1 (1 ((2 (1 1 1)) 2 2 1))))'
>>> for _ in rhythm_tree_container: _
RhythmTreeLeaf(preprolated_duration=Duration(1, 1), is_pitched=True)
RhythmTreeContainer((3, 2))
>>> base_duration = (1, 4)
>>> component_list = rhythm_tree_container(base_duration)
>>> tuplet = component_list[0]
>>> abjad.show(tuplet) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(tuplet)
>>> print(string)
\tweak text #tuplet-number::calc-fraction-text
\times 3/4
{
c'2
\times 4/7
{
\times 2/3
{
c'8
c'8
c'8
}
c'4
c'4
c'8
}
}
"""
### PUBLIC PROPERTIES ###
@property
def lexer_rules_object(self):
return self
@property
def parser_rules_object(self):
return self
### LEX SETUP ###
tokens = ("DURATION", "LPAREN", "RPAREN")
t_LPAREN = r"\("
t_RPAREN = r"\)"
t_ignore = " \n\t\r"
### YACC SETUP ###
start = "toplevel"
### LEX METHODS ###
[docs] def t_DURATION(self, t):
r"-?[1-9]\d*(/[1-9]\d*)?"
parts = t.value.partition("/")
if not parts[2]:
t.value = _duration.Duration(int(parts[0]))
else:
numerator, denominator = int(parts[0]), int(parts[2])
duration = _duration.Duration(numerator, denominator)
if numerator == duration.numerator:
t.value = duration
else:
t.value = (numerator, denominator)
return t
[docs] def t_error(self, t):
print(("Illegal character '%s'" % t.value[0]))
t.lexer.skip(1)
[docs] def t_newline(self, t):
r"\n+"
t.lexer.lineno += t.value.count("\n")
### YACC METHODS ###
[docs] def p_container__LPAREN__DURATION__node_list_closed__RPAREN(self, p):
"""
container : LPAREN DURATION node_list_closed RPAREN
"""
prototype = (tuple, _duration.Duration)
assert isinstance(p[2], prototype), repr(p[2])
if isinstance(p[2], tuple):
argument = p[2]
else:
assert isinstance(p[2], _duration.Duration)
argument = p[2]
assert isinstance(argument, tuple | _duration.Duration)
p[0] = RhythmTreeContainer(children=p[3], preprolated_duration=argument)
[docs] def p_error(self, p):
if p:
print(("Syntax error at '%s'" % p.value))
else:
print("Syntax error at EOF")
[docs] def p_leaf__INTEGER(self, p):
"""
leaf : DURATION
"""
p[0] = RhythmTreeLeaf(preprolated_duration=abs(p[1]), is_pitched=0 < p[1])
[docs] def p_node__container(self, p):
"""
node : container
"""
p[0] = p[1]
[docs] def p_node__leaf(self, p):
"""
node : leaf
"""
p[0] = p[1]
[docs] def p_node_list__node_list__node_list_item(self, p):
"""
node_list : node_list node_list_item
"""
p[0] = p[1] + [p[2]]
[docs] def p_node_list__node_list_item(self, p):
"""
node_list : node_list_item
"""
p[0] = [p[1]]
[docs] def p_node_list_closed__LPAREN__node_list__RPAREN(self, p):
"""
node_list_closed : LPAREN node_list RPAREN
"""
p[0] = p[2]
[docs] def p_node_list_item__node(self, p):
"""
node_list_item : node
"""
p[0] = p[1]
[docs] def p_toplevel__EMPTY(self, p):
"""
toplevel :
"""
p[0] = []
[docs] def p_toplevel__toplevel__node(self, p):
"""
toplevel : toplevel node
"""
p[0] = p[1] + [p[2]]
[docs]def parse_rtm_syntax(string: str) -> _score.Container | _score.Leaf | _score.Tuplet:
r"""
Creates rhythm tree from RTM ``string``; then calls rhythm tree on
quarter-note pulse duration.
A single quarter note:
.. container:: example
>>> result = abjad.rhythmtrees.parse_rtm_syntax("1")
>>> result
Note("c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
c'4
A series of quarter notes:
>>> result = abjad.rhythmtrees.parse_rtm_syntax("1 1 1 1 1 1")
>>> result
Container("c'4 c'4 c'4 c'4 c'4 c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
c'4
c'4
c'4
c'4
c'4
}
Notes with durations of the form ``n * 1/4``:
>>> result = abjad.rhythmtrees.parse_rtm_syntax("1 2 3 4 5")
>>> result
Container("c'4 c'2 c'2. c'1 c'1 c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
c'2
c'2.
c'1
c'1
~
c'4
}
Notes with durations of the form ``1/n * 1/4``:
>>> result = abjad.rhythmtrees.parse_rtm_syntax("1 1/2 1/3 1/4 1/5")
>>> result
Container("c'4 c'8 { 8/12 c'8 } c'16 { 16/20 c'16 }")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
c'8
\tweak edge-height #'(0.7 . 0)
\times 8/12
{
c'8
}
c'16
\tweak edge-height #'(0.7 . 0)
\times 16/20
{
c'16
}
}
With arbitrary multipliers:
>>> result = abjad.rhythmtrees.parse_rtm_syntax("1 2/3 3/5")
>>> result
Container("c'4 { 4/6 c'4 } { 16/20 c'8. }")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
\tweak edge-height #'(0.7 . 0)
\times 4/6
{
c'4
}
\tweak edge-height #'(0.7 . 0)
\times 16/20
{
c'8.
}
}
.. container:: example
Divides quarter-note duration into 1 part; results in a note:
>>> string = "(1 (1))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Note("c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
c'4
Divides quarter-note duration ``1:1``; results in a container:
>>> string = "(1 (1 1))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Container("c'8 c'8")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'8
c'8
}
Divides quarter-note duration ``1:2``; results in a tuplet:
>>> string = "(1 (1 2))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Tuplet('3:2', "c'8 c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
\times 2/3
{
c'8
c'4
}
.. container:: example
Divides half-note duration into 1 part; results in a note:
>>> string = "(2 (1))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Note("c'2")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
c'2
Divides half-note duration ``1:1``; results in a container:
>>> string = "(2 (1 1))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Container("c'4 c'4")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
c'4
}
Divides half-note duration ``1:2``; results in a tuplet:
>>> string = "(2 (1 2))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Tuplet('3:2', "c'4 c'2")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
\times 2/3
{
c'4
c'2
}
.. container:: example
Divides three successive quarter-note durations, according
to ratios of ``1``, ``1:1``, ``1:2``:
>>> string = "(1 (1)) (1 (1 1)) (1 (1 2))"
>>> result = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> result
Container("c'4 c'8 c'8 { 2/3 c'8 c'4 }")
>>> abjad.show(result) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(result)
>>> print(string)
{
c'4
c'8
c'8
\times 2/3
{
c'8
c'4
}
}
.. container:: example
Another example:
>>> string = "(1 (1 (1 (1 1)) 1))"
>>> tuplet = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> abjad.show(tuplet) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(tuplet)
>>> print(string)
\times 2/3
{
c'8
c'16
c'16
c'8
}
.. container:: example
Fractional durations are allowed:
>>> string = "(3/4 (1 1/2 (4/3 (1 -1/2 1))))"
>>> tuplet = abjad.rhythmtrees.parse_rtm_syntax(string)
>>> abjad.show(tuplet) # doctest: +SKIP
.. docs::
>>> string = abjad.lilypond(tuplet)
>>> print(string)
\tweak text #tuplet-number::calc-fraction-text
\times 9/17
{
c'8
c'16
\tweak edge-height #'(0.7 . 0)
\times 8/15
{
c'8
r16
c'8
}
}
"""
container = _score.Container()
rtm_containers = RhythmTreeParser()(string)
prototype = (RhythmTreeLeaf, RhythmTreeContainer)
for node in rtm_containers:
assert isinstance(node, prototype), repr(node)
components = node((1, 4))
container.extend(components)
if len(container) == 1:
result = container[0]
else:
result = container
prototype_ = (_score.Container, _score.Leaf, _score.Tuplet)
assert isinstance(result, prototype_), repr(result)
return result