def transposeCellDiatonically(oldCell,
                              newChord,
                              newNextChord,
                              direction=1,
                              newDestinationDistance=None):

    notes = []

    # set up
    if newDestinationDistance is not None and newDestinationDistance != 88:
        if oldCell.destination < newDestinationDistance:
            direction = 1
        else:
            direction = -1
    # don't make this an else to the above if because we need to set a correct direction for transposeDistance
    if newDestinationDistance is None:
        # set a new destination distance based on the old cell's destination
        newDestinationDistance = td.transposeDistance(oldCell.destination,
                                                      oldCell.chord, newChord,
                                                      direction)
        #print("newDestinationDistance", newDestinationDistance)
        #newDestinationTonal = dtt.distanceToTonal(newDestinationDistance)

    # first get distance between old chord and new chord
    distanceBetweenTonal = newChord.root - oldCell.chord.root
    if direction == 1 and distanceBetweenTonal < 0:
        distanceBetweenTonal += 7
    elif direction == -1 and distanceBetweenTonal > 0:
        distanceBetweenTonal -= 7
    #print('distanceBetweenTonal', distanceBetweenTonal)

    for oldNote in oldCell.notes:

        # adjust pitch
        newVal = (ptt.pitchToTonal(oldNote.pitch) + distanceBetweenTonal -
                  1) % 7 + 1  # TODO: doublecheck the -1 and +1
        newPitch = ttp.tonalToPitch(newVal)
        # adjust distance
        # TODO: think diatonically, consider minor keys, etc - use pitchToDistance()
        newDistance = td.transposeDistance(oldNote.distance, oldCell.chord,
                                           newChord, direction)

        #print('transposing:', oldNote.pitch, ptt.pitchToTonal(oldNote.pitch), oldNote.distance, '--->', newPitch, newVal, newDistance)

        # add everything else
        notes.append(
            mo.Note(newPitch, newDistance, oldNote.rhythm, oldNote.tied,
                    newChord.root, oldNote.tonality, oldNote.seventh,
                    oldNote.inversion, oldNote.secondary,
                    oldNote.secondaryRoot, oldNote.key, oldNote.major,
                    oldNote.timesig))

    newCell = mo.Cell(newChord, newNextChord, oldCell.beats, notes,
                      newDestinationDistance, oldCell.voice)

    return newCell
Example #2
0
def getNextNote(music, chordArray, finalMTX, measure, measures, voice, maxVoices, species = 1):

    if maxVoices == 3:
        bass = 0
        tenor = 1
        soprano = 2
    else:
        bass = 0
        tenor = 1
        soprano = 2
        alto = 3  # keeping as 3 instead of swapping with soprano - easier to add into current code

    # previous note
    prevNote = int(ptt.pitchToTonal(finalMTX[measure - 1][voice][-1].notes[-1].pitch))

    # TODO: fix [measure][0] for species with more than 1 chord per measure
    currentRoot = chordArray[measure][0].root        # current chord root
    nextChord = 1                                    # next chord root, currently unused
    if measure < measures - 1:                       # avoid index out of range error on last measure
        nextChord = chordArray[measure + 1][0].root
    seventh = chordArray[measure][0].seventh         # seventh chord
    tonality = chordArray[measure][0].tonality       # tonal root
    inversion = chordArray[measure][0].inversion     # inversion

    keepGoing = True
    direction = -2

    # TODO: need to add nextChord considerations!!
    #   especially for 7th chords because of 3rd inversion linear descents,
    #   but also for picking 2nd inversions, which should be rare if it isn't linear motion

    # generate random number - PocketBach's soul...
    num1 = random.random()

    # track chord tones
    chordVec = chordArray[measure][0].getPitches()


    ################################################################
    # bass
    ################################################################
    # RULES:
    # 7th chords with only 3 voices cannot be in 2nd inversion (5th in bass)
    # bass line has high chance to pick root, even if there's a jump
    # NOTE: use actual data to derive percentages based on each composer's preferences
    # TO DO: need to consider repeated pitches
    if voice == 0:
        #print(chordVec)

        # return 5th for I-6/4 chords in bass
        if currentRoot == 1 and inversion == 2:
            nextNote = currentRoot + 4
            if nextNote > 7:
                nextNote -= 7
            keepGoing = False
            #return nextNote

        # if on the 2nd to last measure force a V chord
        elif measure == measures - 2 and keepGoing:
            nextNote = 5
            keepGoing = False
            #return 5

        # 7th chords with only 3 voices cannot be in 2nd inversion (5th in bass)
        elif seventh and maxVoices == 3 and keepGoing:
            if inversion == 0:
                del chordVec[2]
            elif inversion == 1:
                del chordVec[1]
            elif inversion == 2:
                del chordVec[0]
            else:
                del chordVec[3]

        if keepGoing:
            if num1 < 0.2:    # 20% chance to repeat bass note (technically less than 20%,
                                # because we check to see if prevNote is a chord tone)
                #print('staying the same')
                if ttp.tonalToPitch(prevNote, music.key) in chordVec:  # repeat prevNote if you can
                    nextNote = prevNote
                else:  # otherwise just go with root
                    nextNote = currentRoot
            elif num1 < 0.4:   # 20% chance to move up linearly (technically less than 20%,
                                # because we check to see if prevNote + 1 is a chord tone)
                #print('moving up')
                if prevNote == 7:  # if prevNote is 7, can't use prevNote + 1
                    if ttp.tonalToPitch(1, music.key) in chordVec:
                        nextNote = 1
                    else:  # otherwise just go with root
                        nextNote = currentRoot
                else:  # if prevNote isn't 7, can use prevNote + 1
                    if ttp.tonalToPitch(prevNote + 1, music.key) in chordVec:
                        nextNote = prevNote + 1
                    else:  # otherwise just go with root
                        nextNote = currentRoot
            elif num1 < 0.6:   # 20% chance to move down linearly (technically less than 20%,
                    # because we check to see if prevNote - 1 is a chord tone)
                #print('moving down')
                if prevNote == 1:  # if prevNote is 1, can't use prevNote - 1
                    if ttp.tonalToPitch(7, music.key) in chordVec:
                        nextNote = 7
                    else:  # otherwise just go with root
                        nextNote = currentRoot
                else:  # if prevNote isn't 1, can use prevNote - 1
                    if ttp.tonalToPitch(prevNote - 1, music.key) in chordVec:
                        nextNote = prevNote - 1
                    else:  # otherwise just go with root
                        nextNote = currentRoot
            else:  # 40% chance to simply pick root
                nextNote = currentRoot

    ################################################################
    # soprano
    ################################################################
    # RULES:
    # can't be 3rd+3rd
    # can't be 5th+5th
    # 7th chords with 3 voices cannot have a 5th
    # 7th chords with 3 voices cannot be root+root
    # 7th chords with 3 voices cannot be 3rd+3rd
    # TO DO: no parallel 5ths or octaves
    # NOTE: use actual data to derive percentages based on each composer's preferences
    elif voice == 2:
        # enforce rules above by changing chordVec!!!
        # check for 7th chords first

        # first of all... if on the last measure, just return tonic
        if chordArray[measure][measures-1] == measures:
            nextNote = 1
            keepGoing = False

        elif keepGoing:
            if not seventh:  # if not a 7th chord
                # can't be 3rd+3rd
                if inversion == 1:  # if inversion = 1
                    del chordVec[0]
                # can't be 5th+5th
                elif inversion == 2:  # if inversion = 2
                    del chordVec[0]
            else:  # if a 7th chord
                # 7th chords with 3 voices cannot have a 5th
                if maxVoices == 3:
                    # 7th chords with 3 voices cannot be root+root
                    if inversion == 0:  # if inversion = 0
                        chordVec = [chordVec[1], chordVec[3]]
                    # 7th chords with 3 voices cannot be 3rd+3rd
                    elif inversion == 1:  # if inversion = 1
                        chordVec = [chordVec[2], chordVec[3]]
                    # elif inversion == 2:  # NOTE: this should never happen with only 3 voices...
                    else:  # inversion == 3:  # if inversion = 3
                        # can only be root or 3rd
                        chordVec = [chordVec[1], chordVec[2]]
                else:
                    # TO DO: 4-voice rules
                    pass

            # all percentages are multiplied by 2.5/7 = 0.36 because there's only about a 2.5/7 chance
            #   for the prevNote or prevNote +1/-1 to be a chord tone
            # TO DO: right now it checks for upward motion first, so percentages of upward/downward
            #   motion aren't equal... need to equalize
            if num1 < 0.83:         # 83% * 0.36 = 30% chance to move up or down
                if prevNote == 7:   # if prevNote is 7, can't use prevNote + 1
                    if ttp.tonalToPitch(1, music.key) in chordVec:
                        nextNote = 1
                        keepGoing = False
                        direction = 1
                else:  # if prevNote isn't 7, can use prevNote + 1
                    if ttp.tonalToPitch(prevNote + 1, music.key) in chordVec:
                        nextNote = prevNote + 1
                        keepGoing = False
                        direction = 1
                if prevNote == 1 and keepGoing:  # if prevNote is 1, can't use prevNote - 1
                    if ttp.tonalToPitch(7, music.key) in chordVec:
                        nextNote = 7
                        keepGoing = False
                        direction = -1
                elif keepGoing:  # if prevNote isn't 1, can use prevNote - 1
                    if ttp.tonalToPitch(prevNote - 1, music.key) in chordVec:
                        nextNote = prevNote - 1
                        keepGoing = False
                        direction = -1
            else:      # 100% * 0.36 = 36% * 0.7 (30% less chance to make it through first if) = 25% chance to repeat note
                if ttp.tonalToPitch(prevNote, music.key) in chordVec:
                    nextNote = prevNote
                    keepGoing = False
                    direction = 0

            # there's a high chance the note will fall through the if-elifs above,
            #   so don't put this line into "else:"
            if keepGoing:
                nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)
                keepGoing = False

    ################################################################
    # alto/tenor
    ################################################################
    # RULES:
    # if root+root, must be 3rd
    # if root+3rd, can be root or 5th
    # if root+5th, must be 3rd
    # if 3rd+root, can be root or 5th
    # can't be 3rd+3rd
    # if 3rd+5th, must be root
    # if 5th+root, must be 3rd
    # if 5th+3rd, must be root
    # can't be 5th+5th
    # can't be 7th+7th
    # 7th chords with 3 voices cannot have a 5th
    # 7th chords with 3 voices cannot be root+root
    # 7th chords with 3 voices cannot be 3rd+3rd
    # 7th chords with 3 voices with root+3rd, must be 7th
    # 7th chords with 3 voices with root+7th, must be 3rd
    # 7th chords with 3 voices with 3rd+root, must be 7th
    # 7th chords with 3 voices with 3rd+7th, must be 7th
    # 7th chords with 3 voices with 7th+root, must be 3rd
    # 7th chords with 3 voices with 7th+3rd, must be root
    # TO DO: no parallel 5ths or octaves
    # NOTE: use actual data to derive percentages based on each composer's preferences
    # TO DO: need to check for note chosen in voice 0 and 1...
    else:
        # enforce rules above by changing chordVec!!!
        # check for 7th chords first
        if not seventh:  # if not a 7th chord
            if inversion == 0:  # if inversion = 0
                # if root+root, must be 3rd
                if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[0]:
                    nextNote = ptt.pitchToTonal(chordVec[1], music.key)
                    keepGoing = False
                # if root+3rd, can be root or 5th
                elif finalMTX[measure][soprano][0].notes[0].pitch == chordVec[1]:
                    del chordVec[1]
                # if root+5th, must be 3rd
                else:
                    nextNote = ptt.pitchToTonal(chordVec[1], music.key)
                    keepGoing = False
            elif inversion == 1:  # if inversion = 1
                # if 3rd+root, can be root or 5th
                if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[2]:
                    del chordVec[0]
                # elif # can't be 3rd+3rd
                else:  # if 3rd+5th, must be root
                    nextNote = ptt.pitchToTonal(chordVec[2], music.key)
                    keepGoing = False
            elif inversion == 2:  # if inversion = 2
                # if 5th+root, must be 3rd
                if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[1]:
                    nextNote = ptt.pitchToTonal(chordVec[2], music.key)
                    keepGoing = False
                # elif # can't be 5th+5th
                else:  # if 5th+3rd, must be root
                    nextNote = ptt.pitchToTonal(chordVec[1], music.key)
                    keepGoing = False
        else:  # if a 7th chord
            # 7th chords with 3 voices cannot have a 5th
            if maxVoices == 3:
                if inversion == 0:  # if inversion = 0
                    # 7th chords with 3 voices cannot be root+root
                    # 7th chords with 3 voices with root+3rd, must be 7th
                    if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[1]:
                        nextNote = ptt.pitchToTonal(chordVec[3], music.key)
                        keepGoing = False
                    # elif # 7th chords with 3 voices cannot have a 5th
                    # 7th chords with 3 voices with root+7th, must be 3rd
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[1], music.key)
                        keepGoing = False

                elif inversion == 1:  # if inversion = 1
                    # 7th chords with 3 voices with 3rd+root, must be 7th
                    if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[3]:
                        nextNote = ptt.pitchToTonal(chordVec[2], music.key)
                        keepGoing = False
                    # 7th chords with 3 voices cannot be 3rd+3rd
                    # 7th chords with 3 voices cannot have a 5th
                    # 7th chords with 3 voices with 3rd+7th, must be root
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[3], music.key)
                        keepGoing = False
                # 7th chords with 3 voices cannot have a 5th, so no 2nd inversion
                else:  # if inversion = 3
                    # 7th chords with 3 voices with 7th+root, must be 3rd
                    if finalMTX[measure][soprano][0].notes[0].pitch == chordVec[1]:
                        nextNote = ptt.pitchToTonal(chordVec[2], music.key)
                        keepGoing = False
                    # 7th chords with 3 voices with 7th+3rd, must be root
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[1], music.key)
                        keepGoing = False
                    # 7th chords with 3 voices cannot have a 5th
                    # can't be 7th+7th

            # not relevant at the moment so skipping them...
            else:
                # TO DO: many possible combinations and rules for 4 voices below...
                pass

        if keepGoing:
            if num1 < 0.4:  # 40% chance to repeat note (technically less)
                            # because we check to see if prevNote is a chord tone)
                direction = 0
                if ttp.tonalToPitch(prevNote, music.key) in chordVec:
                    nextNote = prevNote
                else:
                    nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)
            elif num1 < 0.7:    # 30% chance to move up linearly (technically less than 30%,
                                # because we check to see if prevNote + 1 is a chord tone)
                direction = 1
                if prevNote == 7:  # if prevNote is 7, can't use prevNote + 1
                    if ttp.tonalToPitch(1, music.key) in chordVec:
                        nextNote = 1
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)
                else:  # if prevNote isn't 7, can use prevNote + 1
                    if ttp.tonalToPitch(prevNote + 1, music.key) in chordVec:
                        nextNote = prevNote + 1
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)
            else:   # 30% chance to move down linearly (technically less than 30%,
                    # because we check to see if prevNote - 1 is a chord tone)
                direction = -1
                if prevNote == 1:  # if prevNote is 1, can't use prevNote - 1
                    if ttp.tonalToPitch(7, music.key) in chordVec:
                        nextNote = 7
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)
                else:  # if prevNote isn't 1, can use prevNote - 1
                    if ttp.tonalToPitch(prevNote - 1, music.key) in chordVec:
                        nextNote = prevNote - 1
                    else:
                        nextNote = ptt.pitchToTonal(chordVec[random.randint(0, len(chordVec) - 1)], music.key)


    # get direction if it hasn't been chosen yet. for now 50/50 up/down
    # TODO: pick a better method than this
    if direction == -2:
        if nextNote == prevNote:
            direction = 0
        else:
            num1 = random.random()
            if num1 > 0.5:
                direction = 1
            else:
                direction = -1

    # get distance
    distance = ttd.tonalToDistance(nextNote, direction, finalMTX[measure - 1][voice][-1].notes[-1].distance,
                                   voice, music.key, music.major)
    #TODO: if distance out of range for the voice, change the direction and distance

    # convert to a Note class
    nextNote2 = mo.Note(ttp.tonalToPitch(nextNote), distance, species, False, currentRoot)

    return nextNote2
Example #3
0
def orchestrate(music, chordArray, maxVoices, species = 1):

    if maxVoices == 3:
        bass = 0
        tenor = 1
        soprano = 2
    else:
        bass = 0
        tenor = 1
        soprano = 2
        alto = 3  # keeping as 3 instead of swapping with soprano - easier to add into current code

    beats1234 = [1,2,3,4]
    beats12 = [1,2]
    beats34 = [3,4]

    measures = len(chordArray)
    # create finalMTX - a 2D array of 1D arrays (lists) of Cells - because each measure can hold 1-4 Cells
    finalMTX = np.empty((measures, maxVoices), dtype=object)

    ################################################################
    # orchestrate the first chord
    ################################################################
    # bass
    finalMTX[0][bass] = []
    for chord in range(len(chordArray[0])):
        # if 1 chord in the measure
        if len(chordArray[0]) == 1:
            finalMTX[0][bass].append(mo.Cell(chordArray[0][0], chordArray[1][0], beats1234, [mo.Note(ttp.tonalToPitch(chordArray[0][0].root), ttd.tonalToDistance(chordArray[0][0].root), 1, False, chordArray[0][0].root)], None, bass))
    # TODO: add other species

    chordNotes = [1, 3, 5]  # create array for random to choose from below

    # alto/tenor
    finalMTX[0][tenor] = []
    for chord in range(len(chordArray[0])):
        # if 1 chord in the measure
        if len(chordArray[0]) == 1:
            pitch = ttp.tonalToPitch(random.choice(chordNotes))  # pick 1, 3, or 5
            finalMTX[0][tenor].append(mo.Cell(chordArray[0][0], chordArray[1][0], beats1234, [mo.Note(pitch, ptd.pitchToDistance(pitch, tenor), 1, False, chordArray[0][0].root)], None, tenor))
    # TODO: add other species

    # soprano
    finalMTX[0][soprano] = []
    if finalMTX[0][tenor][0].notes[0].pitch == music.key:  # if 2 roots, must pick 3rd
        pitch = finalMTX[0][tenor][0].chord.getPitches()[1] # 3rd of chord
    elif finalMTX[0][tenor][0].notes[0].pitch == mo.Chord(ptt.pitchToTonal(music.key)).getPitches()[1]:  # if root and 3rd, can pick 1 or 5
        chordNotes = [1, 5]
        pitch = ttp.tonalToPitch(random.choice(chordNotes))
    else:  # if root and 5th, must pick 3rd
        pitch = finalMTX[0][tenor][0].chord.getPitches()[1]  # 3rd of chord
    finalMTX[0][soprano].append(mo.Cell(chordArray[0][0], chordArray[1][0], beats1234, [mo.Note(pitch, ptd.pitchToDistance(pitch, soprano), 1, False, chordArray[0][0].root)], None, soprano))
    # TODO: add other species


    ################################################################
    # orchestrate the remaining chords
    ################################################################
    # NOTE: check for parallel 5ths, parallel octaves, tri-tones - redo a chord that fails check
    attempts = 0
    totalFailures = 0

    # save for overwrite prevention
    i64 = (chordArray[13][0].root == 1) # .inversion == 2

    i = 1
    while i < measures:
        """NOTE: this used to be a for loop from 1 to 'measures', but i changed to a while loop so we 
        could add checks for tritones and reset 'i' to re-write any 'bad' measures"""

        # bass
        notes = []
        for s in range(species):
            # TODO: CONTINUE FROM HERE *******make getNextNote() return Note classes instead of ints*********
            notes.append(gnn.getNextNote(music, chordArray, finalMTX, i, measures, bass, maxVoices))
        finalMTX[i][0][0] = mo.Cell(blah)

        # manually set last measure's bass to 1
        if i == measures - 1:
            finalMTX[0][i][0] = str(noteMTX[i][4])

        # fill out inversion column for the other voices to follow rules
        if int(finalMTX[0][i][0]) == noteMTX[i][4]:
            noteMTX[i][7] = 0
            finalMTX[0][i][7] = 0
        elif int(finalMTX[0][i][0]) == noteMTX[i][4]+2 or int(finalMTX[0][i][0]) == noteMTX[i][4]-5:  # wrapping 1-7
            #print('1st')
            noteMTX[i][7] = 1
            finalMTX[0][i][7] = 1
        elif int(finalMTX[0][i][0]) == noteMTX[i][4]+4 or int(finalMTX[0][i][0]) == noteMTX[i][4]-3:  # wrapping 1-7
            #print('2nd')
            noteMTX[i][7] = 2
            finalMTX[0][i][7] = 2
        else:
            #print('3rd')
            noteMTX[i][7] = 3
            finalMTX[0][i][7] = 3  # 7th chords have 3rd inversion


        # manually reset 164 if it was overwritten
        if i == 13 and i64:
            #print('overwriting')
            noteMTX[13][0] = str(noteMTX[0][4]+4)
            if int(noteMTX[13][0]) > 7:
                noteMTX[13][0] = str(int(noteMTX[13][0]) - 7)
            finalMTX[0][13][0] = noteMTX[13][0]
            for j in range(maxVoices):
                finalMTX[i][j][0].chord.inversion = 2

        # soprano
        finalMTX[2][i][0] = str(gnn.getNextNote(music, noteMTX, finalMTX, i, measures, 2, maxVoices))  # soprano

        # alto/tenor:
        finalMTX[1][i][0] = str(gnn.getNextNote(music, noteMTX, finalMTX, i, measures, 1, maxVoices))  # alto/tenor

        # check for tritones, parallel 5ths, and parallel octaves. if any are found, rewrite the whole measure
        if (int(finalMTX[0][i][0]) == 4 and int(finalMTX[0][i - 1][0]) == 7) or \
            (int(finalMTX[0][i][0]) == 7 and int(finalMTX[0][i - 1][0]) == 4) or \
            (int(finalMTX[1][i][0]) == 4 and int(finalMTX[1][i - 1][0]) == 7) or \
            (int(finalMTX[1][i][0]) == 7 and int(finalMTX[1][i - 1][0]) == 4) or \
            (int(finalMTX[2][i][0]) == 4 and int(finalMTX[2][i - 1][0]) == 7) or \
            (int(finalMTX[2][i][0]) == 7 and int(finalMTX[2][i - 1][0]) == 4) or \
            (int(finalMTX[0][i][0]) == (int(finalMTX[1][i][0]) + 3) % 7 + 1) and (
        int(finalMTX[0][i - 1][0]) == (int(finalMTX[1][i - 1][0]) + 3) % 7 + 1) or \
            (int(finalMTX[0][i][0]) == (int(finalMTX[2][i][0]) + 3) % 7 + 1) and (
        int(finalMTX[0][i - 1][0]) == (int(finalMTX[2][i - 1][0]) + 3) % 7 + 1) or \
            (int(finalMTX[1][i][0]) == (int(finalMTX[2][i][0]) + 3) % 7 + 1) and (
        int(finalMTX[1][i - 1][0]) == (int(finalMTX[2][i - 1][0]) + 3) % 7 + 1) or \
            (int(finalMTX[0][i][0]) == int(finalMTX[1][i][0]) and int(finalMTX[0][i - 1][0]) == int(finalMTX[1][i - 1][0])) or \
            (int(finalMTX[0][i][0]) == int(finalMTX[2][i][0]) and int(finalMTX[0][i - 1][0]) == int(finalMTX[2][i - 1][0])) or \
            (int(finalMTX[1][i][0]) == int(finalMTX[2][i][0]) and int(finalMTX[1][i - 1][0]) == int(finalMTX[2][i - 1][0])):

            # increment
            attempts += 1
            totalFailures += 1

            if totalFailures < 40:  # only try to go back if we're not caught in an endless loop
                i -= attempts  # rewrite the last few measures, the more failures, the farther back we go
            elif totalFailures >= 40:  # otherwise we probably ended up in an endless loop so try changing noteMTX
                print("Changing the matrix. Measure "+str(i + 1)+" is now a I chord.")
                noteMTX[i][4] = 1
                i -= 1  # reattempt current measure with new noteMTX
                totalFailures = 0

            if i < 0: # just in case we go back too far or get caught in a loop, start at measure 2
                i = 0
                attempts = 0
            #print("Attempt #" + str(attempts) + ". Rewriting measure " + str(i + 1) + ".")

        else:
            attempts = 0  # reset
            # set all columns for i-th row of finalMTX using noteMTX
            #   12 note data types: pitch, duration, direction, interval, chord root,
            #       7th chord, tonality, inversion, prev chord root, distance, beat, measure
            for voice in range(3):
                finalMTX[voice][i][1] = chordsPerMeasure    # duration

                # voice 0 interval
                if finalMTX[voice][i][0] == '1' or finalMTX[voice][i][0] == '2':    # interval, must check for 'wrapping'
                    if finalMTX[voice][i-1][0] == '6' or finalMTX[voice][i-1][0] == '7':
                        temp = int(finalMTX[voice][i][0]) - int(finalMTX[voice][i-1][0])
                        if temp < 0:
                            temp += 7
                        finalMTX[0][i][3] = temp
                    else:
                        finalMTX[voice][i][3] = finalMTX[voice][i][0] - finalMTX[voice][i-1][0]
                elif finalMTX[voice][i][0] == '6' or finalMTX[voice][i][0] == '7':
                    if finalMTX[voice][i-1][0] == '1' or finalMTX[voice][i-1][0] == '2':
                        temp = int(finalMTX[voice][i][0]) - int(finalMTX[voice][i-1][0])
                        if temp > 0:
                            temp -= 7
                        finalMTX[voice][i][3] = temp
                    else:
                        finalMTX[voice][i][3] = int(finalMTX[voice][i][0]) - int(finalMTX[voice][i-1][0])
                else:
                    finalMTX[voice][i][3] = int(finalMTX[voice][i][0]) - int(finalMTX[voice][i-1][0])

                if finalMTX[voice][i][3] == 0:                              # direction
                    finalMTX[voice][i][2] = 0
                elif finalMTX[voice][i][3] > 0:
                    finalMTX[voice][i][2] = 1
                else:
                    finalMTX[voice][i][2] = -1

                finalMTX[voice][i][4] = noteMTX[i][4]                       # chord root
                finalMTX[voice][i][5] = noteMTX[i][5]                       # 7th chord
                finalMTX[voice][i][6] = noteMTX[i][6]                       # tonality
                finalMTX[voice][i][7] = noteMTX[i][7]                       # inversion
                finalMTX[voice][i][8] = noteMTX[i-1][4]                     # prev chord root
                finalMTX[voice][i][9] = 0                                   # distance, none in bass
                finalMTX[voice][i][10] = noteMTX[i][10]                     # beat
                finalMTX[voice][i][11] = noteMTX[i][11]                     # measure

        i += 1  # move on to the next measure

    #print(finalMTX)
    return finalMTX
Example #4
0
def getNotesFugue(currentChord, nextChord, beatsArr, startDistance = None, destinationDistance = None, voice = 0, episode = False, previousCell = None, key = 'c', major = True, timesig = None):

    if timesig is None:
        timesig = [4,4]

    # any measure where a voice first enters or re-enters after a rest and no start is specified
    if not startDistance:

        # pick a random index from options
        options = dc.defineChord(currentChord)
        startPitch = random.choice(options[0])
        startTonal = ptt.pitchToTonal(startPitch)
        # pick a distance based on voice
        startDistance = ptd.pitchToDistance(startPitch, voice)
        #print('start:', startTonal, startPitch, startDistance)

    # if we are given a start
    else:

        # convert start to 0-7
        startPitch = dtp.distanceToPitch(startDistance)
        startTonal = ptt.pitchToTonal(startPitch)
        #print('start:', startTonal, startPitch, startDistance)


    # if no destination given, make one
    if not destinationDistance:

        # pick a random destination
        options = dc.defineChord(nextChord)
        destinationPitch = random.choice(options[0])
        destinationTonal = ptt.pitchToTonal(destinationPitch)
        # pick a distance based on voice
        destinationDistance = ptd.pitchToDistance(destinationPitch, voice)
        #print('destination:', destinationTonal, destinationPitch, destinationDistance)

    # if we are given a destination
    else:

        # convert destination to 0-7
        destinationPitch = dtp.distanceToPitch(destinationDistance)
        destinationTonal = ptt.pitchToTonal(destinationPitch)
        #print('destination:', destinationTonal, destinationPitch, destinationDistance)


    notes = []

    # calculate distance and direction
    if startDistance == destinationDistance:
        direction = 0
        distanceBetweenTonal = 0
    elif startDistance < destinationDistance:
        direction = 1
        if startTonal < destinationTonal: # 1 to 7 = 7 - 1 = 6, 3 to 5 = 5 - 3 = 2
            distanceBetweenTonal = max(destinationTonal, startTonal) - min(destinationTonal, startTonal)
        else: # 7 to 1 = 1 - 7 + 7 = 1, 5 to 3 = 3 - 5 + 7 = 5
            distanceBetweenTonal = min(destinationTonal, startTonal) - max(destinationTonal, startTonal) + 7
    else:
        direction = -1
        if startTonal < destinationTonal: # 1 to 7 = 1 - 7 + 7 = 1, 3 to 5 = 3 - 5 + 7 = 5
            distanceBetweenTonal = min(destinationTonal, startTonal) - max(destinationTonal, startTonal) + 7
        else: # 7 to 1 = 7 - 1 = 6, 5 to 3 = 5 - 3 = 2
            distanceBetweenTonal = max(destinationTonal, startTonal) - min(destinationTonal, startTonal)


    print('start: ' + str(startTonal) + '\tdestination: ' + str(destinationTonal) + '\tdistanceBetweenTonal: ' + str(distanceBetweenTonal) + '\tdirection: ' + str(direction))


    #######################################################################
    # FOR NON-EPISODES
    #######################################################################

    if not episode:

        num1 = random.random()
        #print(num1)

        #####################################################
        # LINEAR MOTION
        #####################################################

        # TODO: find good percentage. starting high for debugging
        # 80% chance to move linearly - technically less if distance is out of range
        # NOTE: tested these values with testGetRhythms.py
        if num1 < 0.8 and ( (len(beatsArr) == 4 and distanceBetweenTonal > 2 and distanceBetweenTonal < 9) or (len(beatsArr) == 2 and distanceBetweenTonal > 0 and distanceBetweenTonal < 8) ):

            # create rhythms with a length equal to distance
            # TODO: update and reuse randRhythm() from old getRhythmsChorale.py rather than rely on while loop... wasted cycles
            lenR = -1
            while lenR != distanceBetweenTonal:
                rhythms = grf.getRhythmsFugue(beatsArr, timesig)
                lenR = len(rhythms)

            #print('distance:', distance, '\tlenR:', lenR)

            # TODO: only give linear motion an X% chance. (100-X)% chance to move differently
            # if dist up or dist down is same as number of rhythms, and that distance is < 5
            if lenR == distanceBetweenTonal and direction == 1:
                nextNote = int(startTonal)
                for note in rhythms:
                    if nextNote == 8:
                        nextNote = 1
                    notes.append(nextNote)
                    nextNote += 1
            elif lenR == distanceBetweenTonal and direction == -1:
                # same as above in other direction
                nextNote = int(startTonal)
                for note in rhythms:
                    if nextNote == 0:
                        nextNote = 7
                    notes.append(nextNote)
                    nextNote -= 1
            else:
                print('error under linear motion in getNotes.py')


        #####################################################
        # COMMON PATTERNS
        #####################################################

            # # repeated pitch patterns
            # elif abs(distance) == 0:
            #     pass

            # # 2nd jump patterns
            # elif abs(distance) == 1:
            #     pass

            # # 3rd jump patterns
            # elif abs(distance) == 2:
            #     pass

            # # 4th jump patterns
            # elif abs(distance) == 3:
            #     pass

            # # 5th jump patterns
            # elif abs(distance) == 4:
            #     pass

            # # 6th jump patterns
            # elif abs(distance) == 5:
            #     pass

            # # 7th jump patterns
            # elif abs(distance) == 6:
            #     pass

            # # octave jump patterns
            # elif abs(distance) == 7:
            #     pass

        #####################################################
        # MICRO-DESTINATION
        #####################################################

        else:

            # call getRhythmsFugue to generate rhythms - this is now our number of notes needed
            rhythms = grf.getRhythmsFugue(beatsArr, timesig)
            lenR = len(rhythms)

            # TODO: change this. don't just repeat.
            for note in rhythms:
                notes.append(startTonal)

            # check for accented beats after the first rhythm, create new micro-destinations
            # if len(beatsArr) > 2:
            #     for r in rhythms[1:]:
            #         if r[-1]:  # if accented
            #             # find a new micro-dest with a distance between startDistance and destinationDistance
            #             # move linearly if possible
            #             # else move be step
            #             microDestinationTonal, x, y = gmd.getMicroDestination(currentChord, startDistance, direction)
            #         else:
            #             pass

    #######################################################################
    # FOR EPISODES
    #######################################################################

    else:
        #print('episode')

        # if previousCell is None, it's the first cell of the episode
        if not previousCell:
            rhythms = grf.getRhythmsFugue(beatsArr, timesig)

            # TODO: change this. don't just repeat.
            for note in rhythms:
                notes.append(startTonal)

        # otherwise try to match intervals/rhythms from previousCell
        else:
            rhythms = grf.addRhythmData(previousCell.notes)

            # TODO: change this. don't just repeat.
            for note in rhythms:
                notes.append(startTonal)



    #######################################################################
    # CONVERT TO MUSIC OBJECTS BEFORE RETURNING
    #######################################################################
    #print('notes: ', notes)
    #print('rhythms: ', rhythms)
    #print('destinationDistance:', destinationDistance)

    # convert notes and rhythms to Note classes
    newNotes = []
    for i in range(len(notes)):
        tied = (rhythms[i][0][-1] == '.')
        if tied:
            rhythms[i][0] = rhythms[i][0][:-1]
        ##########################################################################
        # TODO: FIX DISTANCE BELOW! this is where you decide where to move
        ##########################################################################
        if i == 0:
            fixedDistance = int(startDistance)
        else:
            # no octave jumps yet, so this fixes the repeated note problem
            if notes[i] == notes[i-1]:
                fixedDistance = ttd.tonalToDistance(notes[i], 0, fixedDistance)
            else:
                fixedDistance = ttd.tonalToDistance(notes[i], direction, fixedDistance)
                # check range
                if newNotes[-1].distance > fixedDistance and (newNotes[-1].distance - fixedDistance) > 9:
                    fixedDistance += 12
                elif newNotes[-1].distance < fixedDistance and (fixedDistance - newNotes[-1].distance) > 9:
                    fixedDistance += 12
        # if voice == blah, check bounds so distance doesn't go too far
        newNotes.append(mo.Note(ttp.tonalToPitch(notes[i]), fixedDistance, int(rhythms[i][0]), tied, currentChord.root,
                                currentChord.tonality, currentChord.seventh, currentChord.inversion, currentChord.secondary,
                                currentChord.secondaryRoot, key, major, timesig))

    return newNotes, destinationDistance
Example #5
0
def fugueWriter(subjectMTX=None, music=None):

    if music is None:
        music = mo.Music()

    # initialize variables
    measures = 12  # TODO: update to number of measures that have been finished - 32 total
    beats1234 = [1, 2, 3, 4]
    beats12 = [1, 2]
    beats34 = [3, 4]
    #maxVoices = -1
    #while maxVoices != 3 or maxVoices != 4:
    #    maxVoices = int(input("Enter 3 or 4 voices: "))
    maxVoices = 3
    if maxVoices == 3:
        bass = 0
        tenor = 1
        soprano = 2
    else:
        bass = 0
        tenor = 1
        soprano = 2
        alto = 3  # keeping as 3 instead of swapping with soprano - easier to add into current code

    # create finalMTX - a 2D array of 1D arrays (lists) of Cells - because each measure can hold 1-4 Cells
    finalMTX = np.empty((measures, maxVoices), dtype=object)

    #####################################################################
    # CREATE MEASURES 1-2 - Tonic (I)
    # NOTE: default harmonies pick from: I-I, I-IV, I-V
    # Soprano - rest
    # Tenor - Subject
    # Bass - rest
    #####################################################################

    # if no user-generated melody is provided, make one up
    if subjectMTX is None:
        # NOTE: 2 measures, 1 voice
        subjectMTX = np.empty((2, 1), dtype=object)

        # pick from default harmonies: I-I, I-IV, I-V
        # NOTE: all move to a V chord in the 3rd measure
        firstChords = [[1, 1, 5], [1, 4, 5], [1, 5, 5]]
        destinationChords = random.choice(firstChords)
        print('chords for measures 1-3 are:', str(destinationChords))

        # 1st measure
        print("*******************MEASURE 1 (tenor)**********************")
        notes, destinationTenor = gnf.getNotesFugue(
            mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]),
            beats1234, ptd.pitchToDistance(music.key), None,
            tenor)  # NOTE: starting with root of key
        #print('notes:', notes)
        print('destinationTenor:', destinationTenor,
              dtp.distanceToPitch(destinationTenor))
        subjectMTX[0][0] = [
            mo.Cell(mo.Chord(destinationChords[0]),
                    mo.Chord(destinationChords[1]), beats1234, notes,
                    destinationTenor, tenor)
        ]
        # 2nd measure
        print("*******************MEASURE 2 (tenor)**********************")
        notes, destinationTenor = gnf.getNotesFugue(
            mo.Chord(destinationChords[1]), mo.Chord(destinationChords[2]),
            beats1234, destinationTenor, None, tenor)
        #print('notes:', notes)
        print('destinationTenor:', destinationTenor,
              dtp.distanceToPitch(destinationTenor))
        subjectMTX[1][0] = [
            mo.Cell(mo.Chord(destinationChords[1]),
                    mo.Chord(destinationChords[2]), beats1234, notes,
                    destinationTenor, tenor)
        ]

        # free memory
        del firstChords, notes

    else:
        destinationTenor = subjectMTX[0][-1].destination

    # assign subjectMTX values to finalMTX
    # TODO: add for loop (subjectMTX.size[1]) to allow for subjects that are not exactly 2 measures long
    finalMTX[0][tenor] = subjectMTX[0][bass]
    finalMTX[1][tenor] = subjectMTX[1][bass]

    # free memory
    del subjectMTX

    # 1st and 2nd measure for other voices
    print("*******************MEASURE 1 (bass)**********************")
    finalMTX[0][bass] = [
        mo.Cell(finalMTX[0][tenor][0].chord, finalMTX[0][tenor][0].nextChord,
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, bass)
    ]
    print("*******************MEASURE 2 (bass)**********************")
    finalMTX[1][bass] = [
        mo.Cell(finalMTX[0][tenor][0].chord, finalMTX[0][tenor][0].nextChord,
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, bass)
    ]
    print("*******************MEASURE 1 (soprano)**********************")
    finalMTX[0][soprano] = [
        mo.Cell(finalMTX[0][tenor][0].chord, finalMTX[0][tenor][0].nextChord,
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, soprano)
    ]
    print("*******************MEASURE 2 (soprano)**********************")
    finalMTX[1][soprano] = [
        mo.Cell(finalMTX[0][tenor][0].chord, finalMTX[0][tenor][0].nextChord,
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, soprano)
    ]

    #####################################################################
    # CREATE MEASURES 3-4 - Dominant (V)
    # NOTE: default harmonies set to first 2 measure chords +4, so I-V becomes V-ii
    # Soprano - Answer
    # Tenor - Counter-Subject 1
    # Bass - rest
    #####################################################################

    # create destination pitches
    # TODO: remove default V in measure 5 to create some randomness, such as ii-V, IV-V, vi-V
    # TODO: add for loop to allow for subjects longer than 2 measures
    destinationChords = [(finalMTX[0][tenor][0].chord.root + 3) % 7 + 1,
                         (finalMTX[1][tenor][0].chord.root + 3) % 7 + 1, 5]
    print('chords for measures 3-5 are: ' + str(destinationChords))

    # Bass - resting
    print("*******************MEASURE 3 (bass)**********************")
    finalMTX[2][bass] = [
        mo.Cell(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]),
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, bass)
    ]
    print("*******************MEASURE 4 (bass)**********************")
    finalMTX[3][bass] = [
        mo.Cell(mo.Chord(destinationChords[1]), mo.Chord(destinationChords[2]),
                finalMTX[0][tenor][0].beats, [mo.Note('r', 88, 1, False, 1)],
                None, bass)
    ]

    # Tenor - Countersubject
    print("*******************MEASURE 3 (tenor)**********************")
    notes, destinationTenor = gnf.getNotesFugue(mo.Chord(destinationChords[0]),
                                                mo.Chord(destinationChords[1]),
                                                beats1234, destinationTenor,
                                                None, tenor)
    #print('notes:', notes)
    #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
    finalMTX[2][tenor] = [
        mo.Cell(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]),
                beats1234, notes, destinationTenor, tenor)
    ]
    print("*******************MEASURE 4 (tenor)**********************")
    notes, destinationTenor = gnf.getNotesFugue(mo.Chord(destinationChords[1]),
                                                mo.Chord(destinationChords[2]),
                                                beats1234, destinationTenor,
                                                None, tenor)
    #print('notes:', notes)
    #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
    finalMTX[3][tenor] = [
        mo.Cell(mo.Chord(destinationChords[1]), mo.Chord(destinationChords[2]),
                beats1234, notes, destinationTenor, tenor)
    ]

    # Soprano - Answer
    # get pitches that fit the new harmony
    print("*******************MEASURE 3 (soprano)**********************")
    finalMTX[2][soprano] = []
    for cell in finalMTX[0][tenor]:
        # leaving direction and newDistance defaulted - letting it find the new distance
        nextCell = tcd.transposeCellDiatonically(
            cell, mo.Chord(destinationChords[0]),
            mo.Chord(destinationChords[1]), 1)
        nextCell.voice = soprano
        finalMTX[2][soprano].append(nextCell)
    # get pitches that fit the new harmony
    print("*******************MEASURE 4 (soprano)**********************")
    finalMTX[3][soprano] = []
    for cell in finalMTX[1][tenor]:
        # leaving direction and newDistance defaulted - letting it find the new distance
        nextCell = tcd.transposeCellDiatonically(
            cell, mo.Chord(destinationChords[1]),
            mo.Chord(destinationChords[2]), 1)
        nextCell.voice = soprano
        finalMTX[3][soprano].append(nextCell)
    # TODO: soprano destination changes to fit V chord - alter soprano in measure 4 if you want to move linearly
    notesInVChord = dc.defineChord(mo.Chord(5))
    currentDestinationDistance = finalMTX[3][soprano][-1].destination
    # TODO: change this. for now just move down to closest destination that IS in a V chord
    while dtp.distanceToPitch(
            currentDestinationDistance) not in notesInVChord[0]:
        currentDestinationDistance -= 1
    finalMTX[3][soprano][-1].destination = currentDestinationDistance

    #####################################################################
    # CREATE MEASURE 5
    # NOTE: default harmony set to V
    # Soprano - Codetta
    # Tenor - Codetta
    # Bass - rest
    #####################################################################

    # TODO: change all mo.Chord(1)s in this section to match whatever the first chord in the matrix is - not always a I chord
    # Bass - resting
    print("*******************MEASURE 5 (bass)**********************")
    finalMTX[4][0] = [
        mo.Cell(mo.Chord(5), mo.Chord(1), finalMTX[0][tenor][0].beats,
                [mo.Note('r', 88, 1, False, 1)], None, bass)
    ]

    # Tenor - Codetta
    print("*******************MEASURE 5 (tenor)**********************")
    notes, destinationTenor = gnf.getNotesFugue(mo.Chord(5), mo.Chord(1),
                                                beats1234, destinationTenor,
                                                None, tenor)
    #print('notes:', notes)
    print([x.distance for x in notes])
    #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
    finalMTX[4][tenor] = [
        mo.Cell(mo.Chord(5), mo.Chord(1), beats1234, notes, destinationTenor,
                tenor)
    ]

    # Soprano - Codetta
    print("*******************MEASURE 5 (soprano)**********************")
    notes, destinationSoprano = gnf.getNotesFugue(
        mo.Chord(5), mo.Chord(1), beats1234,
        finalMTX[3][soprano][-1].destination, None, soprano)
    # print('notes:', notes)
    print([x.distance for x in notes])
    #print('destinationSoprano:', destinationSoprano, dtp.distanceToPitch(destinationSoprano))
    finalMTX[4][soprano] = [
        mo.Cell(mo.Chord(5), mo.Chord(1), beats1234, notes, destinationSoprano,
                soprano)
    ]

    #####################################################################
    # CREATE MEASURES 6-7 - Tonic (I)
    # Soprano - Counter-Subject 1
    # Tenor - Counter-Subject 2
    # Bass - Subject
    #####################################################################

    destinationChords = []
    for i in range(len(finalMTX[0][tenor])):
        destinationChords.append(finalMTX[0][tenor][i].chord.root)
    for i in range(len(finalMTX[1][tenor])):
        destinationChords.append(finalMTX[1][tenor][i].chord.root)
    print('chords for measures 6-7 are: ' + str(destinationChords))

    # Bass - Subject
    print("*******************MEASURE 6 (bass)**********************")
    finalMTX[5][bass] = []
    for cell in finalMTX[0][tenor]:
        # drop tenor an octave for bass
        nextCell = tcd.transposeCellDiatonically(cell, cell.chord,
                                                 cell.nextChord, -1)
        nextCell.voice = bass
        finalMTX[5][bass].append(nextCell)
    print("*******************MEASURE 7 (bass)**********************")
    finalMTX[6][bass] = []
    for cell in finalMTX[1][tenor]:
        # drop tenor an octave for bass
        nextCell = tcd.transposeCellDiatonically(cell, cell.chord,
                                                 cell.nextChord, -1)
        nextCell.voice = bass
        finalMTX[6][bass].append(nextCell)

    # Tenor - Counter-Subject 2
    print("*******************MEASURE 6 (tenor)**********************")
    for cell in range(len(finalMTX[2][tenor])):
        notes, destinationTenor = gnf.getNotesFugue(
            finalMTX[0][tenor][cell].chord, finalMTX[0][tenor][cell].nextChord,
            beats1234, destinationTenor, None, tenor)
        # print('notes:', notes)
        #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
        finalMTX[5][tenor] = [
            mo.Cell(finalMTX[0][tenor][cell].chord,
                    finalMTX[0][tenor][cell].nextChord, beats1234, notes,
                    destinationTenor, tenor)
        ]
    print("*******************MEASURE 7 (tenor)**********************")
    for cell in range(len(finalMTX[3][tenor])):
        notes, destinationTenor = gnf.getNotesFugue(
            finalMTX[1][tenor][cell].chord, finalMTX[1][tenor][cell].nextChord,
            beats1234, destinationTenor, None, tenor)
        # print('notes:', notes)
        #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
        finalMTX[6][tenor] = [
            mo.Cell(finalMTX[1][tenor][cell].chord,
                    finalMTX[1][tenor][cell].nextChord, beats1234, notes,
                    destinationTenor, tenor)
        ]

    # Soprano - Counter-Subject 1
    print("*******************MEASURE 6 (soprano)**********************")
    finalMTX[5][soprano] = []
    for cell in range(len(finalMTX[2][tenor])):
        # raise tenor for soprano
        nextCell = tcd.transposeCellDiatonically(
            finalMTX[2][tenor][cell], finalMTX[0][tenor][cell].chord,
            finalMTX[0][tenor][cell].nextChord, 1)
        nextCell.voice = soprano
        finalMTX[5][2].append(nextCell)
    print("*******************MEASURE 7 (soprano)**********************")
    finalMTX[6][soprano] = []
    for cell in range(len(finalMTX[3][tenor])):
        # raise tenor for soprano
        nextCell = tcd.transposeCellDiatonically(
            finalMTX[3][tenor][cell], finalMTX[1][tenor][cell].chord,
            finalMTX[1][tenor][cell].nextChord, 1)
        nextCell.voice = soprano
        finalMTX[6][soprano].append(nextCell)

    #####################################################################
    # CREATE MEASURES 8-9 - Dominant (V)
    # Soprano - Counter-Subject 2
    # Tenor - Answer
    # Bass - Counter-Subject 1
    #####################################################################

    destinationChords = []
    for i in range(len(finalMTX[2][tenor])):
        destinationChords.append(finalMTX[2][tenor][i].chord.root)
    for i in range(len(finalMTX[3][tenor])):
        destinationChords.append(finalMTX[3][tenor][i].chord.root)
    print('chords for measures 8-9 are: ' + str(destinationChords))

    # Bass - Counter-Subject 1
    print("*******************MEASURE 8 (bass)**********************")
    finalMTX[7][bass] = []
    for cell in finalMTX[2][tenor]:
        # drop tenor an octave for bass
        nextCell = tcd.transposeCellDiatonically(cell, cell.chord,
                                                 cell.nextChord, -1)
        nextCell.voice = bass
        finalMTX[7][bass].append(nextCell)
    print("*******************MEASURE 9 (bass)**********************")
    finalMTX[8][bass] = []
    for cell in finalMTX[3][tenor]:
        # drop tenor an octave for bass
        nextCell = tcd.transposeCellDiatonically(cell, cell.chord,
                                                 cell.nextChord, -1)
        nextCell.voice = bass
        finalMTX[8][bass].append(nextCell)

    # Tenor - Answer
    print("*******************MEASURE 8 (tenor)**********************")
    finalMTX[7][tenor] = []
    for cell in range(len(finalMTX[2][tenor])):
        notes, destinationTenor = gnf.getNotesFugue(
            finalMTX[2][1][cell].chord, finalMTX[2][1][cell].nextChord,
            beats1234, destinationTenor, None, tenor)
        # print('notes:', notes)
        print([x.distance for x in notes])
        #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
        finalMTX[7][1].append(
            mo.Cell(finalMTX[2][1][cell].chord, finalMTX[2][1][cell].nextChord,
                    beats1234, notes, destinationTenor, tenor))
    print("*******************MEASURE 9 (tenor)**********************")
    finalMTX[8][tenor] = []
    for cell in range(len(finalMTX[3][tenor])):
        notes, destinationTenor = gnf.getNotesFugue(
            finalMTX[3][1][cell].chord, finalMTX[3][1][cell].nextChord,
            beats1234, destinationTenor, None, tenor)
        # print('notes:', notes)
        #print('destinationTenor:', destinationTenor, dtp.distanceToPitch(destinationTenor))
        finalMTX[8][tenor].append(
            mo.Cell(finalMTX[3][1][cell].chord, finalMTX[3][1][cell].nextChord,
                    beats1234, notes, destinationTenor, tenor))

    # Soprano - Counter-Subject 2
    print("*******************MEASURE 8 (soprano)**********************")
    finalMTX[7][soprano] = []
    for cell in range(len(finalMTX[5][tenor])):
        # raise tenor for soprano
        nextCell = tcd.transposeCellDiatonically(
            finalMTX[5][1][cell], finalMTX[2][1][cell].chord,
            finalMTX[2][1][cell].nextChord, 1)
        nextCell.voice = soprano
        finalMTX[7][soprano].append(nextCell)
    print("*******************MEASURE 9 (soprano)**********************")
    finalMTX[8][soprano] = []
    for cell in range(len(finalMTX[6][tenor])):
        # raise tenor for soprano
        nextCell = tcd.transposeCellDiatonically(
            finalMTX[6][1][cell], finalMTX[3][1][cell].chord,
            finalMTX[3][1][cell].nextChord, 1)
        nextCell.voice = soprano
        finalMTX[8][soprano].append(nextCell)

    #####################################################################
    # CREATE MEASURES 10-12
    # Soprano - Episode
    # Tenor - Episode
    # Bass - Episode
    #####################################################################

    # first find the progression from starting chord (measure 5 = V by default)
    #   to relative minor/major (vi by default)
    # for now defaulting to V I, vi ii, viio V/vi
    episodeChords = [
        mo.Chord(5),
        mo.Chord(1),
        mo.Chord(6),
        mo.Chord(2),
        mo.Chord(7),
        mo.Chord(5, 0, False, 0, True, 6),
        mo.Chord(6)
    ]
    destinationChords = []
    for c in episodeChords:
        destinationChords.append(c.root)
    print('chords for measures 10-12 are: ' + str(destinationChords))

    # Bass - Episode
    # TODO: add adaptability. hardcoding bassline in C major for now
    print("*******************MEASURE 10 (bass)**********************")
    newNote = mo.Note('g', 22, 2, False, episodeChords[0].root)
    newCell = mo.Cell(episodeChords[0], episodeChords[1], beats12, [newNote],
                      27, 0)
    finalMTX[9][bass] = [newCell]
    newNote = mo.Note('c', 27, 2, False, episodeChords[1].root)
    newCell = mo.Cell(episodeChords[1], episodeChords[2], beats34, [newNote],
                      24, 0)
    finalMTX[9][bass].append(newCell)
    print("*******************MEASURE 11 (bass)**********************")
    newNote = mo.Note('a', 24, 2, False, episodeChords[2].root)
    newCell = mo.Cell(episodeChords[2], episodeChords[3], beats12, [newNote],
                      29, 0)
    finalMTX[10][bass] = [newCell]
    newNote = mo.Note('d', 29, 2, False, episodeChords[3].root)
    newCell = mo.Cell(episodeChords[3], episodeChords[4], beats34, [newNote],
                      26, 0)
    finalMTX[10][bass].append(newCell)
    print("*******************MEASURE 12 (bass)**********************")
    newNote = mo.Note('b', 26, 2, False, episodeChords[4].root)
    newCell = mo.Cell(episodeChords[4], episodeChords[5], beats12, [newNote],
                      31, 0)
    finalMTX[11][bass] = [newCell]
    newNote = mo.Note('e', 31, 2, False, episodeChords[5].root,
                      episodeChords[5].tonality, episodeChords[5].seventh,
                      episodeChords[5].inversion, episodeChords[5].secondary,
                      episodeChords[5].secondaryRoot)  # V/vi
    newCell = mo.Cell(episodeChords[5], episodeChords[6], beats34, [newNote],
                      24, 0)
    finalMTX[11][bass].append(newCell)

    # Tenor - Episode
    print("*******************MEASURE 10 (tenor)**********************")
    finalMTX[9][tenor] = []
    notes, destinationTenor = gnf.getNotesFugue(
        episodeChords[0], episodeChords[1], beats12,
        finalMTX[8][tenor][-1].destination, None, tenor, True)
    finalMTX[9][tenor].append(
        mo.Cell(episodeChords[0], episodeChords[1], beats12, notes,
                destinationTenor, tenor))
    notes, destinationTenor = gnf.getNotesFugue(episodeChords[1],
                                                episodeChords[2], beats34,
                                                destinationTenor, None, tenor,
                                                True, finalMTX[9][1][0])
    finalMTX[9][tenor].append(
        mo.Cell(episodeChords[1], episodeChords[2], beats34, notes,
                destinationTenor, tenor))
    print("*******************MEASURE 11 (tenor)**********************")
    finalMTX[10][tenor] = []
    notes, destinationTenor = gnf.getNotesFugue(episodeChords[2],
                                                episodeChords[3], beats12,
                                                destinationTenor, None, tenor,
                                                True, finalMTX[9][1][0])
    finalMTX[10][tenor].append(
        mo.Cell(episodeChords[2], episodeChords[3], beats12, notes,
                destinationTenor, tenor))
    notes, destinationTenor = gnf.getNotesFugue(episodeChords[3],
                                                episodeChords[4], beats34,
                                                destinationTenor, None, tenor,
                                                True, finalMTX[9][1][0])
    finalMTX[10][tenor].append(
        mo.Cell(episodeChords[3], episodeChords[4], beats34, notes,
                destinationTenor, tenor))
    print("*******************MEASURE 12 (tenor)**********************")
    finalMTX[11][tenor] = []
    notes, destinationTenor = gnf.getNotesFugue(episodeChords[4],
                                                episodeChords[5], beats12,
                                                destinationTenor, None, tenor,
                                                True, finalMTX[9][1][0])
    finalMTX[11][tenor].append(
        mo.Cell(episodeChords[4], episodeChords[5], beats12, notes,
                destinationTenor, tenor))
    notes, destinationTenor = gnf.getNotesFugue(episodeChords[5],
                                                episodeChords[6], beats34,
                                                destinationTenor, None, tenor,
                                                True, finalMTX[9][1][0])
    finalMTX[11][tenor].append(
        mo.Cell(episodeChords[5], episodeChords[6], beats34, notes,
                destinationTenor, tenor))

    # Soprano - Episode
    print("*******************MEASURE 10 (soprano)**********************")
    finalMTX[9][soprano] = []
    notes, destinationSoprano = gnf.getNotesFugue(
        episodeChords[0], episodeChords[1], beats12,
        finalMTX[8][soprano][-1].destination, None, soprano, True)
    finalMTX[9][soprano].append(
        mo.Cell(episodeChords[0], episodeChords[1], beats12, notes,
                destinationSoprano, soprano))
    notes, destinationSoprano = gnf.getNotesFugue(episodeChords[1],
                                                  episodeChords[2], beats34,
                                                  destinationSoprano, None,
                                                  soprano, True,
                                                  finalMTX[9][2][0])
    finalMTX[9][soprano].append(
        mo.Cell(episodeChords[1], episodeChords[2], beats34, notes,
                destinationSoprano, soprano))
    print("*******************MEASURE 11 (soprano)**********************")
    finalMTX[10][soprano] = []
    notes, destinationSoprano = gnf.getNotesFugue(episodeChords[2],
                                                  episodeChords[3], beats12,
                                                  destinationSoprano, None,
                                                  soprano, True,
                                                  finalMTX[9][2][0])
    finalMTX[10][soprano].append(
        mo.Cell(episodeChords[2], episodeChords[3], beats12, notes,
                destinationSoprano, soprano))
    notes, destinationSoprano = gnf.getNotesFugue(episodeChords[3],
                                                  episodeChords[4], beats34,
                                                  destinationSoprano, None,
                                                  soprano, True,
                                                  finalMTX[9][2][0])
    finalMTX[10][soprano].append(
        mo.Cell(episodeChords[3], episodeChords[4], beats34, notes,
                destinationSoprano, soprano))
    print("*******************MEASURE 12 (soprano)**********************")
    finalMTX[11][soprano] = []
    notes, destinationSoprano = gnf.getNotesFugue(episodeChords[4],
                                                  episodeChords[5], beats12,
                                                  destinationSoprano, None,
                                                  soprano, True,
                                                  finalMTX[9][2][0])
    finalMTX[11][soprano].append(
        mo.Cell(episodeChords[4], episodeChords[5], beats12, notes,
                destinationSoprano, soprano))
    notes, destinationSoprano = gnf.getNotesFugue(episodeChords[5],
                                                  episodeChords[6], beats34,
                                                  destinationSoprano, None,
                                                  soprano, True,
                                                  finalMTX[9][2][0])
    finalMTX[11][soprano].append(
        mo.Cell(episodeChords[5], episodeChords[6], beats34, notes,
                destinationSoprano, soprano))

    #####################################################################
    # CREATE MEASURES 13-14 - Relative minor/major (vi)
    # Soprano - Subject
    # Tenor - Counter-Subject 1
    # Bass - rest
    #####################################################################

    #####################################################################
    # CREATE MEASURES 15-16 - Dominant of relative minor/major (V/vi)
    # Soprano - Counter-Subject 1
    # Tenor - Counter-Subject 2
    # Bass - Answer
    #####################################################################

    #####################################################################
    # CREATE MEASURES 17-20 - vi, ii, V, I
    # Soprano - Episode
    # Tenor - Episode, Answer false entry measure 19
    # Bass - Episode
    #####################################################################

    #####################################################################
    # CREATE MEASURES 21-22 - Subdominant (IV)
    # Soprano - rest
    # Tenor - Subject
    # Bass - Counter-Subject 2
    #####################################################################

    #####################################################################
    # CREATE MEASURES 23-24
    # Soprano - Episode
    # Tenor - Episode
    # Bass - Episode
    #####################################################################

    #####################################################################
    # CREATE MEASURES 25-26 - Tonic (I)
    # Soprano - Subject
    # Tenor - Counter-Subject 2
    # Bass - Counter-Subject 1
    #####################################################################

    #####################################################################
    # CREATE MEASURES 27-28 - Tonic (I)
    # Soprano - Free counterpoint
    # Tenor - Counter-subject 1
    # Bass - Subject
    #####################################################################

    #####################################################################
    # CREATE MEASURES 29-32 - IV ii, V7, I sus43, I
    # Soprano - Coda, Answer false entry measure 30, hold 5th or 3rd measures 31 and 32
    # Tenor - Coda, sus43 measures 30-31 with anticipation of final chord at end of 31
    # Bass - pickup-iii, IV ii, V low-V, I, I and low-I
    #####################################################################

    #####################################################################
    # CREATE FILES: .ly, .xml
    #####################################################################
    #cl.createLily(music.key, music.major, finalMTX, measures, maxVoices)  # commented out while fugueWriter is being written
    # cl.createLily(key, major, finalMTX, measures, maxVoices, 2)  # second species
    # TO DO: add other species
    # not using regex so don't need this anymore, keeping for legacy
    # copyfile('newScore.ly','newScore2.ly')

    # create the pdf score
    print("Creating .pdf file(s) with LilyPond...")
    filename = "Fugue.ly"
    #os.system(filename)  # commented out while fugueWriter is being written
    # time.sleep(3)

    # create the xml file
    print("Creating .xml file(s)...")
    filename = "Fugue.xml"
    cx.createXML(filename, music.key, music.major, music.timesig, finalMTX,
                 measures, maxVoices)
Example #6
0
def melodyTester(subjectMTX = None, music = None):

    if music is None:
        music = mo.Music()

    # initialize variables
    measures = 3 # TODO: update to number of measures that have been finished - 32 total
    beats1234 = [1, 2, 3, 4]
    beats12 = [1, 2]
    beats34 = [3, 4]
    maxVoices = 3
    bass = 0
    tenor = 1
    soprano = 2

    # create finalMTX - a 2D array of 1D arrays (lists) of Cells - because each measure can hold 1-4 Cells
    finalMTX = np.empty((measures, maxVoices), dtype=object)

    # Hard-coding to I-IV-V for testing
    destinationChords = [1,4,5]

    # fill bass and soprano with rests
    finalMTX[0][bass] = [mo.Cell(destinationChords[0], destinationChords[1], beats1234, [mo.Note('r', 88, 1, False, 1)], None, bass)]
    finalMTX[1][bass] = [mo.Cell(destinationChords[1], destinationChords[2], beats1234, [mo.Note('r', 88, 1, False, 1)], None, bass)]
    finalMTX[1][bass] = [mo.Cell(destinationChords[2], destinationChords[0], beats1234, [mo.Note('r', 88, 1, False, 1)], None, bass)]
    finalMTX[0][soprano] = [mo.Cell(destinationChords[0], destinationChords[1], beats1234, [mo.Note('r', 88, 1, False, 1)], None, soprano)]
    finalMTX[1][soprano] = [mo.Cell(destinationChords[1], destinationChords[2], beats1234, [mo.Note('r', 88, 1, False, 1)], None, soprano)]
    finalMTX[1][soprano] = [mo.Cell(destinationChords[2], destinationChords[0], beats1234, [mo.Note('r', 88, 1, False, 1)], None, soprano)]
    
    # notes, destinationTenor = gnf.getNotesFugue(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]), beats1234, ptd.pitchToDistance(music.key), None, tenor)  # NOTE: starting with root of key
    # print('destinationTenor', destinationTenor, dtp.distanceToPitch(destinationTenor))
    # finalMTX[0][tenor] = [mo.Cell(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]), beats1234, notes, destinationTenor, tenor)]
    # notes, destinationTenor = gnf.getNotesFugue(mo.Chord(destinationChords[1]), mo.Chord(destinationChords[2]), beats1234, destinationTenor, None, tenor)  # NOTE: starting with root of key
    # print('destinationTenor', destinationTenor, dtp.distanceToPitch(destinationTenor))
    # finalMTX[1][tenor] = [mo.Cell(mo.Chord(destinationChords[1]), mo.Chord(destinationChords[2]), beats1234, notes, destinationTenor, tenor)]
    # finalMTX[2][tenor] = [mo.Cell(mo.Chord(destinationChords[2]), mo.Chord(1), beats1234, [mo.Note(dtp.distanceToPitch(destinationTenor), destinationTenor, 1, False, 5)], destinationTenor, tenor)]


    # notes
    # using a bunch of melodies: train a neural network? create a decision tree? many options, but leaning toward decision tree with weighted probability
    # decision tree would know which elements are more important at each step (highest information gain using entropy)
    # ie. is linear motion more important than microdestinations landing on a chord tone on an accented beat?
    # ie. is following intervallic/rhythmic repetition more important than landing on a chord tone in the next measure?
    # ie. if landing on a chord tone is more important, then should we change the rhythm or the interval?
    # goal is to avoid landing on non-chord tone on accented beats often, but not always
    # one way to achieve this goal: use microdestinations a large percentage of the time
    # note that microdestinations don't always need to occur directly on the accented beat (ie. suspension), but would still be used
    # question: when should non-chord tones be used? is it truly random or are there times when a suspension is more likely? measure 1-2 vs 2-3, beat 2-3 vs 4-1, V-vi vs V-I, etc

    print('MEASURE 1')
    startDistance = ptd.pitchToDistance(music.key)
    direction = random.choice([1,-1])
    # first use getMicroDestination() to pick destination for next measure
    nextMeasureDestTonal, nextMeasureDestDistance, nextMeasureDestPitch = gmd.getMicroDestination(mo.Chord(destinationChords[1]), startDistance, direction)
    # make sure distance is within range of the voice - testing tenor, so keep from 32-48 in key of C
    while nextMeasureDestDistance > 48:
        nextMeasureDestDistance -= 12
        # direction may have changed
        if startDistance == nextMeasureDestDistance:
            direction = 0
        elif startDistance > nextMeasureDestDistance:
            direction = -1
        else:
            direction = 1
    while nextMeasureDestDistance < 32:
        nextMeasureDestDistance += 12
        # direction may have changed
        if startDistance == nextMeasureDestDistance:
            direction = 0
        elif startDistance > nextMeasureDestDistance:
            direction = -1
        else:
            direction = 1
    print('startDistance', startDistance, dtt.distanceToTonal(startDistance), '\tdirection', direction, '\tnextMeasureDestDistance', nextMeasureDestDistance, nextMeasureDestTonal)

    # use getMicroDestination() again, this time within the measure
    microDestTonal, microDestDistance, microDestPitch = gmd.getMicroDestination(mo.Chord(destinationChords[0]), startDistance, direction)

    # we now have the startDistance, microDestDistance, and nextMeasureDestDistance
    # TODO: test melody-creation here and add final result to getNotesFugue()

    # part1notes, microDestDistance = gnf.getNotesFugue(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[0]), beats12, startDistance, microDestDistance, tenor)
    # part2notes, nextMeasureDestDistance = gnf.getNotesFugue(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]), beats34, microDestDistance, nextMeasureDestDistance, tenor)
    # notes = []
    # for note in part1notes:
    #     notes.append(note)
    # for note in part2notes:
    #     notes.append(note)
    # finalMTX[0][tenor] = [mo.Cell(mo.Chord(destinationChords[0]), mo.Chord(destinationChords[1]), beats1234, notes, nextMeasureDestDistance, tenor)]

    # first pick a rhythm for beats [1 2]
    rhythmOptions = [
        # 1 note
        ['2'],
        # 2 notes
        ['4.', '8'], ['4', '4'], ['8', '4.'],
        # 3 notes
        ['4.', '16', '16'], ['4', '8.', '16'], ['4', '8', '8'], ['8.', '16', '4'], ['8', '8~', '8', '8'], ['8', '8', '4'],
        # ['4~', '16', '8', '16'], ['4~', '16', '16', '8'], ['16', '8.', '4'],
        # 4 notes
        ['4~', '16', '16', '16', '16'], ['4', '8', '16', '16'], ['4', '16', '8', '16'], ['4', '16', '16', '8'],
        ['8.', '16', '8.', '16'], ['8.', '16', '8', '8'], ['8', '8', '8.', '16'],
        ['8', '8~', '8', '16', '16'], ['8', '8', '8', '8'], ['8', '16', '16', '4'],
        ['16', '8', '16', '4'], ['16', '16', '8', '4'],
        # ['16', '8.', '8.', '16'], ['16', '8.', '8', '8'], ['16', '8.', '16', '8.'], ['8.', '16', '16', '8.'], ['8', '8', '16', '8.'],
        # 5 notes
        ['4', '16', '16', '16', '16'], ['8.', '16', '8', '16', '16'], ['8.', '16', '16', '8', '16'], ['8.', '16', '16', '16', '8'],
        ['8', '8~', '16', '16', '16', '16'], ['8', '8', '8', '16', '16'], ['8', '8', '16', '8', '16'], ['8', '8', '16', '16', '8'],
        ['8', '16', '16~', '16', '16', '8'], ['8', '16', '16', '8.', '16'], ['8', '16', '16', '8', '8'],
        ['16', '16', '8~', '8', '16', '16'], ['16', '16', '8~', '16', '8', '16'], ['16', '16', '8~', '16', '16', '8'],
        ['16', '16', '8', '8.', '16'], ['16', '16', '8', '8', '8'],
        # 6 notes
        ['8.', '16', '16', '16', '16', '16'], ['8', '8', '16', '16', '16', '16'], ['8', '16', '16~', '16', '16', '16', '16'],
        ['8', '16', '16', '8', '16', '16'], ['8', '16', '16', '16', '8', '16'], ['8', '16', '16', '16', '16', '8'],
        ['16', '8', '16~', '16', '16', '16', '16'], ['16', '8', '16', '8', '16', '16'],
        ['16', '8', '16', '16', '8', '16'], ['16', '8', '16', '16', '16', '8'], ['16', '16', '8~', '16', '16', '16', '16'],
        ['16', '16', '8', '8', '16', '16'], ['16', '16', '8', '16', '8', '16'], ['16', '16', '8', '16', '16', '8'],
        ['16', '16', '16', '16~', '16', '8', '16'], ['16', '16', '16', '16~', '16', '16', '8'],
        ['16', '16', '16', '16', '8.', '16'], ['16', '16', '16', '16', '8', '8'],
        # ['16', '8.', '16', '16', '16', '16'], ['16', '16', '16', '16', '16', '8.'],
        # 7 notes
        ['8', '16', '16', '16', '16', '16', '16'], ['16', '8', '16', '16', '16', '16', '16'],
        ['16', '16', '8', '16', '16', '16', '16'], ['16', '16', '16', '16~', '16', '16', '16', '16'],
        ['16', '16', '16', '16', '8', '16', '16'], ['16', '16', '16', '16', '16', '8', '16'],
        ['16', '16', '16', '16', '16', '16', '8'],
        # 8 notes
        # ['16', '16', '16', '16', '16', '16', '16', '16']
    ]

    # get random rhythms. check for # of them to prevent too many/few notes
    # make sure there's at least 1 'long' note (half, dotted quarter, tied quarter, or quarter)
    counter = 0
    longNotes = 0
    while (counter > 10 or counter < 3) or (longNotes == 0):
        rhythm12 = random.choice(rhythmOptions)
        rhythm34 = random.choice(rhythmOptions)
        counter = 0
        longNotes = 0
        for i in rhythm12:
            counter += 1
            if i == '2' or i == '4.' or i == '4~' or i == '4':
                longNotes += 1
        for i in rhythm34:
            counter += 1
            if i == '2' or i == '4.' or i == '4~' or i == '4':
                longNotes += 1
        #print(longNotes)

    #print(rhythm12, rhythm34)

    # count notes to microdestinations and combine rhythms
    rhythms = []
    counter12 = 0
    counter34 = 0
    for i in rhythm12:
        rhythms.append(i)
        counter12 += 1
    for i in rhythm34:
        rhythms.append(i)
        counter34 += 1
    print(rhythms)

    # calculate distance and direction
    startTonal = dtt.distanceToTonal(startDistance)
    if startDistance == microDestDistance:
        direction = 0
        distanceBetweenTonal = 0
    elif startDistance < microDestDistance:
        direction = 1
        if startTonal < microDestTonal: # 1 to 7 = 7 - 1 = 6, 3 to 5 = 5 - 3 = 2
            distanceBetweenTonal = max(microDestTonal, startTonal) - min(microDestTonal, startTonal)
        else: # 7 to 1 = 1 - 7 + 7 = 1, 5 to 3 = 3 - 5 + 7 = 5
            distanceBetweenTonal = min(microDestTonal, startTonal) - max(microDestTonal, startTonal) + 7
    else:
        direction = -1
        if startTonal < microDestTonal: # 1 to 7 = 1 - 7 + 7 = 1, 3 to 5 = 3 - 5 + 7 = 5
            distanceBetweenTonal = min(microDestTonal, startTonal) - max(microDestTonal, startTonal) + 7
        else: # 7 to 1 = 7 - 1 = 6, 5 to 3 = 5 - 3 = 2
            distanceBetweenTonal = max(microDestTonal, startTonal) - min(microDestTonal, startTonal)

    # 50% chance to pick a common pattern
    num1 = random.random()
    if num1 < .5:
        if counter12 == distanceBetweenTonal:
            # move linearly
            pass
        elif counter12 == distanceBetweenTonal + 1:
            pass
        elif counter12 == distanceBetweenTonal + 2:
            pass
        elif counter12 == distanceBetweenTonal + 3:
            pass
        else:
            # just do random
        pass
    # otherwise just move randomly
    else:
        if rhythm12[-1] == '16':
            # make sure it moves linearly
            if rhythm12[-2] == '16':
                # make sure both move linearly
                pass
            pass
        num2 = random.random()
        # the shorter the rhythm, the higher the chance to move linearly


    # TODO: continue from here





    # TODO: add measures 2 and 3
    # small percent chance to reuse a rhythm from measure 1
    num1 = random.random()
    if num1 < .1:
        # reuse rhythm12
        pass
    elif num1 < .2:
        # reuse rhythm34
        pass
    elif num1 < .25:
        # reuse both rhythm12 and rhythm34
        pass
    else:
        # pick random rhythms
        pass

    #####################################################################
    # CREATE FILES: .ly, .xml
    #####################################################################
    #cl.createLily(music.key, music.major, finalMTX, measures, maxVoices)  # commented out while fugueWriter is being written
    # cl.createLily(key, major, finalMTX, measures, maxVoices, 2)  # second species
    # TO DO: add other species
    # not using regex so don't need this anymore, keeping for legacy
    # copyfile('newScore.ly','newScore2.ly')


    # create the pdf score
    print("Creating .pdf file(s) with LilyPond...")
    filename = "Melody.ly"
    #os.system(filename)  # commented out while fugueWriter is being written
    # time.sleep(3)

    # create the xml file
    print("Creating .xml file(s)...")
    filename = "Melody.xml"
    cx.createXML(filename, music.key, music.major, music.timesig, finalMTX, measures, maxVoices, "piano")  # note "piano" to avoid .xml organ problems with tenor = 0