Source code for musicscore.util

import math
from typing import Union, List

from musicscore.exceptions import (
    WrongNumberOfChordsError,
    LyricSyllabicOrExtensionError,
)
from musicxml.xmlelement.xmlelement import *

note_types = {
    (1, 12): "32nd",
    (1, 11): "32nd",
    (2, 11): "16th",
    (3, 11): "16th",
    (4, 11): "eighth",
    (6, 11): "eighth",
    (8, 11): "quarter",
    (1, 10): "32nd",
    (3, 10): "16th",
    (1, 9): "32nd",
    (2, 9): "16th",
    (4, 9): "eighth",
    (8, 9): "quarter",
    (1, 8): "32nd",
    (3, 8): "16th",
    (7, 8): "eighth",
    (1, 7): "16th",
    (2, 7): "eighth",
    (3, 7): "eighth",
    (4, 7): "quarter",
    (6, 7): "quarter",
    (1, 6): "16th",
    (1, 5): "16th",
    (2, 5): "eighth",
    (3, 5): "eighth",
    (4, 5): "quarter",
    (6, 5): "quarter",
    (8, 5): "half",
    (1, 4): "16th",
    (2, 4): "eighth",
    (3, 4): "eighth",
    (7, 4): "quarter",
    (4, 3): "half",
    (1, 3): "eighth",
    (2, 3): "quarter",
    (3, 2): "quarter",
    (1, 2): "eighth",
    (1, 1): "quarter",
    (2, 1): "half",
    (3, 1): "half",
    (4, 1): "whole",
    (6, 1): "whole",
    (8, 1): "breve",
    (12, 1): "breve",
}

#:
XML_ARTICULATION_CLASSES = [
    XMLAccent,
    XMLStrongAccent,
    XMLStaccato,
    XMLTenuto,
    XMLDetachedLegato,
    XMLStaccatissimo,
    XMLSpiccato,
    XMLScoop,
    XMLPlop,
    XMLDoit,
    XMLFalloff,
    XMLBreathMark,
    XMLCaesura,
    XMLStress,
    XMLUnstress,
]

#:
XML_TECHNICAL_CLASSES = [
    XMLUpBow,
    XMLDownBow,
    XMLHarmonic,
    XMLOpenString,
    XMLThumbPosition,
    XMLFingering,
    XMLPluck,
    XMLDoubleTongue,
    XMLTripleTongue,
    XMLStopped,
    XMLSnapPizzicato,
    XMLFret,
    XMLString,
    XMLHammerOn,
    XMLPullOff,
    XMLBend,
    XMLTap,
    XMLHeel,
    XMLToe,
    XMLFingernails,
    XMLHole,
    XMLArrow,
    XMLHandbell,
    XMLBrassBend,
    XMLFlip,
    XMLSmear,
    XMLOpen,
    XMLHalfMuted,
    XMLHarmonMute,
    XMLGolpe,
    XMLOtherTechnical,
]

#:
XML_ORNAMENT_CLASSES = [
    XMLDelayedInvertedTurn,
    XMLDelayedTurn,
    XMLHaydn,
    XMLInvertedMordent,
    XMLInvertedTurn,
    XMLInvertedVerticalTurn,
    XMLMordent,
    XMLOtherOrnament,
    XMLSchleifer,
    XMLShake,
    XMLTremolo,
    XMLTrillMark,
    XMLTurn,
    XMLVerticalTurn,
    XMLWavyLine,
]

#:
XML_DYNAMIC_CLASSES = [
    XMLF,
    XMLFf,
    XMLFff,
    XMLFfff,
    XMLFffff,
    XMLFfffff,
    XMLFp,
    XMLFz,
    XMLMf,
    XMLMp,
    XMLP,
    XMLPf,
    XMLPp,
    XMLPpp,
    XMLPppp,
    XMLPpppp,
    XMLPppppp,
    XMLRf,
    XMLRfz,
    XMLSf,
    XMLSffz,
    XMLSfp,
    XMLSfpp,
    XMLSfz,
    XMLSfzp,
    XMLOtherDynamics,
]

#:
XML_OTHER_NOTATIONS = [
    XMLArpeggiate,
    XMLFermata,
    XMLFootnote,
    XMLGlissando,
    XMLLevel,
    XMLNonArpeggiate,
    XMLOtherNotation,
    XMLSlide,
    XMLSlur,
]

#:
XML_DIRECTION_TYPE_CLASSES = [
    XMLRehearsal,
    XMLSegno,
    XMLCoda,
    XMLWords,
    XMLSymbol,
    XMLWedge,
    XMLDashes,
    XMLBracket,
    XMLPedal,
    XMLMetronome,
    XMLOctaveShift,
    XMLHarpPedals,
    XMLDamp,
    XMLDampAll,
    XMLEyeglasses,
    XMLStringMute,
    XMLScordatura,
    XMLPrincipalVoice,
    XMLPercussion,
    XMLAccordionRegistration,
    XMLStaffDivide,
    XMLOtherDirection,
]

#:
XML_ORNAMENT_AND_OTHER_NOTATIONS = [XMLAccidentalMark]

#:
XML_DIRECTION_TYPE_AND_OTHER_NOTATIONS = [XMLDynamics]


[docs]def lcm(l): return math.lcm(*l)
[docs]def dToX(input_list, first_element=0): if isinstance(input_list, list) is False: raise TypeError("xToD(input_list)") else: output = [first_element] for i in range(len(input_list)): output.append(input_list[i] + output[i]) return output
[docs]def xToD(input_list): result = [] for i in range(1, len(input_list)): result.append(input_list[i] - input_list[i - 1]) return result
[docs]def isinstance_as_string( child: object, parent_class_names: Union[str, List[str]] ) -> bool: """ This function can be used to check if some class names (parent_class_names) can be found in another class's __mro__. If parent classes cannot be imported due to recursive imports this can be used instead of isinstance function. :param object child: :param str/[str] parent_class_names: :return: bool """ if isinstance(parent_class_names, str): parent_class_names = [parent_class_names] for parent_class_name in parent_class_names: if parent_class_name not in [cls.__name__ for cls in child.__class__.__mro__]: return False return True
def _chord_is_in_a_repetition(chord): my_index = chord.up.up.get_chords().index(chord) if my_index > 0 and not chord.is_tied_to_previous: all_previous_chords = [ chord.up.up.get_chords()[my_index - i] for i in range(1, my_index + 1) ] if set([ch.is_tied_to_previous for ch in all_previous_chords]) == {True}: return False previous_chord = all_previous_chords[0] if not previous_chord.is_rest and chord.has_same_pitches(previous_chord): return True return False
[docs]def slur_chords(chords, number=1, **kwargs): if len(chords) < 2: raise WrongNumberOfChordsError("util.slur_chords needs at list two chords.") chords[0].add_x(XMLSlur(type="start", number=number, **kwargs)) chords[-1].add_x(XMLSlur(type="stop", number=number)) for ch in chords[1:-1]: ch.add_x(XMLSlur(type="continue", number=number))
[docs]def trill_chords(chords, number=1, placement="above", **kwargs): if len(chords) < 2: raise WrongNumberOfChordsError("util.trill_chords needs at list two chords.") chords[0].add_x(XMLTrillMark(placement=placement, **kwargs)) chords[0].add_x( XMLWavyLine(type="start", number=number, **kwargs), placement=placement ) chords[-1].add_x( XMLWavyLine(type="stop", number=number, **kwargs), placement=placement ) for ch in chords[1:-1]: ch.add_x( XMLWavyLine(type="continue", number=number, **kwargs), placement=placement )
[docs]def wedge_chords(chords, wedge_type, number=1, placement="below", **kwargs): if len(chords) < 2: raise WrongNumberOfChordsError("util.wedge_chords needs at list two chords.") chords[0].add_x( XMLWedge(type=wedge_type, number=number, **kwargs), placement=placement ) chords[-1].add_x(XMLWedge(type="stop", number=number), placement=placement) for ch in chords[1:-1]: ch.add_x(XMLWedge(type="continue", number=number), placement=placement)
[docs]def bracket_chords( chords, line_type="solid", start_line_end="down", end_line_end="down", placement="above", number=1, ): if len(chords) < 2: raise WrongNumberOfChordsError("util.bracket_chords needs at list two chords.") chords[0].add_x( XMLBracket( type="start", line_end=start_line_end, line_type=line_type, number=number ), placement=placement, ) chords[-1].add_x( XMLBracket( type="stop", line_end=end_line_end, line_type=line_type, number=number ), placement=placement, ) for ch in chords[1:-1]: ch.add_x( XMLBracket( type="continue", line_end="none", line_type=line_type, number=number ), placement=placement, )
[docs]def octave_chords(chords, type="down", size=8, number=1): try: len(chords) except TypeError: chords = [chords] if type == "down": placement = "above" else: placement = "below" chords[0].add_x( XMLOctaveShift(type=type, size=size, number=number), placement=placement ) chords[-1].add_x( XMLOctaveShift(type="stop", size=size, number=number), placement=placement ) for ch in chords[1:-1]: ch.add_x( XMLOctaveShift(type="continue", size=size, number=number), placement=placement, )
def _generate_lyrics(lyrics, number=1, show_number=False, mode="list", **kwargs): def _get_syllables_extensions_from_group(syllabic_group): if syllabic_group[0] is None: raise LyricSyllabicOrExtensionError( "Syllabic or extension cannot be added to the beginning of a syllabic group." ) extensions_ = [] syllables_ = list(syllabic_group)[:] while syllables_[-1] is None: extensions_.append(syllables_.pop()) return [syllables_, extensions_] if isinstance(lyrics, str) or isinstance(lyrics, tuple): lyrics = [lyrics] output = [] if mode == "list": for i in range(len(lyrics)): ll = lyrics[i] if isinstance(ll, str): xl = XMLLyric(number=str(number), **kwargs) xl.xml_text = ll xl.xml_syllabic = "single" output.append(xl) elif isinstance(ll, tuple) or isinstance(ll, list): syllables, extensions = _get_syllables_extensions_from_group(ll) if len(syllables) == 1: xl = XMLLyric(number=str(number), **kwargs) xl.xml_text = syllables[0] xl.xml_syllabic = "single" if len(extensions) > 0: xl.xml_extend = XMLExtend(type="start") output.append(xl) else: for j in range(len(syllables)): xl = XMLLyric(number=str(number), **kwargs) l = syllables[j] if l: xl.xml_text = l if j == 0: xl.xml_syllabic = "begin" elif j == len(syllables) - 1: xl.xml_syllabic = "end" if len(extensions) > 0: xl.xml_extend = XMLExtend(type="start") else: xl.xml_syllabic = "middle" else: xl = None output.append(xl) for k in range(len(extensions)): xl = XMLLyric(number=str(number), **kwargs) if k == len(extensions) - 1: xl.xml_extend = XMLExtend(type="stop") else: xl.xml_extend = XMLExtend(type="continue") xl.xsd_check = False output.append(xl) elif ll is None: output.append(None) else: raise NotImplementedError(ll) else: raise NotImplementedError if show_number: first = XMLLyric(**kwargs) for k, i in output[0].attributes.items(): setattr(first, k, i) first.add_child(XMLSyllabic("single")) first.add_child(XMLText(f"{number}.")) first.add_child(XMLElision(" ")) for child in output[0].get_children(): first.add_child(child) output[0] = first return output
[docs]def split_list(original_list, split_indices): if not split_indices: return [original_list] if split_indices[0] == 0: split_indices = split_indices[1:] return [ original_list[i:j] for i, j in zip([0] + split_indices, split_indices + [None]) ]