Fran├žois Pinot 192000 1920 2 192000 1920 2 true false false false true true true true -d false false true true true true -d -f false false 0 false cfm idur = p3 iamp = p4 ifq1a = p5 ifq1b = p6 ifq2a = p7 ifq2b = p8 inx1a = p9 inx1b = p10 inx2a = p11 inx2b = p12 icpsa = p13 icpsb = p14 ifn1 = p15 ifn2 = p16 iphs1 = p17 iphs2 = p18 kfrq1 line ifq1a, idur, ifq1b kfrq2 line ifq2a, idur, ifq2b kndx1 line inx1a, idur, inx1b kndx2 line inx2a, idur, inx2b kcps line icpsa, idur, icpsb kenv linen iamp, 0.05, idur, 0.2 a1, a2 crossfm kfrq1, kfrq2, kndx1, kndx2, kcps, ifn1, ifn2, iphs1, iphs2 outs a1*kenv, a2*kenv cpm idur = p3 iamp = p4 ifq1a = p5 ifq1b = p6 ifq2a = p7 ifq2b = p8 inx1a = p9 inx1b = p10 inx2a = p11 inx2b = p12 icpsa = p13 icpsb = p14 ifn1 = p15 ifn2 = p16 iphs1 = p17 iphs2 = p18 kfrq1 line ifq1a, idur, ifq1b kfrq2 line ifq2a, idur, ifq2b kndx1 line inx1a, idur, inx1b kndx2 line inx2a, idur, inx2b kcps line icpsa, idur, icpsb kenv linen iamp, 0.05, idur, 0.2 a1, a2 crosspm kfrq1, kfrq2, kndx1, kndx2, kcps, ifn1, ifn2, iphs1, iphs2 outs a1*kenv, a2*kenv cfmpm idur = p3 iamp = p4 ifq1a = p5 ifq1b = p6 ifq2a = p7 ifq2b = p8 inx1a = p9 inx1b = p10 inx2a = p11 inx2b = p12 icpsa = p13 icpsb = p14 ifn1 = p15 ifn2 = p16 iphs1 = p17 iphs2 = p18 kfrq1 line ifq1a, idur, ifq1b kfrq2 line ifq2a, idur, ifq2b kndx1 line inx1a, idur, inx1b kndx2 line inx2a, idur, inx2b kcps line icpsa, idur, icpsb kenv linen iamp, 0.05, idur, 0.2 a1, a2 crossfmpm kfrq1, kfrq2, kndx1, kndx2, kcps, ifn1, ifn2, iphs1, iphs2 outs a1*kenv, a2*kenv false 0.0 1 Master 0.0 false false 2 Master 0.0 false false 3 Master 0.0 false false Master Master 0.0 false false ;sine wave f 1 0 16384 10 1 ;pseudo square wave f 2 0 16384 10 1000 0 333.333 0 200 0 142.857 0 111.111 0 90.909 \ 0 76.923 0 66.667 0 58.824 0 52.632 0 ;pseudo triangle wave f 3 0 16384 10 1000 0 111.111 0 40 0 20.408 0 12.346 0 8.264 0 \ 5.917 0 4.444 0 3.46 0 2.77 0 ;pseudo sawtooth wave f 4 0 16384 10 1000 500 333.333 250 200 166.667 142.857 125 111.111 \ 100 90.909 83.333 76.923 71.429 66.667 62.5 58.824 \ 55.556 52.632 50 0dbfs = 1 csound -Wdo devaudio -L stdin false false 2.0 0.0 root -10066279 0 true 30 0 true 1.0 0 5 3.0 0.0 Comment -12566464 Implementation of a Simple Genetic Algorithm to explore the parameter space of the crossfm opcodes. The SGA Classes soundObject is a PythonObject including the SGA classes. It is a generic SGA and can be used to explore other orchestras. A Genetic Algorithm is initiated with the following instruction: ga = SGA(params, popSize, crossoverProbability, mutationProbability) where params is a list of Allele objects. The other arguments have default values when not specified: popSize defaults to 20, crossoverProbability defaults to 0.6, and mutationProbability defaults to 0.0333. A new generation of the SGA is calculated by calling its setFitness() method, and then its action() method. The decoded values from the calculation are get through the getValues() method. ________________________________ The Parameters coding soundObject is an ObjectBuilder. Its graphical interface allows the user to set the parameters coding for the crossfm exploration. The scripting part of the object defines the Parameters class which is a list of Allele objects corresponding to the parameters defined in the graphical interface. The Parameters class has a newNote() method used to yield a new note from an Individual values. ________________________________ The UI soundObject is an ObjectBuilder. Its graphical interface allows the user to control the SGA evolution, and to set the fitness values for each sound through each pass. The scripting part of the object shows how we use the persistence of the Jython environment during a blue session to pass information from one generation of the SGA to the other one. You first have to select the Parameters Coding soundObject and choose the settings you want. Then you select the UI soundObject. Each time you press the play button, one of three possibilities happens. If the Init box is checked, a new SGA is initiated with its first generation randomly calculated. If the Init box is unchecked and the New gen box is unchecked, you ear again the sounds of the last calculated generation. This is useful to set correctly the fitness values (slider controls). Finally, if the Init box is unchecked and the New gen box is checked, a new generation is calculated by the SGA depending on the fitness values set by the user. Note: the first time you hit the play button, the Init box MUST be checked, otherwise you will get a Jython error. This is due to the fact that when you open the cfm_ga.blue file, the ga object does not exist within the Jython memory space, and the only way to create a new ga object is to hit the play button while the UI Init box is checked. 4.0 0.0 SGA Classes -12566464 0 # # Copyright (C) 2009 Francois Pinot # # This code is free software; you can redistribute it # and/or modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this code; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # import random class Sga(object): """Simple genetic algorithm. Adapted from Genetic Algorithms, by David E. Goldberg Addison-Wesley USA, (15767), 1991 """ def __init__(self, alleles, popSize=20, pCross=0.6, pMut=0.0333): self.popSize = popSize self.pCross = pCross self.pMut = pMut self.nCross = 0 self.nMut = 0 self.oldPop = Population(self.popSize, alleles) self.newPop = Population(self.popSize, alleles) def incCross(self): self.nCross += 1 def incMut(self): self.nMut += 1 def getCross(self): return self.nCross def getMut(self): return self.nMut def setCrossProb(self, prob): if (prob >= 0 and prob <= 1.0): self.pCross = prob def setMutProb(self, prob): if (prob >= 0 and prob <= 1.0): self.pMut = prob def generation(self): """Create a new generation through select, crossover, and mutation. Note: an even-numbered popSize is assumed """ self.oldPop.statistics() for i in xrange(0, self.popSize, 2): mate1 = self.oldPop.select() mate2 = self.oldPop.select() child1 = self.newPop[i] child2 = self.newPop[i+1] child1.setParent1(mate1) child1.setParent2(mate2) child2.setParent1(mate1) child2.setParent2(mate2) cp1 = mate1.getChromosome() cp2 = mate2.getChromosome() cc1 = child1.getChromosome() cc2 = child2.getChromosome() lchrom = len(cc1) if (random.random() <= self.pCross): jcross = random.randint(1, lchrom-1) self.nCross += 1 else: jcross = lchrom child1.setCrossPoint(jcross) child2.setCrossPoint(jcross) # 1st exchange, 1 to 1 and 2 to 2 for j in xrange(jcross): if (random.random() <= self.pMut): cc1[j] = 1 - cp1[j] self.nMut += 1 else: cc1[j] = cp1[j] if (random.random() <= self.pMut): cc2[j] = 1 - cp2[j] self.nMut += 1 else: cc2[j] = cp2[j] # 2nd exchange, 1 to 2 and 2 to 1 for j in xrange(jcross, lchrom): if (random.random() <= self.pMut): cc1[j] = 1 - cp2[j] self.nMut += 1 else: cc1[j] = cp2[j] if (random.random() <= self.pMut): cc2[j] = 1 - cp1[j] self.nMut += 1 else: cc2[j] = cp1[j] child1.setValues() child2.setValues() def flipPop(self): tempPop = self.oldPop self.oldPop = self.newPop self.newPop = tempPop def action(self): self.generation() self.flipPop() def getValues(self): ret = [] for ind in self.oldPop: ret.append(ind.getValues()) return ret def setFitness(self, val): if len(val) < len(self.oldPop): return i = 0 for ind in self.oldPop: ind.setFitness(val[i]) i += 1 class Allele(object): """Character in a Chromosome. Allows the decoding of the character. """ def __init__(self, min=0.0, max=0.0, length=1, rounded=False, fixed=False): self.fixed = fixed self.rounded = rounded self.offset = min if self.fixed: self.length = 0 self.coef = 0.0 else: self.length = int(length) self.coef = (max - min) / (2.0 ** length) def getLength(self): return self.length def isFixed(self): return self.fixed def value(self, val=[]): if self.fixed: v = self.offset else: accum = 0.0 powerof2 = 1.0 for i in val: if i == 1: accum += powerof2 powerof2 *= 2.0; v = accum * self.coef + self.offset if self.rounded: v = round(v) else: v = round(v, 3) return v class Chromosome(list): """Includes all the characters explored by the GA.""" def __init__(self, alleles): self.alleles = alleles length = 0 for a in self.alleles: length += a.getLength() for i in xrange(length): if random.random() >= 0.5: self.append(1) else: self.append(0) def decode(self): ret = [] i = j = 0 for a in self.alleles: v = [] if not a.isFixed(): j += a.getLength() v = self[i:j] i = j ret.append(a.value(v)) return ret class Individual(object): """A member of the population treated by the GA.""" def __init__(self, alleles): self.parent1 = self.parent2 = None self.fitness = 0.0 self.chrom = Chromosome(alleles) self.setValues() self.xsite = 0 def getChromosome(self): return self.chrom def getFitness(self): return self.fitness def setFitness(self, val): self.fitness = val def getParent1(self): return self.parent1 def setParent1(self, ind): self.parent1 = ind def getParent2(self): return self.parent2 def setParent2(self, ind): self.parent2 = ind def getValues(self): return self.values def setValues(self): self.values = self.chrom.decode() def getCrossPoint(self): return self.xsite def setCrossPoint(self, n): self.xsite = n class Population(list): "A population treated by the GA.""" MAXPOP = 100 def __init__(self, size=0, alleles=[]): self.sumFitness = 0.0 self.max = self.avg = self.min = 0.0 if (size <= 0 or size > Population.MAXPOP): size = Population.MAXPOP for i in xrange(size): ind = Individual(alleles) self.append(ind) def select(self): """Select a single individual via roulette wheel selection.""" partsum = 0.0 rand = random.random() * self.sumFitness for ind in self: partsum += ind.getFitness() if (partsum >= rand): return ind return self[-1] def statistics(self): self.sumFitness = self.min = self.max = 0.0 for ind in self: fitness = ind.getFitness() self.sumFitness += fitness if (fitness > self.max): self.max = fitness if (fitness < min): self.min = fitness self.avg = self.sumFitness / len(self) def getAverage(self): return self.avg def getBestFitness(self): return self.max def showStats(self, n, nmut, ncross): self.statistics() print "Gen %d max=%f, min=%f, avg=%f, sum=%f, mut=%d, ncross=%d" % \ (n, self.max, self.min, self.avg, self.sumFitness, nmut, ncross) 5.0 0.0 Parameters coding -12566464 0 # # Copyright (C) 2009 Francois Pinot # # This code is free software; you can redistribute it # and/or modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this code; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # class Parameters(list): def __init__(self, orcNum=1, dt=1.0): self.append(Allele(<durMin>, <durMax>, <durLen>, <durRound>, <durConst>)) self.append(Allele(<frq1aMin>, <frq1aMax>, <frq1aLen>, <frq1aRound>, <frq1aConst>)) if <Frq1bOn>: self.append(Allele(<frq1bMin>, <frq1bMax>, <frq1bLen>, <frq1bRound>, <frq1bConst>)) self.append(Allele(<frq2aMin>, <frq2aMax>, <frq2aLen>, <frq2aRound>, <frq2aConst>)) if <Frq2bOn>: self.append(Allele(<frq2bMin>, <frq2bMax>, <frq2bLen>, <frq2bRound>, <frq2bConst>)) self.append(Allele(<inx1aMin>, <inx1aMax>, <inx1aLen>, <inx1aRound>, <inx1aConst>)) if <Inx1bOn>: self.append(Allele(<inx1bMin>, <inx1bMax>, <inx1bLen>, <inx1bRound>, <inx1bConst>)) self.append(Allele(<inx2aMin>, <inx2aMax>, <inx2aLen>, <inx2aRound>, <inx2aConst>)) if <Inx2bOn>: self.append(Allele(<inx2bMin>, <inx2bMax>, <inx2bLen>, <inx2bRound>, <inx2bConst>)) self.append(Allele(<cps_aMin>, <cps_aMax>, <cps_aLen>, <cps_aRound>, <cps_aConst>)) if <Cps_bOn>: self.append(Allele(<cps_bMin>, <cps_bMax>, <cps_bLen>, <cps_bRound>, <cps_bConst>)) self.append(Allele(<fun1Min>, <fun1Max>, <fun1Len>, <fun1Round>, <fun1Const>)) self.append(Allele(<fun2Min>, <fun2Max>, <fun2Len>, <fun2Round>, <fun2Const>)) self.orcNum = orcNum self.dt = dt self.t = 0.0 self.template = "i %d %f %f 0.5 %f %f %f %f %f %f %f %f %f %f %d %d 0 0 \n" def clearDate(self): self.t = 0.0 def newNote(self, values): v = values[:] if not <Frq1bOn>: v.insert(2, v[1]) if not <Frq2bOn>: v.insert(4, v[3]) if not <Inx1bOn>: v.insert(6, v[5]) if not <Inx2bOn>: v.insert(8, v[7]) if not <Cps_bOn>: v.insert(10, v[9]) s = self.template % tuple([self.orcNum, self.t] + v) self.t += (v[0] + self.dt) return s false 13 51 110 22 239 22 355 22 durMin 87 50 5 100 durMax 218 50 10 100 durLen 342 50 13 100 durRound 461 47 false false 20 95 frq1aMin 87 94 55 100 frq1aMax 218 94 1760 100 frq1aLen 342 94 24 100 frq1aRound 461 91 false false 20 124 frq1bMin 87 123 55 100 frq1bMax 218 123 1760 100 frq1bLen 342 123 24 100 frq1bRound 461 120 false false 20 175 frq2aMin 87 173 55 100 frq2aMax 218 173 1760 100 frq2aLen 342 173 24 100 frq2aRound 461 170 false false 20 204 frq2bMin 87 203 55 100 frq2bMax 218 203 1760 100 frq2bLen 342 203 24 100 frq2bRound 461 199 false false 21 262 inx1aMin 86 261 0 100 inx1aMax 217 261 5 100 inx1aLen 341 261 17 100 inx1aRound 461 258 false false 21 291 inx1bMin 86 290 0 100 inx1bMax 217 290 5 100 inx1bLen 341 290 17 100 inx1bRound 461 287 false false 21 343 inx2aMin 86 342 0 100 inx2aMax 217 342 5 100 inx2aLen 341 342 17 100 inx2aRound 461 339 false false 21 374 inx2bMin 86 373 0 100 inx2bMax 217 373 5 100 inx2bLen 341 373 17 100 inx2bRound 461 370 false false 20 424 cps_aMin 85 423 1 100 cps_aMax 216 423 1 100 cps_aLen 340 423 0 100 cps_aRound 461 420 false false 23 504 fun1Min 85 503 1 100 fun1Max 216 503 4 100 fun1Len 340 503 2 100 fun1Round 461 500 true false 23 534 fun2Min 85 533 1 100 fun2Max 216 533 4 100 fun2Len 340 533 2 100 fun2Round 461 530 true false durConst 558 47 true false frq1aConst 558 91 false false frq1bConst 558 120 false false frq2bConst 558 199 false false frq2aConst 558 170 false false inx1bConst 558 287 false false inx1aConst 558 258 false false inx2bConst 558 370 false false inx2aConst 558 339 false false cps_aConst 558 420 true false fun2Const 558 530 true false fun1Const 558 500 true false 743 49 743 68 743 88 743 107 743 127 743 156 743 192 743 212 743 233 743 254 20 455 cps_bMin 85 454 -2 100 cps_bMax 216 454 2 100 cps_bLen 340 454 16 100 cps_bRound 461 451 false false cps_bConst 558 451 false false Frq1bOn 634 120 false false Frq2bOn 634 199 false false Inx1bOn 634 287 true false Inx2bOn 634 370 true false Cps_bOn 634 451 false false 743 277 743 299 743 321 743 343 Python 100.0 0.0 UI -12566464 0 # # Copyright (C) 2009 Francois Pinot # # This code is free software; you can redistribute it # and/or modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this code; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # def notes(ga, p): values = ga.getValues() p.clearDate() s = "" for v in values: s += p.newNote(v) return s if <initFlag>: params = Parameters(<orchNum>, <dtField>) ga = Sga(params) elif <newGenFlag>: ga.setFitness([<fitness_0>, <fitness_1>, <fitness_2>, <fitness_3>, <fitness_4>, <fitness_5>, <fitness_6>, <fitness_7>, <fitness_8>, <fitness_9>, <fitness_10>, <fitness_11>, <fitness_12>, <fitness_13>, <fitness_14>, <fitness_15>, <fitness_16>, <fitness_17>, <fitness_18>, <fitness_19>]) ga.action() score = notes(ga, params) false fitness 21 21 true 0.0 1.0 0.05 300 5 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true 0 0 true 0.0 1.0 0.05 0.0 300 true newGenFlag 97 395 false false initFlag 32 395 true false orchNum 227 395 cfm 1 cpm 2 cfmpm 3 0 false 303 399 424 399 dtField 444 399 0 60 Python true 0.0 -1.0 false false false