def _expandTree(self, parent, lowestDurationLevel, durationLevels, durationSubdivisions, barDuration, currentLevel): # return up the stack if we're at the lowest duration level if (lowestDurationLevel - currentLevel) < 0: return parentLevel = currentLevel - 1 # calculate duration of child subdivisionsParent = durationSubdivisions[parentLevel] parentDuration = parent.getDuration() duration = parentDuration / subdivisionsParent # create as many children as the number of subdivisions of the parent for _ in range(subdivisionsParent): child = RhythmTree(duration, currentLevel) parent.addChild(child) child.assignMetricalAccent(parent, currentLevel) self._expandTree(child, lowestDurationLevel, durationLevels, durationSubdivisions, barDuration, currentLevel + 1) return parent
def testScoresAreModifiedCorrectlyForDensity(): d1 = RhythmTree(1, 0) d2 = RhythmTree(1, 1) d3 = RhythmTree(1, 2) candidates = [d1, d2, d3] scores = [0.8, 0.6, 0.5] r.densityImpact = 1 expectedScores = [ 0.10000000000000002, 0.13333333333333333, 0.16666666666666666 ] newScores = r._modifyScoresForDensity(candidates, scores, 3) assert newScores == expectedScores
def testCalcScoreDistHarmonicTactusReturnsCorrectScores(): d1 = RhythmTree(1, 0) d2 = RhythmTree(1, 1) d3 = RhythmTree(1, 2) d4 = RhythmTree(1, 3) candidates = [d1, d2, d3, d4] expectedScores = [0.4, 0.6, 1, 0.6] r.densityImpact = 0 r.entropyImpact = 0 scores = r._calcDistFromTactusMetric(candidates, 2) assert scores == expectedScores
def testRhythmicSpaceIsCreatedCorrectly(): m = Metre("3/4", "quarternote") rsf = RhythmTreeFactory() rs2 = rsf.createRhythmTree(2, m) rs = RhythmTree(3, 0, [RhythmTree(1, 1, [RhythmTree(0.5, 2), RhythmTree(0.5, 2)]), RhythmTree(1, 1, [RhythmTree(0.5, 2), RhythmTree(0.5, 2)]), RhythmTree(1, 1, [RhythmTree(0.5, 2), RhythmTree(0.5, 2)])]) assert str(rs) == str(rs2)
def _expandNode(self, lowestDurationLevel, currentLevel, parent): numSubdivisions = 2 # return up the stack if we've reached the desired depth if (lowestDurationLevel - currentLevel) < 0: return parentDuration = parent.getDuration() duration = parentDuration / numSubdivisions for _ in range(2): child = RhythmTree(duration, currentLevel) parent.addChild(child) # assign metrical accent to child child.assignMetricalAccent(parent, currentLevel) self._expandNode(lowestDurationLevel, currentLevel + 1, child)
def _createChildren(self, parent, number, noteDuration, durationLevel): """Creates a number of children with a note duration and duration level Args: parent (RhythmTree): Node we want to add the children to number (int): Number of children to be added noteDuration (float): Note duration of children durationLevel (float): duration level of children Returns: parent (RhythmTree) """ parentMetricalAccent = parent.getMetricalAccent() # create children, add them to parent and assign them a metrical accent for i in range(number): child = RhythmTree(noteDuration, durationLevel) if i == 0: child.setMetricalAccent(parentMetricalAccent) else: child.setMetricalAccent(durationLevel) parent.addChild(child) return parent
def createRhythmTree(self, lowestDurationLevel, metre, highestDurationLevel=0): """Instantiate and returns a rhythm space tree Args: lowestDurationLevel (int): Lowest duration level of the rhythm space to be created metre (Metre): Metre object """ startLevel = highestDurationLevel timeSignature = metre.getTimeSignature() duration = RhythmTreeFactory.getDurationAtDurationLevel( timeSignature, startLevel) # instantiate root level of rhythm tree rhythmTree = RhythmTree(duration, startLevel) rhythmTree.setMetricalAccent(startLevel) rhythmTree.setLowestDurationLevel(lowestDurationLevel) durationLevels = metre.getDurationLevels() durationSubdivisions = metre.getDurationSubdivisions() if startLevel == lowestDurationLevel: return rhythmTree # expand root rhythmTree = self._expandTree(rhythmTree, lowestDurationLevel, durationLevels, durationSubdivisions, duration, startLevel + 1) return rhythmTree
def _insertTriplet(self, parent): # add extra child to parent parentDurationLevel = parent.getDurationLevel() child = RhythmTree(1, parentDurationLevel + 1) child.setMetricalAccent(parentDurationLevel + 1) parent.addChild(child) tripletItems = parent.getChildren() tripletItemDuration = parent.calculateTupletItemDuration(3) # change duration of children for item in tripletItems: item.setDuration(tripletItemDuration) lowestDurationLevel = parent.getLowestDurationLevel() startLevel = parentDurationLevel + 2 # expand triplet self._modifyTripletItemsDurations(parent, parentDurationLevel) self._expandNode(lowestDurationLevel, startLevel, parent.children[2])
def testInsertTriplet(): rsf = RhythmTreeFactory() m = Metre("4/4", "quarternote") rs = rsf.createRhythmTree(3, m) rs1 = RhythmTree(2, 0, [RhythmTree(1, 1, [RhythmTree(0.5, 2), RhythmTree(0.5, 2)]), RhythmTree(1, 1, [RhythmTree(0.5, 2), RhythmTree(0.5, 2)])]) rsf._insertTriplet(rs.children[0]) assert len(rs.children[0].children) == 3 for child in rs.children[0].children: assert 0.66 < child.duration < 0.67
def testRhythmicSpaceIsInstantiatedCorrectly(): root = RhythmTree(1, 2) assert root.duration == 1 assert root.durationLevel == 2
def testChordProgressionsAreGeneratedCorrectly(): hpg = HarmonyPitchGenerator() rs1 = RhythmTree(3.5, 0) rs2 = RhythmTree(0.5, 0) rs3 = RhythmTree(2, 0) rs4 = RhythmTree(2, 1) rs1.setMetricalAccent(0) rs2.setMetricalAccent(2) rs3.setMetricalAccent(0) rs4.setMetricalAccent(1) harmonicRhythm = [rs1, rs2, rs3, rs4] progression = hpg.generateHarmonyPitchMU(harmonicRhythm, 0.7, 0.7, "musicunit") print([x.pitchSet for x in progression])
def testChordProgressionsForCadencesAreCreatedCorrectly(): rs1 = RhythmTree(4, 1) rs2 = RhythmTree(3, 1) rs3 = RhythmTree(2, 1) rs1.setMetricalAccent(1) rs2.setMetricalAccent(2) rs3.setMetricalAccent(3) hpg = HarmonyPitchGenerator() cadence = hpg._createCadence([rs1, rs2, rs3]) assert len(cadence) == 2
def testBackboneNotesAreGeneratedCorrectly(): c1 = Chord("0+-") r1 = RhythmTree(1, 1) r1.metricalAccent = 0 n1 = Note(r1, c1) c2 = Chord("5-+") r2 = RhythmTree(1, 1) r2.metricalAccent = 2 n2 = Note(r2, c2) c3 = Chord("7+--") r3 = RhythmTree(1, 1) r3.metricalAccent = 1 n3 = Note(r3, c3) c4 = Chord("0-+") r4 = RhythmTree(1, 1) r4.metricalAccent = 0 n4 = Note(r4, c4) backboneNotes = [n1, n2, n3, n4] notes = mbg.generateBackbonePitches(backboneNotes, pitchHeight=-0.5, pitchRange=0, melodicComplexity=0) assert len(notes) == 4 s = mbg._realizeM21Sequence(notes)