Basic Usage

Rhythm Tools

The decitala package includes a number of rhythmic fragments in the corpora directory. We can access the data for these fragments using the decitala.fragment module. Classes like fragment.Decitala, fragment.GreekFoot, and fragment.ProsodicFragment are currently supported. More fragment types will become available in the future. (Note: rhythmic corpora would be gladly accepted as contributions.) These objects are normally instantiated with a name (as provided in the corpora directory). We can also create custom fragments with the fragment.GeneralFragment class.

>>> from decitala.fragment import Decitala, GreekFoot, GeneralFragment
>>> ragavardhana = Decitala("Ragavardhana")
>>> ragavardhana.ql_array()
array([0.25 , 0.375, 0.25 , 1.5  ])
>>> bacchius = GreekFoot("Bacchius")
>>> bacchius.greek_string
'⏑ –– ––'
>>> my_fragment = GeneralFragment(data=[1.0, 0.25, 0.375, 4.0]) # can also be instantiated with a filepath.
>>> my_fragment.std()
1.5242185497821

See the fragment module for more information on methods. Also included is a function for fast queries on rhythmic datasets. The hash_table module allows the user to create and query a generic hash_table.FragmentHashTable object as below. The modification types are based on Olivier Messiaen’s well-documentation rhythmic manipulation techniques.

>>> from decitala.hash_table import FragmentHashTable
>>> my_ht = FragmentHashTable(
...      datasets = ["greek_foot"],
...      custom_fragments = [GeneralFragment(data=filepath), Decitala("Ragavardhana")]
... )
>>> my_ht.datasets
["greek_foot"]
>>> my_ht.load() # You must first load the modifications into the data.
>>> query = my_ht.data[(1.0, 1.0, 2.0)]
>>> query["fragment"]
<fragment.GreekFoot Anapest>
>>> query["factor"]
1.0

The hash_table module also subclasses hash_table.FragmentHashTable with the included datasets. These objects are pre-loaded and can be created by calling, for instance, GreekFootHashTable() (or similarly DecitalaHashTable or ProsodicFragmentHashTable):

>>> from decitala.hash_table import GreekFootHashTable
>>> ght = GreekFootHashTable()
>>> ght
<decitala.hash_table.FragmentHashTable 2855 fragments>

If we’re interested in querying a score for all detected fragments stored in one of these FragmentHashTable or its subclasses, we can use the search.rolling_hash_search. This function applies a rolling window to a composition and, at each stage, queries the hash table for the given fragment.

>>> from decitala.hash_table import GreekFootHashTable
>>> from decitala.search import rolling_hash_search
>>> composition = "/Users/lukepoeppel/decitala/tests/static/Shuffled_Transcription_1.xml"
>>> all_search_results = rolling_hash_search(
...      filepath=composition,
...      part_num=0,
...      table=GreekFootHashTable(),
...      windows=[2, 3, 5, 7], # restrict search windows to our desired parameters.
... )

The rolling_hash_search function returns a list holding Extraction dataclasses. Each Extraction object stores the fragment, onset range, pitch content, articulation information, modification technique, etc… of these extracted fragments.

>>> for result in all_search_results[:5]:
...      print(result.fragment, result.onset_range)
...
<fragment.GreekFoot Anapest> (0.125, 0.625)
<fragment.GreekFoot Iamb> (0.25, 0.625)
<fragment.GreekFoot Iamb> (0.875, 1.25)
<fragment.GreekFoot Amphibrach> (0.875, 1.375)
<fragment.GreekFoot Trochee> (1.0, 1.375)

This package can also be used to find ‘paths’ of rhythms through a given composition. If we’re interested in finding a path of Greek metrics through a given part we use the dynamic programming path-finding algorithms (implemented in decitala.path_finding). The default algorithm used is an implementation of Dijkstra with a cost function determined by the gap between detected fragments and the number of onsets. Using only default parameters, this is as simple as:

>>> from decitala.search import path_finder
>>> from decitala.hash_table import GreekFootHashTable
>>> composition = "/Users/lukepoeppel/decitala/tests/static/Shuffled_Transcription_1.xml"
>>> path = path_finder(
...   filename=composition,
...   part_num=0,
...   table=GreekFootHashTable(),
... )
>>> for fragment in path:
...   print(fragment.fragment, fragment.onset_range)
...
<fragment.GreekFoot Peon_IV> (0.0, 0.625)
<fragment.GreekFoot Iamb> (0.875, 1.25)
<fragment.GreekFoot Peon_IV> (1.25, 1.875)
<fragment.GreekFoot Peon_IV> (2.375, 3.0)

We can change a number of the default parameters used here. Most notably, the user can interpolate their own cost function into the path-finding algorithm as follows:

>>> from decitala.path_finding import path_finding_utils
>>> from decitala.search import path_finder
>>> from decitala.hash_table import GreekFootHashTable
>>> my_cost_function = path_finding_utils.CostFunction(
...   def __init__(self, std_weight):
...      self.std_weight = std_weight
...   def cost(self, vertex_a, vertex_b):
...      return vertex_a.fragment.std() + vertex_b.fragment.std()
... )
>>> composition = "/Users/lukepoeppel/decitala/tests/static/Shuffled_Transcription_1.xml"
>>> path = path_finder(
...   filename=composition,
...   part_num=0,
...   table=GreekFootHashTable(),
...   cost_function_class=my_cost_function()
... )

Rhythmic Manipulation

Messiaen often altered rhythmic fragments from the various datasets he used before including them in his compositions. He still establishes an equivalence relation where a fragment \(F\) and a transformed fragment \(T(F)\) are examples of the same fragment so long as there exists a highly specified (but simple) transformation between them. These possible transformations include multiplicative augmentation, additive augmentation, mixed augmentation, flips into retrograde, subdivision, and “contiguous summation.”

Harmony-Melody (hm) Tools

The decitala package also includes a number of tools for standard harmonic and melodic analysis of Messiaen’s music. The decitala.hm.molt allows users to generate Modes of Limited Transposition [MOLT] (1944) as follows:

>>> from decitala.hm import molt
>>> m2t2 = molt.MOLT(2, 2)
>>> m2t2
<moiseaux.MOLT mode=2, transposition=2>
>>> m2t2.color
['Gold', 'Brown']
>>> m2t2.pc_vector()
array([0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1])

We can also query the Modes of Limited Transposition for a given collection using the MOLT_query function.

>>> collection = [60, 62, 64, 66, 68]
>>> for scale in molt.MOLT_query(collection):
...   print(scale)
<moiseaux.MOLT mode=1, transposition=1>
<moiseaux.MOLT mode=3, transposition=1>
<moiseaux.MOLT mode=3, transposition=3>
<moiseaux.MOLT mode=6, transposition=1>
<moiseaux.MOLT mode=6, transposition=3>
<moiseaux.MOLT mode=6, transposition=5>
<moiseaux.MOLT mode=7, transposition=2>
<moiseaux.MOLT mode=7, transposition=4>
<moiseaux.MOLT mode=7, transposition=6>

The decitala.hm.contour module contains tools for generating the pitch contour of a set of pitches, as well as tools for processing. Notably, we can prune a given contour for its prime form (see Morris 1993) as follows:

>>> from decitala.hm import contour
>>> c = [1, 0, 0, 1, 2]
>>> contour.contour_to_prime_contour(c)
(array([1, 0, 2]), 2)

I’ve also implemented Robert Schultz’s contour reduction algorithm (see Schultz 2008):

>>> from decitala.hm import contour
>>> c = [1, 0, 0, 1, 2, 1, 2, 1, 2]
>>> contour.contour_to_schultz_prime_contour(c)
(array([1, 0, 2]), 3)