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
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
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
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
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)
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