Source code for musicscore.accidental

from typing import Optional, Union

from musicxml import XMLAccidental

from musicscore.musictree import MusicTree
from musicscore.xmlwrapper import XMLWrapper

__all__ = ['STANDARD', 'FLAT', 'SHARP', 'ENHARMONIC', 'FORCESHARP', 'FORCEFLAT', 'SIGNS', 'Accidental']
#:
STANDARD = {
    0: ('C', 0, 0),
    0.5: ('C', 0.5, 0),
    1: ('C', 1, 0),
    1.5: ('C', 1.5, 0),
    2: ('D', 0, 0),
    2.5: ('E', -1.5, 0),
    3: ('E', -1, 0),
    3.5: ('E', -0.5, 0),
    4: ('E', 0, 0),
    4.5: ('E', 0.5, 0),
    5: ('F', 0, 0),
    5.5: ('F', 0.5, 0),
    6: ('F', 1, 0),
    6.5: ('F', 1.5, 0),
    7: ('G', 0, 0),
    7.5: ('A', -1.5, 0),
    8: ('A', -1, 0),
    8.5: ('A', -0.5, 0),
    9: ('A', 0, 0),
    9.5: ('B', -1.5, 0),
    10: ('B', -1, 0),
    10.5: ('B', -0.5, 0),
    11: ('B', 0, 0),
    11.5: ('C', -0.5, 1)
}

#:
FLAT = {
    0: ('C', 0, 0),
    0.5: ('D', -1.5, 0),
    1: ('D', -1, 0),
    1.5: ('D', -0.5, 0),
    2: ('D', 0, 0),
    2.5: ('E', -1.5, 0),
    3: ('E', -1, 0),
    3.5: ('E', -0.5, 0),
    4: ('E', 0, 0),
    4.5: ('F', -0.5, 0),
    5: ('F', 0, 0),
    5.5: ('G', -1.5, 0),
    6: ('G', -1, 0),
    6.5: ('G', -0.5, 0),
    7: ('G', 0, 0),
    7.5: ('A', -1.5, 0),
    8: ('A', -1, 0),
    8.5: ('A', -0.5, 0),
    9: ('A', 0, 0),
    9.5: ('B', -1.5, 0),
    10: ('B', -1, 0),
    10.5: ('B', -0.5, 0),
    11: ('B', 0, 0),
    11.5: ('C', -0.5, 1)
}

#:
SHARP = {
    0: ('C', 0, 0),
    0.5: ('C', 0.5, 0),
    1: ('C', 1, 0),
    1.5: ('C', 1.5, 0),
    2: ('D', 0, 0),
    2.5: ('D', 0.5, 0),
    3: ('D', 1, 0),
    3.5: ('D', 1.5, 0),
    4: ('E', 0, 0),
    4.5: ('E', 0.5, 0),
    5: ('F', 0, 0),
    5.5: ('F', 0.5, 0),
    6: ('F', 1, 0),
    6.5: ('F', 1.5, 0),
    7: ('G', 0, 0),
    7.5: ('G', 0.5, 0),
    8: ('G', 1, 0),
    8.5: ('G', 1.5, 0),
    9: ('A', 0, 0),
    9.5: ('A', 0.5, 0),
    10: ('A', 1, 0),
    10.5: ('A', 1.5, 0),
    11: ('B', 0, 0),
    11.5: ('B', 0.5, 0)
}

#:
ENHARMONIC = {
    0: ('C', 0, 0),
    0.5: ('D', -1.5, 0),
    1: ('D', -1, 0),
    1.5: ('D', -0.5, 0),
    2: ('D', 0, 0),
    2.5: ('D', 0.5, 0),
    3: ('D', 1, 0),
    3.5: ('D', 1.5, 0),
    4: ('E', 0, 0),
    4.5: ('F', -0.5, 0),
    5: ('F', 0, 0),
    5.5: ('G', -1.5, 0),
    6: ('G', -1, 0),
    6.5: ('G', -0.5, 0),
    7: ('G', 0, 0),
    7.5: ('G', 0.5, 0),
    8: ('G', 1, 0),
    8.5: ('G', 1.5, 0),
    9: ('A', 0, 0),
    9.5: ('A', 0.5, 0),
    10: ('A', 1, 0),
    10.5: ('A', 1.5, 0),
    11: ('B', 0, 0),
    11.5: ('B', 0.5, 0)
}

#:
FORCESHARP = {
    0: ('B', 1, -1),
    0.5: ('B', 1.5, -1),
    1: ('B', 2, -1),
    1.5: ('C', 1.5, 0),
    2: ('C', 2, 0),
    2.5: ('D', 0.5, 0),
    3: ('D', 1, 0),
    3.5: ('D', 1.5, 0),
    4: ('D', 2, 0),
    4.5: ('E', 0.5, 0),
    5: ('E', 1, 0),
    5.5: ('E', 1.5, 0),
    6: ('E', 2, 0),
    6.5: ('F', 1.5, 0),
    7: ('F', 2, 0),
    7.5: ('G', 0.5, 0),
    8: ('G', 1, 0),
    8.5: ('G', 1.5, 0),
    9: ('G', 2, 0),
    9.5: ('A', 0.5, 0),
    10: ('A', 1, 0),
    10.5: ('A', 1.5, 0),
    11: ('A', 2, 0),
    11.5: ('B', 0.5, 0)
}

#:
FORCEFLAT = {
    0: ('D', -2, 0),
    0.5: ('D', -1.5, 0),
    1: ('D', -1, 0),
    1.5: ('D', -0.5, 0),
    2: ('E', -2, 0),
    2.5: ('E', -1.5, 0),
    3: ('F', -2, 0),
    3.5: ('F', -1.5, 0),
    4: ('F', -1, 0),
    4.5: ('F', -0.5, 0),
    5: ('G', -2, 0),
    5.5: ('G', -1.5, 0),
    6: ('G', -1, 0),
    6.5: ('G', -0.5, 0),
    7: ('A', -2, 0),
    7.5: ('A', -1.5, 0),
    8: ('A', -1, 0),
    8.5: ('A', -0.5, 0),
    9: ('B', -2, 0),
    9.5: ('B', -1.5, 0),
    10: ('C', -2, 1),
    10.5: ('C', -1.5, 1),
    11: ('C', -1, 1),
    11.5: ('C', -0.5, 1)
}

#:
SIGNS = {-2: 'flat-flat',
         -1.5: 'three-quarters-flat',
         -1: 'flat',
         -0.5: 'quarter-flat',
         0: 'natural',
         0.5: 'quarter-sharp',
         1: 'sharp',
         1.5: 'three-quarters-sharp',
         2: 'double-sharp'
         }


[docs]class Accidental(MusicTree, XMLWrapper): """ Parent type: :obj:`~musicscore.midi.Midi` Child type: None Accidental is the class for managing :obj:`musicscore.midi.Midi`'s accidental sign and its pitch parameters: step, alter, octave. The parameter mode ('standard', 'enharmonic', 'flat', 'sharp', 'force-flat', 'force-sharp') can be used to set different enharmonic variants of the same pitch. """ _ATTRIBUTES = {'mode', 'show', 'parent_midi'} XMLClass = XMLAccidental def __init__(self, mode='standard', show: Optional[bool] = None, **kwargs): super().__init__() self._xml_object = self.XMLClass(value_='natural', **kwargs) self._mode = None self._show = None self.show = show self.mode = mode def _update(self): self._update_parent_midi() self._update_xml_object() def _update_parent_midi(self): if self.parent_midi and self.parent_midi.value != 0: self.parent_midi._update_pitch_parameters() def _update_xml_object(self): if self.sign: self._xml_object.value_ = self.sign @XMLWrapper.xml_object.getter def xml_object(self) -> Optional[XMLClass]: """ If an attribute is not found directly in a :obj:`~musicscore.musictree.MusicTree` class which inherits this class, it wll be passed on to ``__get_attribute__`` and ``__set__attribute__`` methods of ``xml_object``. It can also use the short cut attributes of :obj:`~musicxml.xmlelement.xmlelement.XMLElement` to get or set the first child. :return: wrapped MusicXML element of type :obj:`XMLClass` """ if self.parent_midi and self.parent_midi.value == 0: return None if self.show is True: return self._xml_object elif self.show is False: return None @property def mode(self): """ permitted modes: ``standard``, ``flat``, ``sharp``, ``enharmonic``, ``force-sharp``, ``force-flat`` :return: accidental mode which corresponds to global variables :obj:`~musicscore.accidental.STANDARD`, :obj:`~musicscore.accidental.FLAT`, :obj:`~musicscore.accidental.SHARP`, :obj:`~musicscore.accidental.ENHARMONIC`, :obj:`~musicscore.accidental.FORCESHARP`, :obj:`~musicscore.accidental.FORCEFLAT` """ return self._mode @mode.setter def mode(self, value): permitted = ('standard', 'flat', 'sharp', 'enharmonic', 'force-sharp', 'force-flat') if value not in permitted: raise TypeError(f'accidental_mode.value {value} must be in {permitted}') self._mode = value self._update_xml_object() self._update_parent_midi() @property def parent_midi(self) -> 'Midi': """ :return: The midi parent of Accidental in the :obj:`~musicscore.musictree.MusicTree` structure. It is equivalent to :obj:`up` or :obj:`get_parent()` :rtype: :obj:`musicscore.midi.Midi` """ return self.up @parent_midi.setter def parent_midi(self, val): val.add_child(self) @property def sign(self) -> Optional[str]: """ Converts ``alter`` parameter of :obj:`~get_pitch_parameters` into an actual ``sign`` depending on :obj:`~mode`. :obj:`~parent_midi` must be set first. :return: Possible values: ``flat-flat``, ``three-quarters-flat``, ``flat``, ``quarter-flat``, ``natural``, ``quarter-sharp``, ``sharp``, ``three-quarters-sharp``, ``double-sharp`` """ try: alter = self.get_pitch_parameters()[1] return SIGNS[alter] except TypeError: return None @property def show(self) -> bool: """ If ``False`` ``xml_object`` will be ``None`` and no accidental is shown in the :obj:`~musicscore.score.Score`. If ``True`` the accidental is shown. If the value of one of the following xml_object :obj:`~musicxml.xmlelement.xmlelement.XMLAccidental` properties:``self.parentheses``, ``self.editorial``. ``self.cautionary`` or ``self.bracket`` is set to yes, the accidental is always shown regardless of this property """ if 'yes' in [self.parentheses, self.editorial, self.cautionary, self.bracket]: return True return self._show @show.setter def show(self, val): if val is not None and not isinstance(val, bool): raise TypeError if val != self._show: self._show = val try: self.up.up._update_xml_accidental() except AttributeError: pass
[docs] def get_pitch_parameters(self, midi_value: Optional[Union[int, float]] = None) -> Optional[tuple]: """ :param: a valid midi value or ``None``. If ``midi_value == 0`` (for a rest) return value is ``None``. If ``midi_value is None`` and a :obj:`~parent_midi` exists, parent_midi's value will be used. :return: A tuple consisting of pitch step name, alter value and octave value: ``(step, alter, octave)`` .. seealso:: :obj:`~mode` """ if midi_value is None: if self.parent_midi: midi_value = self.parent_midi.value else: return None if midi_value == 0: return None if self.mode == 'standard': output = STANDARD[midi_value % 12] elif self.mode == 'enharmonic': output = ENHARMONIC[midi_value % 12] elif self.mode == 'force-sharp': output = FORCESHARP[midi_value % 12] elif self.mode == 'force-flat': output = FORCEFLAT[midi_value % 12] elif self.mode == 'flat': output = FLAT[midi_value % 12] elif self.mode == 'sharp': output = SHARP[midi_value % 12] else: raise ValueError return output[0], output[1], output[2] + (int(midi_value // 12)) - 1
def __copy__(self): return self.__class__(mode=self.mode, show=self.show)