Beispiel #1
0
def rule_6(data):
    """Les voix ne doivent pas toutes effectuer le même mouvement, sauf si on enchaîne deux accords de sixtes."""
    for group1, group2 in util.pairwise(tools.iter_melodies(*data)):
        try:
            assert len(group1) == len(group2) and "Error : pauses not handled"
        except:
            from IPython import embed;embed()
        is_same_mov =  True
        for i in range(len(group1)):
            for j in range(i+1,len(group1)):
                notes1 = sorted(util.to_pitch([group1[i],group1[j]]),key=lambda x : x.value.semitone)
                notes2 = sorted(util.to_pitch([group2[i],group2[j]]),key=lambda x : x.value.semitone)
                if motion.MotionType.motion(*notes1,*notes2) is not motion.MotionType.direct:
                    is_same_mov = False
                    break
            if is_same_mov is False:
                break

        if is_same_mov:
            # are there 2 consecutive sixth?
            pos1 = max(group1,key=lambda x:x.pos).pos
            scale1 = data[0].scaleAt(pos1)
            c1 = chord.AbstractChord.findBestChord(group1,scale1)
            pos2 = max(group2,key=lambda x:x.pos).pos
            scale2 = data[0].scaleAt(pos2)
            c2 = chord.AbstractChord.findBestChord(group2,scale2)

            if not (c1.isInversion(group1,1) and c2.isInversion(group2,1)):
                warn(f"All the voices shouldn't be in the same direction.",group1,group2)
Beispiel #2
0
def forbid_consecutive_interval(s1, s2, interval):
    """Warn if two intervals of the same type are consecutive
    Interval must be qualified"""
    for (na,n1), (nb, n2) in util.pairwise(tools.iter_melodies(s1,s2,all=True)):
        nap, nbp, n1p, n2p = [util.to_pitch(x) for x in (na, nb, n1, n2)]
        if nap.isQualifiedInterval(interval).With(n1p) and nbp.isQualifiedInterval(interval).With(n2p):
            error.warn(f"Two {interval} in a row are forbidden",na,nb,n1,n2,f"in {s1.title} and {s2.title}")
Beispiel #3
0
def forbid_false_relation_tritone(s1,s2,allow_in_minor=False):
    """
    Warn if tritone false relation is found
    """
    msg = f"False relation of tritone is forbidden. In {s1.title} and {s2.title}"
    def __fun(n1, n2):
        n1p, n2p = [util.to_pitch(x) for x in (n1,n2)]
        def __is_VI_VII_minor():
            if allow_in_minor is not True:
                return False
            if s1.scale.mode == scale.Mode.m_rising:
                _scale = s1.scale.mode
            elif s1.scale.mode == scale.Mode.m_full:
                _scale = s1.scale.minor_scales[scale.Mode.m_rising]
            elif s1.scale.mode == scale.Mode.M:
                _scale = s1.scale.mode.relative(scale.Mode.m_rising)
            else:
                return False 
            try:
                return _scale.isDegree(n1p,6) or _scale.isDegree(n1p,7) or _scale.isDegree(n2p,6) or _scale.isDegree(n2p,7)
            except ValueError:
                return False

        if n1p.isTritoneWith(n2p) and not __is_VI_VII_minor():
            error.warn(msg,n1,n2)

    for (na, n1), (nb, n2) in util.pairwise(tools.iter_melodies(s1,s2,all=True)):
        nap, nbp, n1p, n2p = [util.to_pitch(x) for x in (na, nb, n1, n2)]
        __fun(na,n2)
        __fun(n1,nb)
Beispiel #4
0
def rule_8(s1,s2,min=pitch.Pitch.F3):
    """Les tierces dans le grave sont lourdes et à éviter.
    Here, we choose to warn if the third is under F3"""
    for n1, n2 in tools.iter_melodies(s1,s2):
        np1,np2 = util.to_pitch((n1,n2))
        if np1.isInterval(3).With(np2) and (np1 < min and np2 < min):
            warn(f"Low thirds should be avoided",n1,n2,s1.title,s2.title)
Beispiel #5
0
    def motion(cls, *seq, **kw):  # TEST
        """Find the motion of seq.
        seq is a sequence of four Pitch: the first two
        is the first harmonic interval, the second is the 
        second interval.The first, the higher.
        It is possible to pass a sequence directly.
        If kw nosort == True, the seq isn't sorted at all
        """
        __sort = lambda x, y: (x, y) if x.value.step < y.value.step else (y, x)
        if len(seq) != 4:
            seq = seq[0]
        assert len(seq) == 4

        seq = [util.to_pitch(elt) for elt in seq]
        h1, l1, h2, l2 = seq
        if kw.get('nosort', False) is False:
            h1, l1 = __sort(h1, l1)
            h2, l2 = __sort(h2, l2)

        if h1 == h2 and l1 == l2:  # no movement
            return cls.no
        if h1 == h2 or l1 == l2:  # oblique
            return cls.oblique

        if (h1.value.semitone > h2.value.semitone
                and l1.value.semitone > l2.value.semitone) or (
                    h1.value.semitone < h2.value.semitone
                    and l1.value.semitone < l2.value.semitone):
            return cls.direct

        return cls.contrary
def rule_4(cp,cf):
    """Le temps fort doit être en consonance. Il sera toléré, dans les cas difficiles, de placer la dissonance au temps fort, à la condition qu'elle se produise dans la forme de broderie ou de note de passage et que les deux parties procèdent par mouvement contraire et par degrés conjoints"""
    # in this function, we do not look at the first note if the first note is at the downbeat, for it is already checked by function rule_3
    for (n1, na), (n2, nb) in util.pairwise(tools.iter_melodies(cp,cf,alone=False)):
        # is it downbeat?
        if cp.isUpBeat(n2):
            continue
        n1p, nap, n2p, nbp = [util.to_pitch(x) for x in (n1,na, n2, nb)]
        #is it a consonance?
        if n2p.isConsonantWith(nbp):
            continue
        #is it a cambiata or a passing tone?
        is_pt_or_c = cp.isCambiata(n2,False) or cp.isPassingTone(n2,False)
        #is it a conjunct movement?
        is_conjunct = cp.isConjunct(n2)
        #is it a contray motion?
        first = [n1p,nap]
        second = [n2p,nbp]
        first.sort(key=lambda x : x,reverse=True)
        second.sort(key=lambda x : x,reverse=True)
        is_contrary = motion.MotionType.motion(*first,*second) == motion.MotionType.contrary

        warn(f"""The downbeat is dissonant. It must be:
        - a cambiata or passing tone: {is_pt_or_c}
        - come from a conjunct motion: {is_conjunct}
        - come from a contrary motion: {is_contrary}
        """,cp.title,n2)
Beispiel #7
0
def rule_16(s1,s2):
    """Les croisements sont interdits"""
    is_s1_higher = s1.notes[0].pitch > s2.notes[0].pitch
    for n1, n2 in tools.iter_melodies(s1,s2):
        p1,p2 = [util.to_pitch(n) for n in (n1,n2)]
        if (is_s1_higher and p2 > p1) or (not is_s1_higher and p1 > p2):
            warn(f"Intersection is forbidden",n1,n2,s1.title,s2.title)
def rule_13(cp,cf):
    """Éviter la broderie ou la note de passage qui produit l'intervalle de seconde mineure avec le chant donné"""
    for (n1, na), (n2, nb) in util.pairwise(tools.iter_melodies(cp,cf,alone=False)):
        if not (cp.isCambiata(n2) or cp.isPassingTone(n2)):
            continue
        n1p, nap, n2p, nbp = [util.to_pitch(x) for x in (n1,na, n2, nb)]
        if n2p.semitoneWith(nbp) == 1:
            warn(f"Avoid minor seconds",cp.title,n2)
Beispiel #9
0
def rule_4(voice):
    """Les tritons en quatre notes sont à surveiller"""
    # AJOUTER: s'ils sont dans la même direction (sinon peu importe)
    # TODO
    for notes in util.nwise(voice,4):
        np1,*other,np4 = util.to_pitch(notes)
        if np1.semitoneWith(np4) == 6:
            warn(f"Tritone in 4 notes must be checked.",notes,voice.title)
def rule_5(cp, cf):
    """Le temps faible peut être en consonance ou en dissonance, pourvu que la dissonance se produise par degrés conjoints"""
    for (n1, na), (n2, nb) in util.pairwise(tools.iter_melodies(cp,cf,alone=False)):
        if not cp.isUpBeat(n2):
            continue
        n1p, nap, n2p, nbp = [util.to_pitch(x) for x in (n1,na, n2, nb)]
        if not n2p.isConsonantWith(nbp):
            if not cp.isConjunct(n2):
                warn(f"If the upbeat is dissonant, it must come from a conjunct motion.",cp.title,n2)
Beispiel #11
0
def rule_9(s1, s2):
    """Ne pas employer de croisements ni d'unissons"""
    s1_high = s1.notes[0].pitch > s2.notes[0].pitch
    for n1, n2 in zip(s1.notes, s2.notes):
        p1, p2 = [util.to_pitch(n) for n in (n1, n2)]
        if p1 == p2:
            raise error.CompositionError(f"Unisons are forbidden", n1, n2)

        if (s1_high and p2 > p1) or (not s1_high and p1 > p2):
            raise error.CompositionError(f"Intersection is forbidden", n1, n2)
Beispiel #12
0
def forbid_direct_interval(s1, s2, *intervals):
    """Warns if interval occured by direct motion
    intervals must be qualified or not """
    for (na,n1), (nb, n2) in util.pairwise(tools.iter_melodies(s1,s2,all=True)):
        nap, nbp, n1p, n2p = [util.to_pitch(x) for x in (na, nb, n1, n2)]

        answer = nbp.isQualifiedInterval(*intervals).With(n2p) if not isinstance(intervals[0],int) else nbp.isInterval(*intervals,True).With(n2p)

        if answer and (motion.MotionType.motion(na,n1,nb,n2) == motion.MotionType.direct):
            error.warn(f"It is forbidden to go to direct {intervals}",na,nb,n1,n2,f"in {s1.title} and {s2.title}")
def rule_7(cp, cf):
    """Lorsqu'une broderie forme dissonance attractive de quarte augmentée ou de quinte diminuée, et qu'elle retourne sur la note consonante qui l'a précédée, elle est à éviter. Cette règle est applicable aussi à la dissonance de quarte et, avec plus de tolérance, à celles de seconde et de septième"""
    for (n1, na), (n2, nb) in util.pairwise(tools.iter_melodies(cp,cf,alone=False)):
        if not cp.isCambiata(n2):
            continue
        n1p, nap, n2p, nbp = [util.to_pitch(x) for x in (n1,na, n2, nb)]
        if n2p.isQualifiedInterval((5,"diminished"),(4,"augmented"),(4,"perfect")).With(nbp) and n1p.isConsonantWith(nap):
            warn(f"It is forbidden to have a cambiata having an interval of 5th diminished, 4th perfect or augmented that return to a consonant pitch", cp.title,n2)

        elif n2p.isInterval(2,7,True).With(nbp) and n1p.isConsonantWith(nap):
            warn(f"If the cambiata forms a seventh or a second with the other voice, it can return to the consonant note by tolerance only",cp.title,n2)
Beispiel #14
0
def intersection(s1, s2):
    """True if the two melodies intersect.
    Warn at every point intersection is found"""
    state = None
    for notes in tools.iter_melodies(s1,s2):
        pnotes = [util.to_pitch(n) for n in notes]
        if len(notes) == 1 or pnotes[0] == pnotes[1]:
            continue
        new_state = pnotes[0] < pnotes[1]
        if state is not None and new_state is not state:
            error.warn(f"Melodies intersect.",*notes)
        state = new_state
Beispiel #15
0
    def positionOf(self, note):
        """Return the position in the scale of note"""
        note = util.to_pitch(note)

        if note not in self:
            raise ValueError(f"Note {note} not in scale {self}")

        if self.mode == Mode.m_full:
            for sub_scale in self.__minor__scales.values():
                if note in sub_scale:
                    return sub_scale.positionOf(note)
        return self.notes.index(note)
Beispiel #16
0
def rule_21(s: stave.Stave, ratio=5 / 20):
    """Employer de préférence les consonances imparfaites"""
    bars = []
    for bar in s.barIter():
        ft, sd = [util.to_pitch(n) for n in bar]
        if ft.isPerfectlyConsonantWith(sd):
            bars.append(bar)

    if len(bars) / len(s) > ratio:
        error.warn(
            "The number of perfect consonances is possibly higher than requested",
            *bars)
Beispiel #17
0
def rule_2(voice):
    """Les tritons en trois notes sont interdits si le mouvement ne change pas de sens.
    Ces intervalles interdits peuvent être bons ou tolérés lors d'une modulation.
    """
    for notes in util.nwise(voice,3):
        np1,np2,np3 = util.to_pitch(notes)
        if np1.semitoneWith(np3) == 6:
            if tools.is_same_direction(np1,np2,np3):
                if melodic.modulation_at(notes[1],voice) or melodic.modulation_at(notes[-1],voice):
                    msg = "Tritone in 3 notes is possible when a modulation occurs."
                else:
                    msg = "Tritone in 3 notes are forbidden, except when the middle note is lower or greater than the 2 other notes. It can also be tolerated in a seventh dominant chord. Check that."
                warn(msg,notes,voice.title)
Beispiel #18
0
def rule_9(s1,s2):
    """
    1. Le soprano et le contralto ne doivent pas être éloignés de la voix inférieure d’un intervalle supérieur à l’octave.
    2. Le ténor et la basse ne doivent pas être éloignés d’un intervalle supérieur à la douzième.
    3. Il est possible d’excéder l’intervalle maximal dans le cas d’un saut d’octave, de préférence suivi d’un mouvement mélodique inverse.
    """
    titles = (s1.title,s2.title)
    max = 12 if titles == ("Tenor","Bass") else 8

    for n1, n2 in tools.iter_melodies(s1,s2):
        np1,np2 = util.to_pitch((n1,n2))
        if np1.intervalWith(np2) > max:
            warn(f"Max interval between {s1.title} and {s2.title} is {max}, except if there's a melodic octave, or other thing of that kind followed by a melodic contrary movement. Is that the case?",n1,n2,*titles)
Beispiel #19
0
def rule_10(data):
    """Éviter l'accord diminué à l'état direct"""
    scale_ = data[0].scale
    previous = None
    for j, notes in enumerate(zip(*data)):
        c = chord.Chord.findChord(notes, scale_, True)
        if isinstance(c, chord.Chord) and c.isQuality("diminished"):
            for i, n in enumerate(notes):
                np = util.to_pitch(n)
                if c.findPosition(np) == 5:
                    err = error.CompositionError(
                        f"Diminished chord must be prepared and resolved", n)
                    #check previous pos
                    if np != previous[i]:
                        raise err
                    # check next pos
                    if (j + 1) == len(data[0]):
                        raise err
                    following_note = data[i][j + 1]
                    following_pitch = util.to_pitch(following_note)
                    if np.semitone - following_pitch.semitone != 1:
                        raise err
Beispiel #20
0
def calculate_motion_types(s1, s2):
    """Finds the number of motion type: direct, contrary
    and oblique"""
    movs = { motion.MotionType.direct : 0,
            motion.MotionType.contrary : 0,
            motion.MotionType.oblique : 0,
            motion.MotionType.no:0}

    for (na,n1), (nb, n2) in util.pairwise(tools.iter_melodies(s1,s2,alone=False)):
        nap, nbp, n1p, n2p = [util.to_pitch(x) for x in (na, nb, n1, n2)]
        movs[motion.MotionType.motion(nap,n1p,nbp,n2p)] += 1

    return movs
Beispiel #21
0
 def __contains__(self, arg):
     """True if note is in notes available for
     this scale.
     If arg in an iterable, True only if every note
     is in the scale"""
     # is iterable ?
     try:
         for note in arg:
             if util.to_pitch(note) not in self.notes:
                 return False
         return True
     except TypeError:
         # it should be a simple note
         return arg in self.notes
Beispiel #22
0
    def __contains__(self, arg) -> bool:  #TEST
        """True if arg is
        above min and beyond max
        condoned are not used here
        arg may be an iterable
        """

        try:
            for n in arg:
                if n not in self:
                    return False
                return True
        except TypeError:
            n = util.to_pitch(arg)
            return self.min.value.semitone <= n.value.semitone <= self.max.value.semitone
Beispiel #23
0
def rule_22(cp: stave.Stave, cf: stave.Stave):
    """À l'avant dernière mesure, on emploiera la sixte majeure lorsque le chant donné sera à la basse et la tierce mineure suivie de l'octave ou de l'unisson lorsqu'il sera à la partie supérieure"""
    # is the cantus firmus above or beyond?
    cp_above = None
    for cpn, cfn in zip(cp.barIter(), cf.barIter()):
        cpn, cfn = [util.to_pitch(n[0]) for n in (cpn, cfn)]
        if cpn != cfn:
            cp_above = cpn.value.step > cfn.value.step
            break
    assert cp_above is not None

    # check the before last one bar
    cpn = util.to_pitch(cp.getBar(cp.barNumber - 2)[0])
    cfn = util.to_pitch(cf.getBar(cf.barNumber - 2)[0])
    cp = cp.copy()
    cp.extend(cf)
    before_last_bar = cp.getBar(cp.barNumber - 2)

    if cp_above and not cfn.isQualifiedInterval((6, "major")):
        raise error.CompositionError(
            "The before last interval must be a 6th major", before_last_bar)
    elif not cp_above and not cfn.isQualifiedInterval((3, "minor")):
        raise error.CompositionError(
            "The before last interval must be a 3rd minor", before_last_bar)
Beispiel #24
0
def rule_3(voice):
    """Les neuvièmes et septièmes en trois notes sont interdits, à moins que l’un des intervalles intermédiaires soit une octave.
    Ces intervalles interdits peuvent être bons ou tolérés lors d'une modulation.
    """
    for notes in util.nwise(voice,3):
        np1, np2, np3 = util.to_pitch(notes)
        if np1.isInterval(7,9).With(np3):
            if not np1.isQualifiedInterval(
                    (2,'minor'),
                    (2,'major'),
                    (8,'perfect')).With(np2):
                if melodic.modulation_at(notes[1],voice) or melodic.modulation_at(notes[-1],voice):
                    msg = "7th or 9th in 3 notes are possible in a modulation"
                else:
                    msg = 'Between 3 notes, it is not allowed to use 7th or 9nth, except where an octave leap occured between one of these 3 notes'

                warn(msg,*notes,voice.title)
Beispiel #25
0
def no_consecutive(interval: int, s1: stave.Stave, s2: stave.Stave) -> bool:
    """Raise error if two interval in a row
    between s1 and s2"""
    nb = 0
    for n1, n2 in zip(s1, s2):
        p1, p2 = [util.to_pitch(n) for n in (n1, n2)]
        if p1.isInterval(interval, True).With(p2):
            nb += 1
        else:
            nb = 0

        if nb >= 2:
            raise error.CompositionError(
                f"It is forbidden to have two {interval} in a row", n1, n2,
                s1.findBarOf(n1))

    return True
Beispiel #26
0
def no_more_than(s: stave.Stave, x: int) -> bool:
    """s is a pure melody. 
    Warn if one note is used more
    than x times in a row
    """
    nb = 0
    previous = None
    for i, n in enumerate(s.notes):
        pn = util.to_pitch(n)
        if i != 0 and pn == previous:
            nb += 1
        else:
            nb = 0
        if nb >= x:
            error.warn(
                f"It is forbidden to use the same note more than {x} times if a row",
                n, s.findBarOf(n))
        previous = pn
Beispiel #27
0
def rule_6(s):
    """Éviter les intervalles mélodiques supérieurs à la Sixte Majeure (octave exceptée) ainsi que les intervalles augmentés ou diminués"""
    previous = None
    for n in s.notes:
        p = util.to_pitch(n)
        if previous is not None:
            interval = previous.intervalWith(p)
            if interval > 6 and interval != 8:
                raise error.CompositionError(
                    f"It is forbidden to use melodic interals over the 6th which is not a 8ve",
                    previous, n)
            if p.qualifiedIntervalWith(previous).quality in ("diminished",
                                                             "augmented"):
                raise error.CompositionError(
                    f"It is forbidden to use diminished or augmented intervals",
                    previous, n)

        previous = p
Beispiel #28
0
def rule_15(s1,s2):
    """L'unisson est interdit
    Dans les accords parfaits, l'unisson peut être toléré:
        - sur les temps faibles,
        - en cas de saut d'octave
        - si la basse monte haut
        - si le soprano descend bas
        - mais l'unisson ténor-alto est très maladroit.
    """
    for n1, n2 in tools.iter_melodies(s1,s2):
        p1,p2 = [util.to_pitch(n) for n in (n1,n2)]
        if p1 == p2:
            warn(f"""Unisons are forbidden.
            There are tolerances in perfect chords progression at root position if:
            - the unison is in a downbeat
            - there is an octave leap
            - if the bass is really high, or the soprano really low
            - note that the unison alto-tenor is really clumsy.
            """,n1,n2,s1.title,s2.title)
Beispiel #29
0
def rule_5(data):
    """N'arriver sur une quinte ou une octave par mouvement direct entre les parties extrêmes que lorsque le soprano procède par degrés conjoints"""
    # get the staves
    soprano = util.list_finder(data, "Soprano", fun=lambda s: s.title)
    bass = util.list_finder(data, "Bass", fun=lambda s: s.title)

    # check
    previous_soprano = previous_interval = None

    for n1, n2 in zip(soprano, bass):
        p1, p2 = [util.to_pitch(n) for n in (n1, n2)]
        if previous_interval is not None and MT.motion(*previous_interval, p1,
                                                       p2) == MT.direct:
            if p1.isInterval(5, 8, True).With(p2) and not p1.isInterval(
                    2, 1, True).With(previous_soprano):
                raise error.CompositionError(
                    f"It is forbidden to go to a 5th or an 8ve by direct motion with disjunct motion at soprano.",
                    n1, n2)
        previous_soprano = p1
        previous_interval = [p1, p2]
Beispiel #30
0
    def __fun(n1, n2):
        n1p, n2p = [util.to_pitch(x) for x in (n1,n2)]
        def __is_VI_VII_minor():
            if allow_in_minor is not True:
                return False
            if s1.scale.mode == scale.Mode.m_rising:
                _scale = s1.scale.mode
            elif s1.scale.mode == scale.Mode.m_full:
                _scale = s1.scale.minor_scales[scale.Mode.m_rising]
            elif s1.scale.mode == scale.Mode.M:
                _scale = s1.scale.mode.relative(scale.Mode.m_rising)
            else:
                return False 
            try:
                return _scale.isDegree(n1p,6) or _scale.isDegree(n1p,7) or _scale.isDegree(n2p,6) or _scale.isDegree(n2p,7)
            except ValueError:
                return False

        if n1p.isTritoneWith(n2p) and not __is_VI_VII_minor():
            error.warn(msg,n1,n2)