def _createCadence(self, harmonicRhythm): """Applies a stock cadence Args: harmonicRhythm (list): List of RhythmTree objects Returns: cadenceChordProgression (list): List of Chord objects """ random = RandomManager.getActive() # choose cadence to apply cadences = self.chordProfile.getCadences() #TODO: Choose cadence in more intelligent way cadence = random.choice(cadences) scale = self.chordProfile.getScale().getName() cadenceChordProgression = [] reversedHarmonicRhythm = reversed(harmonicRhythm[:]) # create as many cadence for count, durationObj in enumerate(reversedHarmonicRhythm): duration = durationObj.getDuration() code = cadence[-count+1] chord = Chord(code, duration=duration, scale=scale, octave=4) if count >= len(cadence): return cadenceChordProgression # prepend chord cadenceChordProgression.insert(0, chord) return cadenceChordProgression
def generateHarmonicRhythmMU(self, metre, numBarsMU, densityImpact=None): """Generates a harmonic rhythm sequence for a MU Args: metre (Metre): harmonicDensityImpact (float): Arousal feature that influences the density of the harmony numBarsMU (int): Number of bars in a MU """ # if we need to change the densityImpact, do so on the parent class which is how it's used if densityImpact is not None: super(HarmonyRhythmGenerator, self).setDensityImpact(densityImpact) rhythmicSeq = [] # decide whether to use bar as a repeated pattern random = RandomManager.getActive() r = random.random() if r < self._probabilityRepeatBar: rhythmicSeqBar = self._generateHarmonicRhythmBar(metre) rhythmicSeq = [] for i in range(numBarsMU): rhythmicSeq += rhythmicSeqBar else: # create harmonic rhythm for each bar for i in range(numBarsMU): rhythmicSeqBar = self._generateHarmonicRhythmBar(metre) rhythmicSeq += rhythmicSeqBar #print() #print(rhythmicSeq) return rhythmicSeq
def _decideShape(self, numBackboneNotes): """Decides contour shapes for a backbone Args: numBackboneNotes (int): Number of notes of the backbone Returns: shape (list): List of "UPs" and "DOWNs" that determine the shape of the melodic contour """ type = self.type typeTemplate = self._availableTypes[type] random = RandomManager.getActive() # manage "ascending" and "descending" contour types if type == "ascending" or type == "descending": shape = typeTemplate * (numBackboneNotes - 1) # manage "arch" and "invertedArch" types elif type == "arch" or type == "invertedArch": if type == "arch": shape = [UP, DOWN] else: shape = [DOWN, UP] numBackboneNotesLeft = numBackboneNotes - (len(shape) + 1) # insert motions if we still have backbone notes if numBackboneNotesLeft: # decide how many notes reuse the first motion firstMotionProlongations = random.randrange( numBackboneNotesLeft) # insert motions between first and last motions for i in range(numBackboneNotesLeft): # case we reuse the last motion if i > firstMotionProlongations: # insert motion in penultimate position shape.insert(-1, shape[-1]) # case we reuse the first motion else: # insert motion in penultimate position shape.insert(-1, shape[0]) # manage "random" type else: shape = [] # choose up and down motions randomly for _ in range(numBackboneNotes - 1): t = random.choice([UP, DOWN]) shape.append(t) return shape
def gaussSampling(minVal, maxVal, mean, sigma): random = RandomManager.getActive() s = random.gauss(mean, sigma) if s < minVal: return minVal elif s > maxVal: return maxVal else: return s
def _decideToApplyDot(self, rhythmTree, maxDepth): """Decides whether to apply a dot either single or double. Args: rhythmTree (RhythmTree): Chosen rhythm space node Returns: newDuration (list): Pair duration, 't' (symbol for tie), if tie gets applied numDots (int): Number of dots applied """ duration = rhythmTree.getDuration() durationLevel = rhythmTree.getDurationLevel() random = RandomManager.getActive() r = random.random() numDots = 0 # decide whether to apply a dot if r <= self._probabilityDot[durationLevel] and maxDepth >= 1: # decide which type of dot to apply #TODO need to verify that we're using melodrive's random manager when we integrate random = RandomManager.getActive() r2 = random.random() # handle single dot #TODO: this is confusing, but I get why you did it this way (compactness) if r2 <= self._probabilitySingleDot[durationLevel] or maxDepth < 2: numDots = 1 duration = self._calcDotDuration(duration, numDots) return [duration, None], numDots # handle double dot else: numDots = 2 duration = self._calcDotDuration(duration, numDots) return [duration, None], numDots # handle case in which no dots were applied else: return [duration, None], numDots
def _decideToApplyTie(self, rhythmicSeqElement, durationLevel): """Decides whether to apply tie and adds a 't' to the duration Args: rhythmicSeqElement (list): [duration, None] - None indicates no tie durationLevel (int): Metrical level of chosen rhythm space node Returns: newDuration (list): Pair duration, 't' (symbol ofr tie), if tie gets applied """ duration = rhythmicSeqElement[0] random = RandomManager.getActive() r = random.random() if r <= self._probabilityTie[durationLevel]: return [duration, 't'] else: return [duration, None]
def generateMelodicRhythmMU(self, metre, numBarsMU): random = RandomManager.getActive() rhythmicSeq = [] # decide whether to generate pickup if random.random() < self._additionalMUmaterial["pickup"]["prob"]: pickupSeq = self._generateAdditionalBar(metre, "pickup") rhythmicSeq.append(pickupSeq) # generate core bars for _ in range(numBarsMU): barSeq = self._generateMelodicRhythmBar(metre) rhythmicSeq.append(barSeq) # decide whether to generate prolongation if random.random() < self._additionalMUmaterial["prolongation"]["prob"]: prolongationSeq = self._generateAdditionalBar(metre, "prolongation") rhythmicSeq.append(prolongationSeq) print() print(rhythmicSeq) return rhythmicSeq
def addTupletsToRhythmTree(self, parent, probTuplets, probTupletType): """Inserts tuplets in the rhythm space tree Args: probTuplet (list): Prob of having a tuplet at different duration levels probTupletType (dict): Prob of having different types of tuplets at different duration levels Returns: newTree (RhythmTree): Rhythm space with tuplets """ lowestDurationLevel = parent.getLowestDurationLevel() currentLevel = parent.getDurationLevel() metricalAccent = parent.getMetricalAccent() random = RandomManager.getActive() # return up the stack if we're at the penultimate lowest duration level if (lowestDurationLevel - currentLevel) < 1: return parent # decide whether to insert tuplets r = random.random() if r < probTuplets[currentLevel][metricalAccent]: # decide which type of tuplets to insert tupletType = self._decideTupletType(probTupletType, currentLevel) self.insertTuplet(parent, tupletType) return parent children = parent.getChildren() for child in children: self.addTupletsToRhythmTree(child, probTuplets, probTupletType) return parent
def decideCumulativeDistrOutcomeDict(distr): random = RandomManager.getActive() r = random.random() return getCumulativeDistrOutcomeDict(r, distr)
def generateHarmonyPitchMU(self, harmonicRhythm, harmonicComplexity, minMajRatio, structureLevelMU): """Generates a chord progression for a harmonic rhythm sequence Args: harmonicRhythm (list): List containing RhythmTree objects harmonicComplexity (float): Value of emotional feature minMajRatio (float): Value of emotional feature structureLevelMU (str): Returns: chordProgression (list): List of Chord objects """ # decide whether to have cadence cadenceProbDict = self.chordProfile.getCadenceProb() cadenceProb = cadenceProbDict[structureLevelMU] random = RandomManager.getActive() r = random.random() cadenceChordProgression = [] if r <= cadenceProb: cadenceChordProgression = self._createCadence(harmonicRhythm) if len(cadenceChordProgression) == len(harmonicRhythm): return cadenceChordProgression # remove as many durations from harmonicRhythm as the # number of chords used for the cadence harmonicRhythm = harmonicRhythm[:-len(cadenceChordProgression)] chordProgression = [] previousTriadCode = None chordIndex = None # step through all durations forming the harmonic rhythm to assign # chord for durationObj in harmonicRhythm: duration = durationObj.getDuration() scale = self.chordProfile.getScale().getName() metricalAccentLevel = durationObj.getMetricalAccent() # calculate scores scores = self._calcMetrics(previousTriadCode, chordIndex, metricalAccentLevel, harmonicComplexity, minMajRatio) # choose triad chord, chordIndex = self._decideTriad(scores, durationObj) code = chord.getCode() # get probability of adding dissonant thirds dissonanceProb = self._calcDissonanceProb(harmonicComplexity, metricalAccentLevel) r = random.random() # decide whether to apply dissonance if r <= dissonanceProb: # add dissonance(s) code = self._decideDissonance(chord) # create new chord newChord = Chord(code, duration=duration, scale=scale, octave=4) # append chord to progression chordProgression.append(newChord) # update previous code and triad code previousCode = newChord.getCode() previousTriadCode = previousCode[:3] # add up chord progression and chords for cadence chordProgression += cadenceChordProgression s = self._realizeM21Sequence(chordProgression) s.show("midi") return chordProgression