utils

exception decitala.utils.UtilsException[source]

Bases: Exception

decitala.utils.get_logger(name, print_to_console=True, write_to_file=None)[source]

A simple helper for logging.

Parameters
  • name (str) – name of the logging object to be created.

  • print_to_console (bool) – whether to print the logs to the console.

  • write_to_file (str) – optional filepath to save the logs to.

decitala.utils.carnatic_string_to_ql_array(string_)[source]
Parameters

string (str) – A string of carnatic durations separated by spaces.

Returns

The input string converted to a quarter length array.

Return type

numpy.array.

>>> carnatic_string_to_ql_array('oc o | | Sc S o o o')
array([0.375, 0.25 , 0.5  , 0.5  , 1.5  , 1.   , 0.25 , 0.25 , 0.25 ])
decitala.utils.ql_array_to_carnatic_string(ql_array)[source]
Parameters

ql_array – A quarter length array.

Returns

The quarter length array converted to carnatic notation.

Return type

str

>>> ql_array_to_carnatic_string([0.5, 0.25, 0.25, 0.375, 1.0, 1.5, 1.0, 0.5, 1.0])
'| o o oc S Sc S | S'
decitala.utils.ql_array_to_greek_diacritics(ql_array)[source]

Returns the input ql_array in greek prosodic notation. This notation only allows for two types of rhythmic values (long & short).

Parameters

ql_array – A quarter length array.

Returns

The quarter length array converted to greek prosodic notation.

Return type

str

>>> ql_array_to_greek_diacritics(ql_array=[1.0, 0.5, 0.5, 1.0, 1.0, 0.5])
'–– ⏑ ⏑ –– –– ⏑'
decitala.utils.roll_window(array, window_size, fn=None)[source]

Takes in a list and returns a numpy vstack holding rolling windows of length window_size.

Parameters
  • array – a list, tuple, numpy array, etc.

  • window_size (int) – size of the window

  • fn (lambda) – a function evaluating a bool; will only iterate over elements satifying a condition.

Returns

A rolling windows of array, each of length window_size.

Return type

numpy.vstack

>>> composers = np.array(['Mozart', 'Monteverdi', 'Messiaen', 'Mahler', 'MacDowell', 'Massenet'])
>>> for window in roll_window(array=composers, window_size=3):
...     print(window)
('Mozart', 'Monteverdi', 'Messiaen')
('Monteverdi', 'Messiaen', 'Mahler')
('Messiaen', 'Mahler', 'MacDowell')
('Mahler', 'MacDowell', 'Massenet')
>>> contour_extrema = [[0, {1, -1}], [4, {1}], [2, {-1}], [5, {1}], [5, {1}], [1, {1, -1}]]
>>> max_check = lambda x: 1 in x[1]
>>> for this_frame in roll_window(contour_extrema, window_size=3, fn=max_check):
...     print(this_frame)
([0, {1, -1}], [4, {1}], [5, {1}])
([4, {1}], [5, {1}], [5, {1}])
([5, {1}], [5, {1}], [1, {1, -1}])
decitala.utils.power_list(data)[source]
Parameters

data – an iterable

Returns

power set of the data as a list (excluding the empty list).

Return type

list

>>> l = [1, 2, 3]
>>> power_list(l)
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
>>> for x in power_list([(0.0, 2.0), (4.0, 5.5), (6.0, 7.25)]):
...     print(x)
((0.0, 2.0),)
((4.0, 5.5),)
((6.0, 7.25),)
((0.0, 2.0), (4.0, 5.5))
((0.0, 2.0), (6.0, 7.25))
((4.0, 5.5), (6.0, 7.25))
((0.0, 2.0), (4.0, 5.5), (6.0, 7.25))
decitala.utils.get_shells(data)[source]
Parameters

data – an iterable

Returns

a list holding tuples. Each tuple holds the shells in outer-to-inner order. If the data holds an odd number of elements, the central element is returned as a singleton.

Return type

list

>>> l = [1, 2, 3, 4]
>>> get_shells(l)
[(1, 4), (2, 3)]
>>> l2 = [('bob', 1), ('sue', 2), ('andrew', 3), ('luke', 4), ('alex', 5)]
>>> get_shells(l2)
[(('bob', 1), ('alex', 5)), (('sue', 2), ('luke', 4)), (('andrew', 3),)]
decitala.utils.augment(ql_array, factor=1.0, difference=0.0)[source]

Returns an augmentation in the style of Messiaen. If difference is set to 0.0, then the augmentation is multiplicative. If factor is set to 1.0, then augmentation is additive. If factor & difference are non-zero, we have a mixed augmentation.

Parameters
  • ql_array – A quarter length array.

  • factor (float) – The factor for multiplicative augmentation.

  • difference (float) – The factor for additive augmentation.

Returns

The fragment augmented by the given factor and difference.

Return type

numpy.array

>>> augment(ql_array=[1.0, 1.0, 0.5, 0.25], factor=2.0, difference=0.25)
array([2.25, 2.25, 1.25, 0.75])
decitala.utils.stretch_augment(ql_array, factor, stretch_factor)[source]

A special kind of rhythmic augmentation catered toward the Greek metrics. The “short” is kept as is and the “long” is modified by the given difference. The new long must still be longer than the short value.

Parameters

ql_array – A quarter length array.

>>> stretch_augment(ql_array=[1.0, 2.0], factor=0.125, stretch_factor=0.25)
array([0.125, 0.5  ])
decitala.utils.successive_ratio_array(ql_array)[source]

Returns array defined by the ratio of successive elements. By convention, we set the first value to 1.0.

Parameters

ql_array – A quarter length array.

Returns

An array consisting of successive ratios of the input elements.

Return type

numpy.array

>>> successive_ratio_array([1.0, 1.0, 2.0, 0.5, 0.5, 0.25, 1.0])
array([1.  , 1.  , 2.  , 0.25, 1.  , 0.5 , 4.  ])
decitala.utils.successive_difference_array(ql_array)[source]

Returns the first order difference of ql_array (contiguous differences).

Parameters

ql_array – A quarter length array.

Returns

An array consisting of successive differences of the input elements.

Return type

numpy.array

>>> successive_difference_array([0.25, 0.25, 0.75, 0.75, 0.5, 1.0, 1.5])
array([ 0.  ,  0.  ,  0.5 ,  0.  , -0.25,  0.5 ,  0.5 ])
decitala.utils.dseg(ql_array, reduced=False, as_str=False)[source]
Parameters
  • reduced (bool) – Whether to remove equal contiguous values.

  • as_str (bool) – Whether to make the return type a string.

Returns

The d-seg of the fragment, as introducted in The Perception of Rhythm in Non-Tonal Music (Marvin, 1991). Maps a fragment into a sequence of relative durations.

Return type

numpy.array (or string if as_str=True).

>>> g3 = fragment.GeneralFragment(np.array([0.25, 0.75, 2.0, 1.0]), name='marvin-p70')
>>> g3.dseg()
array([0, 1, 3, 2])
decitala.utils.non_retrogradable_measures(filepath, part_num)[source]

Function for retrieving all non-retrogradable measures in a given filepath and part number.

decitala.utils.find_clusters(input_, data_mode=False)[source]

Finds the regions with consecutive equal elements.

Parameters
Returns

a list of cluster indices; if not in data mode, regions with equal quarter length values; if data_mode=True, then regions where the quarter lengths and pitch content are equal.

>>> varied_ragavardhana = np.array([1, 1, 1, 0.5, 0.75, 0.75, 0.5, 0.25, 0.25, 0.25])
>>> clusters = find_clusters(varied_ragavardhana)
>>> clusters
[[0, 2], [4, 5], [7, 9]]
>>> varied_ragavardhana[clusters[0][0]:clusters[0][1]+1]
array([1., 1., 1.])
>>> varied_ragavardhana[clusters[1][0]:clusters[1][1]+1]
array([0.75, 0.75])
>>> varied_ragavardhana[clusters[2][0]:clusters[2][1]+1]
array([0.25, 0.25, 0.25])
>>> # We can also find clusters of pitch and rhythmic information for data from get_object_indices.
>>> from music21 import note, chord
>>> example_data = [
...             (note.Note("F#"), (6.5, 6.75)),
...     (note.Note("G"), (6.75, 7.0)),
...     (note.Note("G"), (7.0, 7.25)),
...     (note.Note("C#"), (7.25, 7.5)),
...     (note.Note("G"), (7.5, 7.75)),
...     (note.Note("G"), (7.75, 8.0)),
...     (note.Note("A-"), (8.0, 8.125)),
... ]
>>> find_clusters(example_data, data_mode=True)
[[1, 2], [4, 5]]
>>> example_data2 = [
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.0, 0.125)),
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.125, 0.25)),
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.25, 0.375)),
...     (chord.Chord(["E-3", "D4"], quarterLength=0.125), (0.375, 0.5)),
...     (chord.Chord(["A2", "A-3"], quarterLength=0.25), (0.5, 0.75)),
... ]
>>> find_clusters(example_data2, data_mode=True)
[[0, 2]]
decitala.utils.find_possible_superdivisions(ql_array, include_self=True)[source]

There is a more general approach to the subdivision problem, but we note that Messiaen’s subdivision of tala components tends to be even.

>>> long_fragment = np.array([1, 1, 1, 0.5, 0.75, 0.75, 0.5, 0.25, 0.25, 0.25])
>>> for x in find_possible_superdivisions(long_fragment):
...     print(x)
[1.   1.   1.   0.5  0.75 0.75 0.5  0.25 0.25 0.25]
[3.   0.5  0.75 0.75 0.5  0.25 0.25 0.25]
[1.   1.   1.   0.5  1.5  0.5  0.25 0.25 0.25]
[1.   1.   1.   0.5  0.75 0.75 0.5  0.75]
[3.   0.5  1.5  0.5  0.25 0.25 0.25]
[3.   0.5  0.75 0.75 0.5  0.75]
[1.   1.   1.   0.5  1.5  0.5  0.75]
[3.   0.5  1.5  0.5  0.75]
>>> varied_ragavardhana = np.array([1, 1, 1, 0.5, 0.75, 0.5])
>>> for x in find_possible_superdivisions(varied_ragavardhana, include_self=False):
...     print(x)
[3.   0.5  0.75 0.5 ]
decitala.utils.net_ql_array(filepath, part_num, include_rests=False, ignore_grace_notes=True)[source]

Function for retrieving all quarter lengths from a part number in a filepath.

decitala.utils.transform_to_time_scale(ql_array)[source]

Transforms a quarter length array to time-scale (binary) notation.

Parameters

ql_array – A quarter length array.

>>> udikshana = fragment.Decitala("Udikshana")
>>> udikshana.ql_array()
array([0.5, 0.5, 1. ])
>>> transform_to_time_scale(ql_array=udikshana.ql_array())
array([1, 1, 1, 0])
decitala.utils.get_object_indices(filepath, part_num, measure_divider_mode=None, ignore_grace=False)[source]

Returns data of the form [(object, (start, end)), …] for a given file path and part number. (Supports rests and grace notes.)

Parameters
  • filepath (str) – Path to file to be analyzed.

  • part_num (int) – Part number to be analyzed.

  • measure_divider_mode (str) – Tool used for dividing the data into measures. If measure_divider_mode="str", the data is returned with "B" used as the measure marker. If instead measure_divider_mode="list", the data is returned partitioned by measure. The default is None, so no measure divisions are present.

  • ignore_grace (bool) – Whether to ignore grace notes in the output. False by default.

decitala.utils.phrase_divider(filepath, part_num)[source]

This is an oversimplified but useful tool for phrase analysis (particularly for the birdsongs). It returns the same output as decitala.utils.get_object_indices but divides the output by “phrases,” only as defined by the appearance of rests and fermatas. Ignores all dividers.

Parameters
  • filepath (str) – Path to file to be analyzed.

  • part_num (int) – Part number to be analyzed.

decitala.utils.reframe_ts(ts, new_denominator=None)[source]

Function for reducing a music21.meter.TimeSignature object (lowest denominator of 1) to a given denominiator.

Parameters

ts – A music21.meter.TimeSignature object.

Returns

A new time signature that is fully reduced by removing all possible powers of 2.

Return type

music21.meter.TimeSignature

>>> from music21.meter import TimeSignature
>>> reframe_ts(TimeSignature("4/16"))
<music21.meter.TimeSignature 1/4>
>>> reframe_ts(TimeSignature("4/4"), new_denominator=2)
<music21.meter.TimeSignature 2/2>
decitala.utils.rolling_SRR(filepath, part_num, window_size, ignore_tuplets=True)[source]

Given a filepath, part number, and window size, extracts all notes/chords in the work and, on a rolling window, calculates the successive ratio representation (SRR) of each window. Returning the results as a list.

Parameters
  • filepath (str) – Path to file to be analyzed.

  • part_num (int) – Part number to be analyzed.

  • window_size (int) – Window size used in the calculations.

  • ignore_tuplets (bool) – Whether to ignore tuplets in the output. The use of tuplets will add some erroneous ratios to the data. Default is True.

Returns

List holding successive ratio representations of each window on a rolling window of size window_size.

Return type

list

decitala.utils.contiguous_summation(data)[source]

Given some data from get_object_indices, finds every location where the pitch and rhythmic material are contiguously equal and sums these regions.

>>> from music21 import note, chord
>>> example_data = [
...             (note.Note("F#"), (6.5, 6.75)),
...     (note.Note("G"), (6.75, 7.0)),
...     (note.Note("G"), (7.0, 7.25)),
...     (note.Note("C#"), (7.25, 7.5)),
...     (note.Note("G"), (7.5, 7.75)),
...     (note.Note("G"), (7.75, 8.0)),
...     (note.Note("A-"), (8.0, 8.125)),
... ]
>>> for this_object in contiguous_summation(example_data):
...     print(this_object)
(<music21.note.Note F#>, (6.5, 6.75))
(<music21.note.Note G>, (6.75, 7.25))
(<music21.note.Note C#>, (7.25, 7.5))
(<music21.note.Note G>, (7.5, 8.0))
(<music21.note.Note A->, (8.0, 8.125))
>>> # Also works with chords.
>>> example_data2 = [
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.0, 0.125)),
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.125, 0.25)),
...     (chord.Chord(["F#2", "F3"], quarterLength=0.125), (0.25, 0.375)),
...     (chord.Chord(["E-3", "D4"], quarterLength=0.125), (0.375, 0.5)),
...     (chord.Chord(["A2", "A-3"], quarterLength=0.25), (0.5, 0.75)),
... ]
>>> sum_search = contiguous_summation(example_data2)
>>> for this_object in sum_search:
...     print(this_object)
(<music21.chord.Chord F#2 F3>, (0.0, 0.375))
(<music21.chord.Chord E-3 D4>, (0.375, 0.5))
(<music21.chord.Chord A2 A-3>, (0.5, 0.75))
>>> # The quarter lengths of the objects change according to the new summation.
>>> for this_object in sum_search:
...     print(this_object[0].quarterLength)
0.375
0.125
0.25
decitala.utils.filter_single_anga_class_fragments(data)[source]
Parameters

data (list) – data from rolling_search.

Returns

data from the input with all single-anga-class talas removed. For information on anga-class, see: num_anga_classes.

Return type

list

>>> from decitala.fragment import GreekFoot
>>> data = [
... {'fragment': GreekFoot("Spondee"), 'mod': ('r', 0.125), 'onset_range': (0.0, 0.5), 'is_spanned_by_slur': False, 'pitch_content': [(80,), (91,)]},  # noqa: E501
... {'fragment': GreekFoot("Trochee"), 'mod': ('r', 0.125), 'onset_range': (0.25, 0.625), 'is_spanned_by_slur': False, 'pitch_content': [(91,), (78,)]},
... {'fragment': GreekFoot("Spondee"), 'mod': ('r', 0.0625), 'onset_range': (0.5, 0.75), 'is_spanned_by_slur': False, 'pitch_content': [(78,), (85,)]},
... {'fragment': GreekFoot("Iamb"), 'mod': ('r', 0.125), 'onset_range': (0.625, 1.0), 'is_spanned_by_slur': False, 'pitch_content': [(85,), (93,)]},
... {'fragment': GreekFoot("Spondee"), 'mod': ('r', 0.125), 'onset_range': (0.75, 1.25), 'is_spanned_by_slur': False, 'pitch_content': [(93,), (91,)]}
... ]
>>> filtered = filter_single_anga_class_fragments(data)
>>> for x in filtered:
...     print(x)
{'fragment': <fragment.GreekFoot Trochee>, 'mod': ('r', 0.125), 'onset_range': (0.25, 0.625), 'is_spanned_by_slur': False, 'pitch_content': [(91,), (78,)]}
{'fragment': <fragment.GreekFoot Iamb>, 'mod': ('r', 0.125), 'onset_range': (0.625, 1.0), 'is_spanned_by_slur': False, 'pitch_content': [(85,), (93,)]}
decitala.utils.filter_sub_fragments(data, filter_in_retrograde=True)[source]
Parameters

data (list) – data from rolling_search.

Returns

data from the input with all sub-talas removed; that is, talas that sit inside of another.

Return type

list

decitala.utils.measure_by_measure_time_signatures(filepath)[source]

Returns list of meter.TimeSignature objects from music21 for each measure of an input stream.

decitala.utils.loader(filepath)[source]

Useful function for loading analyses into native python format (from a json).

Parameters

filepath (str) – path to analysis file in the databases/analyses directory.

Returns

analysis in native python types. Fragments and their associated onset range.

Return type

list

decitala.utils.write_analysis(data, filepath)[source]

Function for writing an analysis to JSON.

class decitala.utils.NormalizedCounter(iterable, count=False)[source]

Bases: collections.Counter

Class that inherits from collections.Counter. Takes in an array-like object, but the values of the ‘counter’ become proportions, unless count is set to True (in which case it acts like a regular counter).

>>> l = [1, 1, 2, 2, 3, 3, 4, 4]
>>> NormalizedCounter(l, count=True)
NormalizedCounter({1: 2, 2: 2, 3: 2, 4: 2})
>>> NormalizedCounter(l, count=False)
NormalizedCounter({1: 0.25, 2: 0.25, 3: 0.25, 4: 0.25})
decitala.utils.dict_to_ordered_dict(dict_in, key, reverse=True)[source]

Takes in a dict and returns an OrderedDict object sorted by a given key (in the form of a lambda expression).

Parameters
  • dict_in (dict) – a dictionary to be turned into an ordered dictionary.

  • key – lambda expression.

  • reverse (bool) – whether to sort the dict in reverse.

Returns

the dictionary input, ordered by the given key.

Return type

collections.OrderedDict