Source code for mutwo.music_converters.metricities
"""Calculate metricity of a bar"""
import functools
import math
import operator
import typing
from sympy.ntheory import factorint
from mutwo import core_converters
from mutwo import core_utilities
__all__ = ("RhythmicalStrataToIndispensability",)
[docs]class RhythmicalStrataToIndispensability(core_converters.abc.Converter):
"""Builds metrical indispensability for a rhythmical strata.
This technique has been described by Clarence Barlow in `On the Quantification
of Harmony and Metre` (1992). The technique aims to model the weight
of single beats in a particular metre. It allocates each beat of a metre
to a specific value that describes the `indispensability` of a beat: the higher the
assigned value, the more accented the beat.
"""
# ###################################################################### #
# static methods #
# ###################################################################### #
@staticmethod
def _indispensability_of_nth_beat_for_simple_meter(
beat_index: int, prime_number: int
) -> int:
"""Calculate indispensability for nth beat of a metre that is composed of a singular prime number."""
if prime_number == 2:
return prime_number - beat_index
elif beat_index == prime_number - 1:
return int(prime_number / 4)
else:
factorised = tuple(
sorted(factorint(prime_number - 1, multiple=True), reverse=True)
)
q = RhythmicalStrataToIndispensability._indispensability_of_nth_beat(
beat_index - int(beat_index / prime_number), factorised
)
return int(q + (2 * math.sqrt((q + 1) / prime_number)))
@staticmethod
def _indispensability_of_nth_beat(
beat_index: int, rhythmical_strata: tuple[int, ...]
) -> int:
"""Calculate indispensability for the nth beat of a metre.
The metre is defined with the primes argument.
"""
z = len(rhythmical_strata)
rhythmical_strata = (1,) + rhythmical_strata + (1,)
r_sum = []
for r in range(0, z):
up = (beat_index - 2) % functools.reduce(
operator.mul, tuple(rhythmical_strata[j] for j in range(1, z + 1))
)
down = functools.reduce(
operator.mul, tuple(rhythmical_strata[z + 1 - k] for k in range(r + 1))
)
local_result = 1 + (int(1 + (up / down)) % rhythmical_strata[z - r])
base_indispensability = RhythmicalStrataToIndispensability._indispensability_of_nth_beat_for_simple_meter(
local_result, rhythmical_strata[z - r]
)
product = functools.reduce(
operator.mul, tuple(rhythmical_strata[i] for i in range(0, z - r))
)
r_sum.append(product * base_indispensability)
return sum(r_sum)
# ###################################################################### #
# public methods for interaction with the user #
# ###################################################################### #
[docs] def convert(
self, rhythmical_strata_to_convert: typing.Sequence[int]
) -> tuple[int, ...]:
"""Convert indispensability for each beat of a particular metre.
:param rhythmical_strata_to_convert: The rhythmical strata defines
the metre for which the indispensability shall be calculated. The
rhythmical strata is a list of prime numbers which product is the
amount of available beats within the particular metre. Earlier
prime numbers in the rhythmical strata are considered to be more
important than later prime numbers.
:return: A tuple of a integer for each beat of the respective metre where
each integer describes how accented the particular beat is (the higher the
number, the more important the beat).
**Example:**
>>> from mutwo import music_converters
>>> metricity_converter = music_converters.RhythmicalStrataToIndispensability()
>>> metricity_converter.convert((2, 3)) # time signature 3/4
(5, 0, 3, 1, 4, 2)
>>> metricity_converter.convert((3, 2)) # time signature 6/8
(5, 0, 2, 4, 1, 3)
"""
# inverse so that the first numbers are more important than the later numbers
rhythmical_strata_to_convert = tuple(reversed(rhythmical_strata_to_convert))
length = functools.reduce(operator.mul, rhythmical_strata_to_convert)
return tuple(
RhythmicalStrataToIndispensability._indispensability_of_nth_beat(
i + 1, rhythmical_strata_to_convert
)
for i in range(length)
)