def getMicroDestination(currentChord, startDistance, direction=1): # could also just use defineChord() pitches = currentChord.getPitches() # get starting pitch startPitch = dtp.distanceToPitch(startDistance) # remove starting pitch from pitches list - is this necessary? #pitches.remove(startPitch) # pick a random pitch remaining in the chord # TODO: consider not making this random chosenPitch = random.choice(pitches) # convert to tonal chosenTonal = ptt.pitchToTonal(chosenPitch) # start at bass chosenDistance = ptd.pitchToDistance(chosenPitch, 0) # reset to 0 chosenDistance -= 24 # move into range - assumes while chosenDistance < startDistance: chosenDistance += 12 # fix if direction is -1 if direction == -1: chosenDistance -= 12 return chosenTonal, chosenDistance, chosenPitch
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 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 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 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