# A Bottom-up Approach to Complex Rhythms

``````

In [1]:

``````
``````

In [2]:

import random
durations = []
numerators = range(16)
denominators = [2**x for x in range(1, 5)]
for x in range(10):
numerator = random.choice(numerators)
denominator = random.choice(denominators)
duration_token = (numerator, denominator)
durations.append(duration)

for d in durations:
print(d)

``````
``````

5/2
1/2
7/8
3/4
5/2
1/2
0
9/16
15/4
3/8

``````

Assignability is a thing:

``````

In [3]:

``````
``````

---------------------------------------------------------------------------
AssignabilityError                        Traceback (most recent call last)
----> 2 note = abjad.Note(0, (duration))

99         else:
100             raise ValueError("can not initialize note from {arguments!r}.")
--> 101         Leaf.__init__(self, written_duration, multiplier=multiplier, tag=tag)
102         if written_pitch is not None:
103             if written_pitch not in drums:

57         self._before_grace_container = None
58         self.multiplier = multiplier
---> 59         self.written_duration = written_duration
60
61     ### SPECIAL METHODS ###

240     @written_duration.setter
241     def written_duration(self, argument):
--> 242         return Leaf.written_duration.fset(self, argument)
243
244     @property

537         if not duration.is_assignable:
538             message = f"not assignable duration: {duration!r}."
--> 539             raise exceptions.AssignabilityError(message)
540         self._written_duration = duration

AssignabilityError: not assignable duration: Duration(5, 4).

``````

The `LeafMaker` class helps us around the assignability error:

``````

In [4]:

pitches = [None]
leaves = maker(pitches, durations)
leaves

``````
``````

Out[4]:

Selection([Rest('r4.'), Rest('r2'), Rest('r8')])

``````

# Tuplets

Use the `Tuplet` class to make tuplets (of any non-binary division). The Tuplet class provides an interface for working with complex rhythms in a bottom-up way:

``````

In [5]:

``````
``````

In [6]:

``````
``````

tspan { white-space: pre; }

3

``````

You can also pack existing leaves into a Tuplet instance.

``````

In [7]:

``````
``````

In [8]:

``````
``````

In [9]:

``````
``````

tspan { white-space: pre; }

3

``````

Tuplet instances all have a multiplier and components.

``````

In [10]:

tuplet

``````
``````

Out[10]:

Tuplet(Multiplier(2, 3), "fs'8 g'8 r8")

``````

## Understanding Augmentation and Diminution

Remember that any tuplet can be represented as an augmentation or a diminution relative to the written notes' default values. Our example tuplet's multiplier of (2,3) for three eighth notes means that each written eighth note lasts for 2/3rds its written value. Because the original durations have been reduced, this is a diminution:

``````

In [11]:

tuplet.diminution()

``````
``````

Out[11]:

True

``````

A tuplet with a multiplier greater than 1, on the other hand, would be an augmentation:

``````

In [12]:

tuplet = abjad.Tuplet((4,3), "fs'16 g'16 r16")

``````
``````

In [13]:

tuplet.diminution()

``````
``````

Out[13]:

False

``````
``````

In [14]:

``````
``````

tspan { white-space: pre; }

3:4

``````

This last tuplet is an augmentation in which each of the written sixteenth notes lasts for 4/3rds of its written duration. The sounding result would be identical, and these are just two ways of writing the same thing, the former of which happens to be conventional.

Remember that object-oriented programming gives us objects with characteristics and behaviors. We can use the dot-chaining syntax to read and write the tuplet's multiplier attribute:

``````

In [15]:

``````
``````

In [16]:

tuplet.multiplier

``````
``````

Out[16]:

Multiplier(2, 3)

``````
``````

In [17]:

``````
``````

In [18]:

tuplet

``````
``````

Out[18]:

Tuplet(Multiplier(4, 5), "fs'8 g'8 r8")

``````
``````

In [19]:

``````
``````

tspan { white-space: pre; }

5

``````

The multiplier is a reasonable characteristic for our tuplet to have, but what about behaviors? Well, you probably want to be able to build up tuplets by adding leaves to them, one or several a time. The append method adds leaves to the end of a tuplet (and to any Python list), one leaf at a time:

``````

In [20]:

``````
``````

In [21]:

``````
``````

tspan { white-space: pre; }

5

``````

...or using a LilyPond string:

``````

In [22]:

tuplet.append("bf8")

``````
``````

In [23]:

``````
``````

tspan { white-space: pre; }

5

``````

Likewise, the extend method adds two or more leaves at a time:

``````

In [24]:

``````
``````

In [25]:

tuplet.extend(notes)

``````
``````

In [26]:

``````
``````

tspan { white-space: pre; }

5

``````

And you can use a LilyPond string with extend, too:

``````

In [27]:

tuplet.extend("gs'8 a8")

``````
``````

In [28]:

``````
``````

tspan { white-space: pre; }

5

``````

## Removing Leaves

You can remove tuplet components by reference using the remove method:

``````

In [29]:

tuplet.remove(tuplet[3])

``````
``````

In [30]:

``````
``````

tspan { white-space: pre; }

5

``````

If you want to remove a component by index and then keep it to do something else with it, use the pop method instead of remove:

``````

In [31]:

popped = tuplet.pop(2)
popped

``````
``````

Out[31]:

Rest('r8')

``````
``````

In [32]:

``````
``````

tspan { white-space: pre; }

5

``````

## Indexing Leaves

Tuplets support indexing, if you'd like to do something to the nth component of a tuplet:

``````

In [33]:

tuplet[1]

``````
``````

Out[33]:

Note("g'8")

``````

If you've added an existing list to a tuplet's components, and you'd like to see where a component of that list is in the tuplet, you can use the tuplet's index method - in our case, we'll use our notes list from above:

``````

In [34]:

notes

``````
``````

Out[34]:

[Note("fs'32"), Note("e'32"), Note("d'32"), Rest('r32')]

``````
``````

In [35]:

notes[1]

``````
``````

Out[35]:

Note("e'32")

``````
``````

In [36]:

tuplet.index(notes[1]) # Where is the second element of notes in tuplet?

``````
``````

Out[36]:

4

``````
``````

In [37]:

abjad.show(tuplet) # it's at index 4

``````
``````

tspan { white-space: pre; }

5

``````

## Making Tuplets from Durations and Ratios

The Tuplet class also provides a method for constructing a tuplet from a duration and a ratio, where the ratio is a list of integers, the sum of which determines the number of equally spaced pulses within the duration:

``````

In [38]:

``````
``````

In [39]:

``````
``````

In [40]:

``````
``````

tspan { white-space: pre; }

5

``````

This might not seem like much, but we can write functions that use these kinds of basic functionalities to do more complicated things, like we'll do in this next example, taken from a real score:

# Brian Ferneyhough - Unsichtbare Farben

Mikhial Malt analyzes the rhythmic materials of Ferneyhough’s solo violin composition, Unsichtbare Farben, in The OM Composer’s Book 2.

Malt explains that Ferneyhough used OpenMusic to create an “exhaustive catalogue of rhythmic cells” such that:

Each cell consists of the same duration divided into two pulses related by a durational proportion ranging from 1:1 to 1:11.

The second pulse is then subdivided successively into 1, 2, 3, 4, 5 and 6 equal parts.

Let’s recreate Malt’s results in Abjad.

## The Proportions

We use a list comprehension to describe a list of (1,n) tuples, each of which will describe the durational proportion between a cell's first and second pulse:

``````

In [41]:

proportions = [(1, n) for n in range(1, 11 + 1)]

``````
``````

In [42]:

proportions

``````
``````

Out[42]:

[(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(1, 7),
(1, 8),
(1, 9),
(1, 10),
(1, 11)]

``````

## The Transforms

Next we’ll show how to divide a quarter note into various ratios, and then divide the final logical tie of the resulting tuplet into yet another ratio:

``````

In [43]:

def make_nested_tuplet(
tuplet_duration,
outer_tuplet_proportions,
inner_tuplet_subdivision_count,
):
r'''Makes nested tuplet.
'''

tuplet_duration, outer_tuplet_proportions)
inner_tuplet_proportions = inner_tuplet_subdivision_count * [1]
right_logical_tie.to_tuplet(inner_tuplet_proportions)
return outer_tuplet

``````

And of course it's easier to see what this function does with an example of use:

``````

In [44]:

tuplet = make_nested_tuplet(abjad.Duration(1, 4), (1, 1), 5)

``````
``````

In [45]:

``````
``````

In [46]:

``````
``````

tspan { white-space: pre; }

5

1:1

``````

We see that a duration of a quarter note (the first argument) has been divided into two pulses with a durational proportion of 1:1 (second argument), the second pulse of which has then been divided into five equally spaced parts (the third argument). Try changing the arguments and see what happens.

``````

In [47]:

tuplet = make_nested_tuplet(abjad.Duration(1, 4), (13, 28), 11)

``````
``````

In [48]:

``````
``````

In [49]:

``````
``````

tspan { white-space: pre; }

41

11:7

``````
``````

In [50]:

tuplet = make_nested_tuplet(abjad.Duration(1, 4), (3, 1), 5)

``````
``````

In [51]:

``````
``````

In [52]:

``````
``````

tspan { white-space: pre; }

5

1:1

``````

### Logical Ties Solve the Problem of Five

A logical tie is a selection of notes or chords connected by ties. It lets us talk about a notated rhythm of 5/16, for example, which can not be expressed with only a single leaf.

Note how we can divide a tuplet whose outer proportions are 3/5, where the second logical tie requires two notes to express the 5/16 duration:

``````

In [53]:

``````
``````

In [54]:

``````
``````

In [55]:

``````
``````

tspan { white-space: pre; }

1:1

``````
``````

In [56]:

subdivided_tuplet = make_nested_tuplet(abjad.Duration(1, 4), (3, 5), 3)

``````
``````

In [57]:

``````
``````

In [58]:

``````
``````

tspan { white-space: pre; }

6:5

1:1

``````

Do you see which objects and methods in our make_nested_tuplet function convert a logical tie into a tuplet?

## The Rhythms

Now that we know how to make the basic building block, let’s make a lot of tuplets all at once.

We’ll set the duration of each tuplet equal to a quarter note:

``````

In [59]:

``````

Reusing our make_nested_tuplet function, we make one row of rhythms, with the last logical tie increasingly subdivided:

``````

In [60]:

def make_row_of_nested_tuplets(
tuplet_duration,
outer_tuplet_proportions,
column_count,
):
r'''Makes row of nested tuplets.
'''

assert 0 < column_count
row_of_nested_tuplets = []
for n in range(column_count):
inner_tuplet_subdivision_count = n + 1
nested_tuplet = make_nested_tuplet(
tuplet_duration,
outer_tuplet_proportions,
inner_tuplet_subdivision_count,
)
row_of_nested_tuplets.append(nested_tuplet)
return row_of_nested_tuplets

``````
``````

In [61]:

tuplets = make_row_of_nested_tuplets(duration, (2, 1), 6)

``````
``````

In [62]:

``````
``````

In [63]:

``````
``````

tspan { white-space: pre; }

3

5

3

1:1

3

3

3

3

1:1

3

1:1

3

``````

If we can make one single row of rhythms, we can make many rows of rhythms. We reuse this last function to make another function:

``````

In [64]:

def make_rows_of_nested_tuplets(tuplet_duration, row_count, column_count):
r'''Makes rows of nested tuplets.
'''

assert 0 < row_count
rows_of_nested_tuplets = []
for n in range(row_count):
outer_tuplet_proportions = (1, n + 1)
row_of_nested_tuplets = make_row_of_nested_tuplets(
tuplet_duration, outer_tuplet_proportions, column_count)
rows_of_nested_tuplets.append(row_of_nested_tuplets)
return rows_of_nested_tuplets

``````
``````

In [65]:

``````
``````

In [66]:

for tuplet_row in make_rows_of_nested_tuplets(duration, 10, 6):

``````
``````

In [67]:

``````
``````

tspan { white-space: pre; }

16:9

1:1

8:7

4:3

8:5

1:1

4:3

1:1

1:1

1:1

3

1:1

5

1:1

3

1:1

5

3

7

1:1

9

3

5

11

8:5

5

5

1:1

3

1:1

5

3

7

1:1

9

5

11

7

1:1

9

5

11

1:1

10:9

5

10:7

5:3

1:1

5

5:3

1:1

4:3

1:1

1:1

1:1

3

1:1

5

3

7

1:1

9

5

11

8:5

16:9

1:1

3

1:1

5

3

7

1:1

9

5

11

8:5

16:9

1:1

8:7

4:3

8:5

9

5

11

6:5

4:3

3

12:7

1:1

6:5

3

1:1

3

3

1:1

8:7

4:3

8:5

1:1

4:3

1:1

1:1

1:1

3

1:1

5

3

7

1:1

6:5

4:3

3

12:7

1:1

6:5

3

1:1

3

3

``````

This example illustrates how simpler bottom-up rhythmic construction might be abstracted to explore the potential of a bottom-up rhythmic idea.

# Meter

``````

In [68]:

### Chopping Durations with Durations

``````

It's easy to make durations that don't neatly fit into a particular time signature:

``````

In [69]:

staff = abjad.Staff("c'4. c'4. c'4. c'4. c'4. c'4. c'4. c'4.")

``````
``````

\new Staff
{
c'4.
c'4.
c'4.
c'4.
c'4.
c'4.
c'4.
c'4.
}

``````

Let's make this metrically legible by chopping the leaves every whole note using `mutate().split()`.

Note that this function returns selections full of the kind of component you split: if you want to split leaves in place, pass in `staff[:]`, and if you want to return `Selection`s containing new `Staff`s, pass in `staff`.

To keep splitting by the same duration until we get through all of the leaves, set the keyword argument cyclic equal to `True`.

``````

In [70]:

``````
``````

\new Staff
{
c'4.
c'4.
c'4
~
c'8
c'4.
c'4.
c'8
~
c'4
c'4.
c'4.
}

``````

This is better -- the music now plays nice with LilyPond's barlines and isn't incorrectly notated -- but in Python, we still don't have any `Measure` objects, and our staff still iterates through leaves:

``````

In [71]:

for leaf in staff[:5]:
print(leaf)

``````
``````

c'4.
c'4.
c'4
c'8
c'4.

``````

What should we do if we want to iterate through our music measure by measure?

### Wrapping Leaves in Measures

We can create `Measure` objects by wrapping each split `Selection` in a `Container`:

``````

In [72]:

for shard in abjad.mutate(staff[:]).split([duration] , cyclic=True):

``````
``````

\new Staff
{
{
c'4.
c'4.
c'4
~
}
{
c'8
c'4.
c'4.
c'8
~
}
{
c'4
c'4.
c'4.
}
}

tspan { white-space: pre; }

``````

Now we have measures in our score tree, and our staff will iterate through measures by default.

``````

In [73]:

for measure in staff:
print(measure)

``````
``````

Container("c'4. c'4. c'4 ~")
Container("c'8 c'4. c'4. c'8 ~")
Container("c'4 c'4. c'4.")

``````

If we want to iterate by leaves, now we need to use `abjad.iterate()`.

``````

In [74]:

print(leaf)

``````
``````

c'4.
c'4.
c'4
c'8
c'4.
c'4.
c'8
c'4
c'4.
c'4.

``````

### Rewriting Rhythms with Metric Hierarchies

This is looking better, but the internal rhythms of our music still fail to comport with conventions of notation that dictate that we should always show beat three in a measure of (4,4). To do this, we need to impose a hierarchical model of meter onto our music, measure by measure.

``````

In [75]:

for measure in staff:

``````
``````

\new Staff
{
{
c'4.
c'4.
c'4
~
}
{
c'8
c'4.
c'4.
c'8
~
}
{
c'4
c'4.
c'4.
}
}

``````

This didn't change anything, because Abjad's default (4,4) hierarchy is just four quarter notes:

``````

In [76]:

``````
``````

G

cluster_offsets

node_0

4/4

node_1

1/4

node_0->node_1

node_2

1/4

node_0->node_2

node_3

1/4

node_0->node_3

node_4

1/4

node_0->node_4

node_5_0

0

++

node_1->node_5_0

node_5_1

1/4

+

node_1->node_5_1

node_2->node_5_1

node_5_2

1/2

+

node_2->node_5_2

node_3->node_5_2

node_5_3

3/4

+

node_3->node_5_3

node_4->node_5_3

node_5_4

1

++

node_4->node_5_4

``````

We need to define a custom meter that includes another level of metric hierarchy and rewrite according to that. To do this, we'll use IRCAM's rhythm tree syntax:

``````

In [77]:

other_four = abjad.Meter('(4/4 ((2/4 (1/4 1/4)) (2/4 (1/4 1/4)) ))')
for measure in staff:

``````
``````

\new Staff
{
{
c'4.
c'8
~
c'4
c'4
~
}
{
c'8
c'4.
c'4.
c'8
~
}
{
c'4
c'4
~
c'8
c'4.
}
}

``````

This works, because our new meter contains a second level of metric hierarchy.

``````

In [78]:

``````
``````

G

cluster_offsets

node_0

4/4

node_1

2/4

node_0->node_1

node_4

2/4

node_0->node_4

node_2

1/4

node_1->node_2

node_3

1/4

node_1->node_3

node_7_0

0

+++

node_2->node_7_0

node_7_1

1/4

+

node_2->node_7_1

node_3->node_7_1

node_7_2

1/2

++

node_3->node_7_2

node_5

1/4

node_4->node_5

node_6

1/4

node_4->node_6

node_5->node_7_2

node_7_3

3/4

+

node_5->node_7_3

node_6->node_7_3

node_7_4

1

+++

node_6->node_7_4

``````

Now you can start to model your metric habits. For further useful customizations, including dot-count, see `MutationAgent.rewrite_meter()`.

``````

In [ ]:

``````