Esempio n. 1
0
def rule_3(cf: stave.Stave, base_cf: stave.Stave):
    """
    3 - Le chant donné (ou plain-chant) peut être transposé toutes les fois qu'il ne dépassera pas l'étendue ordinaire, au grave ou à l'aigu, de la voix pour laquelle on le transposera.
    """
    # check the tessitura
    T = tessitura

    valid_tessitura = False
    for tess in (T.soprano, T.tenor, T.bass, T.alto):
        if cf in tess:
            valid_tessitura = True
            break

    if not valid_tessitura:
        raise error.CompositionError(
            "The cantus firmus transposed is too high or too low", cf)

    # check the transposition is correct
    def _semitone_sum(n, n2):
        return n.pitch.value.semitone - n2.pitch.value.semitone

    for i, (b1, b2) in enumerate(zip(cf, base_cf)):
        if i + 1 == len(cf):
            break
        if _semitone_sum(b1, cf[i + 1]) != _semitone_sum(b2, base_cf[i + 1]):
            raise error.CompositionError(
                "Cantus firmus transposed doesn't match with original cantus firmus",
                b1, b2)
Esempio n. 2
0
def rule_12(s: stave.Stave):
    """
    no more than two leaps in a row; no consecutive leaps in the same direction
    """
    previous = None
    leap_nb = 0
    leaps = []

    for n in s.notes:
        p = n.pitch
        if previous is not None:
            if previous.intervalWith(p) > 2:
                leap_nb += 1
                leaps.append((previous,p))
                if len(leaps) == 2 and tools.is_same_direction(*leaps[0],p):
                    raise error.CompositionError("No consecutive leaps in the same direction",leaps)
            else:
                leap_nb = 0
                leaps = []

            if leap_nb > 2:
                raise error.CompositionError("No more than two leaps in a row",leaps)


        previous = p
Esempio n. 3
0
def rule_4(s: stave.Stave):
    """On doit commencer par une consonance parfaite (unisson, quinte ou douzième, octave ou quinzième) et finir par l'octave ou l'unisson
    """
    def nb_error(poss, posi, notes):
        if len(notes) != 2:
            raise error.CompositionError(
                f"Two notes are expected at the {poss} of the track",
                s.getBar(posi))

    # start
    notes = s.atFirstPos(0)
    nb_error("start", 0, notes)

    if not notes[0].pitch.isPerfectlyConsonantWith(notes[1].pitch):
        raise error.CompositionError(
            "The first two notes are not fully consonant.", s.getBar(0))

    # end
    notes = s.atFirstPos(s.lastFirstPos)
    nb_error("end", s.getBar(s.barNumber - 1), notes)

    if not notes[0].pitch.isInterval(1, 8, True).With(notes[1].pitch):
        raise error.CompositionError(
            "The last interval must be an unison or an octave.",
            s.getBar(s.barNumber - 1))
Esempio n. 4
0
def rule_7(s: stave.Stave):
    """Les intervalles formant consonance parfaites ou imparfaites avec le chant donné sont seuls employés"""
    for bar in s.barIter():
        if len(bar) != 2:
            raise error.CompositionError("Two notes expected", bar)
        ft, sd = bar[0].pitch, bar[1].pitch
        if not ft.isConsonantWith(sd):
            raise error.CompositionError("The notes must be consonant", bar)
Esempio n. 5
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)
Esempio n. 6
0
def rule_4(s: stave.Stave):
    """
    approach final tonic by step (usually re - do, sometimes ti - do
    """
    penultimate = NS(s.scale,s.notes[-2])
    if not (penultimate.isSupertonic or penultimate.isLeading):
            raise error.CompositionError("The penultimate note should be a re or a ti",penultimate)

    last = NS(s.scale, s.notes[-1])
    if abs(penultimate.distanceWith(last)) != 1:
        raise error.CompositionError("The penultimate note should be at one step of the lst one",penultimate, last)
Esempio n. 7
0
    def check_degree(notes, degree):
        c = chord.Chord.findChord(notes, scale_)
        try:
            c = [_c for _c in c if _c.degree == degree][0]
        except IndexError:
            raise error.CompositionError(
                f"{degree}th degree was expected after 2nd inversion chord",
                notes)

        if not c.isInversion(notes, 0):
            raise error.CompositionError(
                f"After 2nd inversion root, the {degree}th must be at root position",
                notes)
Esempio n. 8
0
def rule_1(data):
    """Prendre une phrase en valeurs égales non modulante
    toujours note contre note
    Only breve and semibreve are accepted."""
    scale_ = data[0].scale
    for n in itertools.chain.from_iterable(data):
        if n.duration not in (note.Duration.MINIM.value,
                              note.Duration.SEMIBREVE.value):
            from IPython import embed
            embed()
            raise error.CompositionError(
                f"The note should be a breve or a semibreve", n)
        if n.pitch not in scale_:
            raise error.CompositionError(f"Modulation not allowed", n)
Esempio n. 9
0
def rule_1(s: stave.Stave):
    """
    s is the main stave
    1 - Le contrepoint se compose d'une partie en rondes combinée avec le Chant donné (en rondes)

    """
    for nb, bar in enumerate(s.barIter()):

        # check that each bar contains only two notes
        if len(bar.elts) != 2:
            raise error.CompositionError("Number of breve should be two", bar)

        # check that each note is a breve
        if bar.elts[0].duration != bar.elts[1].duration and bar.elts[
                0].duration != note.Duration.SEMIBREVE:
            raise error.CompositionError("The notes are not breves", bar)
Esempio n. 10
0
def rule_5(s: stave.Stave):
    """L'unisson est défendu dans le courant du contrepoint"""

    for elt in s.barIter():

        #if elt.pos == 0 or elt.pos == s.lastFirstPos:
        if elt.pos in (0, s.barNumber - 1):
            # first and last interval can be unisons.
            continue

        if len(elt) != 2:
            raise error.CompositionError("Two notes are expected.", elt)

        if elt[0].pitch == elt[1].pitch:
            raise error.CompositionError(
                "Unison is forbidden inside the couterpoint", elt)
Esempio n. 11
0
def rule_2(data):
    """N'user que d'accords consonants à 3 sons"""
    scale_ = data[0].scale
    for x, y, z in zip(*data):
        if not chord.Chord.findChord([x, y, z], scale_):
            raise error.CompositionError(f"Only consonant chords can be used",
                                         x, y, z)
Esempio n. 12
0
def rule_9(s: stave.Stave):
    """
    mostly stepwise motion, but with some leaps (mostly small leaps)
    """
    previous = None
    steps = {}

    for n in s.notes:
        p = n.pitch
        if previous is not None:
            interval = p.intervalWith(previous)
            val = steps.get(interval,0)
            val += 1
            steps[interval] = val
        previous = p

    stepwise = steps.get(1,0) + steps.get(2,0)
    if  stepwise < (len(s) / 2):
        raise error.CompositionError(f"The cantus firmus must be mostly stepwise motion. Number of steps of 2 or 1: {stepwise}")

    if max(steps.keys()) > 4:
        error.warn("The cantus firmus must be with mostly small leaps.",steps)

    if max(steps.keys()) <= 2:
        error.warn("The cantus firmus should contain at least some (small) leaps")
Esempio n. 13
0
def rule_8(data):
    """N'user de la quarte et sixte que pour les cadences"""
    scale_ = data[0].scale

    def check_degree(notes, degree):
        c = chord.Chord.findChord(notes, scale_)
        try:
            c = [_c for _c in c if _c.degree == degree][0]
        except IndexError:
            raise error.CompositionError(
                f"{degree}th degree was expected after 2nd inversion chord",
                notes)

        if not c.isInversion(notes, 0):
            raise error.CompositionError(
                f"After 2nd inversion root, the {degree}th must be at root position",
                notes)

    for i, notes in enumerate(zip(*data)):
        c = chord.Chord.findChord(notes, scale_, best=True)
        if isinstance(c, chord.Chord) and c.isInversion(notes, 2):
            if c.degree != 1:
                raise error.CompositionError(
                    f"The 2nd inversion chord must be of the 1st degree only",
                    notes)

            # check the Vth degree
            check_degree(list(zip(*data))[i + 1], 5)
            # check the 1st degree
            check_degree(list(zip(*data))[i + 2], 1)
Esempio n. 14
0
def rule_13(s: stave.Stave):
    """Lorsque la dominante du ton se trouve à la partie inférieure - et qu'elle a été précédée de l'accord du premier degré - il faut éviter de la combiner avec une sixte car cela donnerait le sentiment d'un accord de quarte et sixte, ce qui est défendu. Si elle permet de sous-entendre un autre accord, on peut l'employer"""
    """There is maybe a problem here: if the main scale is major, every switch to the relative minor will install it as the main scale and the program will never consider that the swith is finished since not pitch of the major is outside of the minor. Maybe we should consider that we return to the major scale each measure.
    WARNING"""
    current_scale = s.scale
    previous_chord = None

    for bar in s.barIter():
        # change scale if necessary
        if bar[0].pitch not in current_scale or bar[
                1].pitch not in current_scale:
            current_scale = current_scale.relative(scale.Mode.m_full)

        notes = [scalenote.NoteScale(current_scale, n) for n in bar]
        n1, n2 = notes
        # generate chord
        first = chord.Chord(1, current_scale)
        # is it the first degree chord?
        if notes in first:
            previous_chord = notes
            continue
        if previous_chord is not None:
            # is it the dominant in the bass and an interval of sixth?
            low, high = (n1, n2) if n1 < n2 else (n2, n1)
            if low.isDominant and low.note.isInterval(6).With(high.note):
                raise error.CompositionError(
                    "It is forbidden to use a tonic chord followed by a dominant at the bass and a sixth with the dominant",
                    previous_chord, bar)

            # clean-up
            previous_chord = None
Esempio n. 15
0
def rule_2(s: stave.Stave): # TEST
    """
    arhythmic (all whole notes; no long or short notes)
    """
    for n in s:
        if n.duration != note.Duration.SEMIBREVE.value:
            raise error.CompositionError("Whole notes only are permitted",n)
Esempio n. 16
0
def rule_11(cp: stave.Stave, cd: stave.Stave):
    """Les croisements sont tolérés, employés avec une grande réserve"""
    cp_high_part = None

    for cp_n, cd_n in zip(cp.barIter(), cd.barIter()):

        if not (len(cp_n) == len(cd_n) == 1):
            raise error.CompositionError("Two notes are expected", cp_n, cd_n)

        cdn, cpn = cd_n[0], cp_n[0]

        if cp_high_part is None:
            cp_high_part = cpn.pitch.value.semitone > cdn.pitch.value.semitone if cpn.pitch.value.semitone != cdn.pitch.value.semitone else None
        else:
            if cp_high_part is True:
                if cdn.pitch.value.semitone > cpn.pitch.value.semitone:
                    error.warn(
                        "The melodies intersect: cantus firmus is now over the counterpoint",
                        cp_n, cd_n)
                    cp_high_part = False
            elif cp_high_part is False:
                if cpn.pitch.value.semitone > cdn.pitch.value.semitone:
                    error.warn(
                        "The melodies intersect: counterpoint is now over cantus firmus",
                        cp_n, cd_n)
                    cp_high_part = True

            else:
                assert cp_high_part is not None
Esempio n. 17
0
def _Rule_10(s: stave.Stave):
    """Éviter les marches d'harmonie"""
    for start in range(s.barNumber):
        for end in range(start, s.barNumber):

            motif = []
            for i in range(start, end + 1):
                for n in s.getBar(i):
                    motif.append(n)

            motif_bar_number = end - start + 1
            try:
                following = []
                for i in range(end + 1, motif_bar_number + end + 1):
                    for n in s.getBar(i):
                        following.append(n)
            except IndexError:
                break
            try:
                if tools.matchSequence(motif, following, s.scale):
                    msg = f"Sequence in {s.title}. It should be avoided"
                    if len(motif) == 1:
                        continue
                    if len(motif) <= 2:
                        error.warn(msg, motif, following)
                    else:
                        raise error.CompositionError(msg, motif, following)
            except ValueError:
                # It should be that the relative one is encountered, so no sequence here
                continue
Esempio n. 18
0
def rule_13(s: stave.Stave):
    """
    the leading tone progresses to the tonic
    """
    for i,n in enumerate(s.notes):
        ns = NS(s.scale,n)
        if ns.isLeading and ((i == len(s) -1) or (not NS(s.scale,s.notes[i+1]).isTonic)):
            raise error.CompositionError("The leading should progress to the tonic",ns)
Esempio n. 19
0
def rule_23(s: stave.Stave):
    """La première et la dernière mesure sont obligatoirement harmonisées par l'accord de tonique à l'état fondamental"""
    c = chord.Chord(1, s.scale)
    for bar in (s.getBar(0), s.getBar(-1)):
        if not c.isInversion([*bar], 0):
            raise error.CompositionError(
                "First and last bar must be at the root position of the chord of the first degree",
                bar)
Esempio n. 20
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
Esempio n. 21
0
def rule_6(s: stave.Stave):
    """
    range (interval between lowest and highest notes) of no more than a tenth, usually less than an octave
    """
    min, max = tools.min_max(s)
    if min.intervalWith(max) > 10:
        raise error.CompositionError("The range should be no more than a tenth",min,max)
    if min.intervalWith(max) > 8:
        error.warn("It is a good thing to keep the range in an octave",min,max)
Esempio n. 22
0
def rule_11(s: stave.Stave):
    """
    any large leaps (fourth or larger) are followed by step in opposite direction
    """
    previous = None
    for i,n in enumerate(s):
        p = n.pitch
        if previous is not None and previous.intervalWith(p) > 4:
            if i + 1 == len(s) or tools.is_same_direction(previous,p,s.notes[i+1].pitch):
                raise error.CompositionError("A large leap must be followed by a step in another direction",previous,p,s.notes[i+1].pitch)
Esempio n. 23
0
def rule_6(s: stave.Stave):
    """Le mouvement chromatique est défendu"""
    previous = None
    for elt in s.barIter():
        note = elt[0]
        if previous is not None and previous[
                0].pitch.isChromaticInflectionWith(note.pitch):
            raise error.CompositionError("Chromatic inflection is forbidden",
                                         previous, elt)
        previous = elt
Esempio n. 24
0
def rule_5(s: stave.Stave):
    """
    all note-to-note progressions are melodic consonances
    """
    previous = None
    for n in s.notes:
        n = n.pitch
        if previous is not None and (not previous.isMelodicConsonantWith(n)):
            raise error.CompositionError("Melodic consonances only",previous,n)
        previous = n
Esempio n. 25
0
def rule_12(s: stave.Stave):
    """On ne doit moduler qu'aux tons relatifs"""
    sc = scale.Scale(s.keynote, s.mode)
    relative = sc.relative(scale.Mode.m_full)
    for bar in s.barIter():
        note = bar[0]
        if note.pitch not in sc:
            if note.pitch not in relative:
                raise error.CompositionError(
                    "It is forbidden to modulate outside the relative key",
                    bar)
Esempio n. 26
0
def rule_17(s: stave.Stave):
    """Les mouvements de quarte augmentée (triton), quinte dimininuée, de septième majeure et mineure sont défendus"""
    previous = None
    for bar in s.barIter():
        if previous is not None and bar[0].pitch.isQualifiedInterval(
            (5, "diminished"), (4, "augmented"), (7, "minor"),
            (7, "major")).With(previous[0].pitch):
            raise error.CompositionError(
                "Melodic motion can't be a 4th augmented, 5th diminished, 7th minor or major",
                previous, bar)
        previous = bar
Esempio n. 27
0
def rule_14(s: stave.Stave):
    """
    in minor, the leading tone only appears in the penultimate bar;
    the raised submediant is only used when progressing to that leading tone
    """
    # minor?
    if s.scale.mode not in (scale.Mode.m,scale.Mode.m_full,scale.Mode.m_rising,scale.Mode.m_full):
        return

    # minor: the leading at the end
    for i,n in enumerate(s.notes):
        ns = NS(s.scale,n)
        if ns.isLeading and i != len(s) -2:
            raise error.CompositionError("In minor, the leading tone must be at the penultimate bar only",n)

    # minor: the raised submediant used when progressing to leading
    for i,n in enumerate(s.notes):
        ns = NS(s.scale,n)
        if ns.isRaisedSubmediant and ((i + 1 == len(s)) or not NS(s.notes[i+1]).isLeading):
            raise error.CompositionError("Raised submediant should be used before leadingtone only",ns)
Esempio n. 28
0
def rule_15(s: stave.Stave):
    """Le contrepoint ne doit pas parcourir une étendue plus grande que la dixième et par exception la onzième."""
    res = tools.min_max(s)
    min = res.min
    max = res.max

    if max.intervalWith(min) == 11:
        error.warn("The counterpoint can exceed the 11th by tolerance only.",
                   *s.barIter())
    if max.intervalWith(min) > 11:
        raise error.CompositionError(
            "It is strictly forbidden to exceed the 11th.", *s.barIter())
Esempio n. 29
0
def rule_7(s: stave.Stave): # TEST
    """
    a single climax (high point) that appears only once in the melody
    """
    max = tools.min_max(s).max
    nb = 0
    for n in s:
        if n.pitch == max:
            nb += 1

    if nb > 1:
        raise error.CompositionError(f"There should be only one climax. Here, {max} is found {nb} time{s if nb > 1 else ''}")
Esempio n. 30
0
def rule_8(s: stave.Stave):
    """On ne doit pas entendre plus de trois tierces ou trois sixtes de suite"""
    _6 = _3 = 0

    for bar in s.barIter():
        if len(bar) != 2:
            raise error.CompositionError("Two notes expected", bar)
        f, s = bar[0].pitch, bar[1].pitch
        int_ = f.intervalWith(s, True)
        if int_ == 3:
            _3 += 1
            _6 = 0
        elif int_ == 6:
            _6 += 1
            _3 = 0
        else:
            _6 = _3 = 0

        if 4 in (_6, _3):
            raise error.CompositionError(
                "It is forbidden to use more than three sixths or thirds in a row",
                bar)