Source code for mutwo.music_parameters.volumes

"""Submodule for the parameter volume.

'Volume' is defined as any object that knows a :attr:`amplitude` attribute.
"""

import typing

import numpy as np  # type: ignore

from mutwo import core_constants
from mutwo import core_utilities
from mutwo import music_parameters

__all__ = ("DirectVolume", "DecibelVolume", "WesternVolume")


[docs]class DirectVolume(music_parameters.abc.Volume): """A simple volume class that gets directly initialised by its amplitude. :param amplitude: The amplitude of the :class:`DirectVolume` object. May be used when a converter class needs a volume object, but there is no need or desire for a complex abstraction of the respective volume. """ def __init__(self, amplitude: core_constants.Real): self._amplitude = amplitude @property def amplitude(self) -> core_constants.Real: return self._amplitude def __repr__(self) -> str: return "{}({})".format(type(self).__name__, self.amplitude)
[docs]class DecibelVolume(music_parameters.abc.Volume): """A simple volume class that gets directly initialised by decibel. :param decibel: The decibel of the :class:`DecibelVolume` object (should be from -120 to 0). May be used when a converter class needs a volume object, but there is no need or desire for a complex abstraction of the respective volume. """ def __init__(self, decibel: core_constants.Real): self._decibel = decibel @property def decibel(self) -> core_constants.Real: return self._decibel @decibel.setter def decibel(self, decibel: core_constants.Real): self._decibel = decibel @property def amplitude(self) -> core_constants.Real: return self.decibel_to_amplitude_ratio(self.decibel) def __repr__(self) -> str: return "{}({})".format(type(self).__name__, self.amplitude)
[docs]class WesternVolume(music_parameters.abc.Volume): """Volume with a traditional Western nomenclature. :param name: Dynamic indicator in traditional Western nomenclature ('f', 'pp', 'mf', 'sfz', etc.). For a list of all supported indicators, see :const:`mutwo.music_parameters.constants.DYNAMIC_INDICATOR_TUPLE`. :type name: str :param minimum_decibel: The decibel value which is equal to the lowest dynamic indicator (ppppp). :type minimum_decibel: core_constants.Real, optional :param maximum_decibel: The decibel value which is equal to the highest dynamic indicator (fffff). :type maximum_decibel: core_constants.Real, optional **Example:** >>> from mutwo.music_parameters import volumes >>> volumes.WesternVolume('fff') WesternVolume(fff) """ def __init__( self, name: str, minimum_decibel: typing.Optional[core_constants.Real] = None, maximum_decibel: typing.Optional[core_constants.Real] = None, ): minimum_decibel = ( minimum_decibel or music_parameters.configurations.DEFAULT_MINIMUM_DECIBEL_FOR_MIDI_VELOCITY_AND_STANDARD_DYNAMIC_INDICATOR ) maximum_decibel = ( maximum_decibel or music_parameters.configurations.DEFAULT_MAXIMUM_DECIBEL_FOR_MIDI_VELOCITY_AND_STANDARD_DYNAMIC_INDICATOR ) self.name = name self._standard_dynamic_indicator_to_decibel_mapping = ( WesternVolume._make_standard_dynamic_indicator_to_value_dict( minimum_decibel, maximum_decibel, float, ) ) self._dynamic_indicator_to_decibel_mapping = ( WesternVolume._make_dynamic_indicator_to_value_dict( self._standard_dynamic_indicator_to_decibel_mapping ) ) self._decibel_to_standard_dynamic_indicator_mapping = { decibel: dynamic_indicator for dynamic_indicator, decibel in self._standard_dynamic_indicator_to_decibel_mapping.items() } def __repr__(self) -> str: return f"{type(self).__name__}({self.name})" # ###################################################################### # # static private methods # # ###################################################################### # @staticmethod def _make_standard_dynamic_indicator_to_value_dict( minima: float, maxima: float, dtype: typing.Type[float] = float ) -> dict[str, float]: return { dynamic_indicator: decibel for dynamic_indicator, decibel in zip( music_parameters.constants.STANDARD_DYNAMIC_INDICATOR, np.linspace( minima, maxima, len(music_parameters.constants.STANDARD_DYNAMIC_INDICATOR), dtype=dtype, ), ) } @staticmethod def _make_dynamic_indicator_to_value_dict( standard_dynamic_indicator_to_value_dict: dict[str, float] ) -> dict[str, float]: dynamic_indicator_to_value_dict = {} dynamic_indicator_to_value_dict.update(standard_dynamic_indicator_to_value_dict) for ( special_dynamic_indicator, standard_dynamic_indicator, ) in ( music_parameters.constants.SPECIAL_DYNAMIC_INDICATOR_TO_STANDARD_DYNAMIC_INDICATOR_DICT.items() ): dynamic_indicator_to_value_dict.update( { special_dynamic_indicator: dynamic_indicator_to_value_dict[ standard_dynamic_indicator ] } ) return dynamic_indicator_to_value_dict # ###################################################################### # # class methods (alternative constructors) # # ###################################################################### #
[docs] @classmethod def from_amplitude(cls, amplitude: core_constants.Real) -> "WesternVolume": """Initialise `WesternVolume` from amplitude ratio. :param amplitude: The amplitude which shall be converted to a `WesternVolume` object. >>> from mutwo.music_parameters import volumes >>> volumes.WesternVolume.from_amplitude(0.05) WesternVolume(p) """ decibel = cls.amplitude_ratio_to_decibel(amplitude) return cls.from_decibel(decibel)
[docs] @classmethod def from_decibel(cls, decibel: core_constants.Real) -> "WesternVolume": """Initialise `WesternVolume` from decibel. :param decibel: The decibel which shall be converted to a `WesternVolume` object. >>> from mutwo.music_parameters import volumes >>> volumes.WesternVolume.from_decibel(-24) WesternVolume(p) """ volume_object = cls("mf") closest_decibel: float = core_utilities.find_closest_item( decibel, tuple(volume_object._decibel_to_standard_dynamic_indicator_mapping.keys()), ) indicator = volume_object._decibel_to_standard_dynamic_indicator_mapping[ closest_decibel ] volume_object.name = indicator return volume_object
# ###################################################################### # # properties # # ###################################################################### # @property def name(self) -> str: """The western nomenclature name for dynamic. For a list of all supported indicators, see :const:`mutwo.music_parameters.constants.DYNAMIC_INDICATOR_TUPLE`. """ return self._name @name.setter def name(self, name: str) -> None: try: assert name in music_parameters.constants.DYNAMIC_INDICATOR_TUPLE except AssertionError: raise ValueError( f"unknown dynamic name '{name}'. Supported dynamic names " f"are '{music_parameters.constants.DYNAMIC_INDICATOR_TUPLE}'." ) self._name = name @property def decibel(self) -> core_constants.Real: return self._dynamic_indicator_to_decibel_mapping[self.name] @property def amplitude(self) -> core_constants.Real: return self.decibel_to_amplitude_ratio(self.decibel)