Commits

dan mackinlay committed caca3e6

pluggable instantiation architecture

Comments (0)

Files changed (4)

phenosynth/controllers.sc

   
   //Instance vars are all public to aid debugging, but, honestly, don't
   //touch them. Why would you touch them?
-  var <phenoFactory;
   var <outBus;
   var <numChannels;
   var <q; //i.e. a Queue.
   var <server;
   var <all;
   var <playGroup;
-  *new {|server, phenoFactory, bus, numChannels=2, q|
-    ^super.newCopyArgs(phenoFactory, bus, numChannels, q).init(server);
+  *new {|server, bus, numChannels=2, q|
+    ^super.newCopyArgs(bus, numChannels, q).init(server);
   }
   init {|serverOrGroup|
     all = IdentityDictionary.new;
     outBus ?? {q.push({outBus = Bus.audio(server, numChannels)});};
   }
   playIndividual {|phenome|
-    all.put(phenome.identityHash, (\phenome: phenome));
+    //this doesn't actually play - it sets up a callback to play
+    q.push({
+      var indDict;
+      indDict = this.getIndividualDict(phenome);
+      this.loadIndividualDict(
+        indDict
+      );
+      this.actuallyPlay(indDict);
+    });
+  }
+  loadIndividualDict{|indDict|
+    all.put(indDict.phenome.identityHash, indDict);
+  }
+  getIndividualDict {|phenome|
+    //this doesn't need to be called in the server queue;
+    //but in general, one could so need.
+    ^(\phenome: phenome,
+      \playBus: outBus
+    );
+  }
+  actuallyPlay {|indDict|
+    q.push({
+      indDict.phenome.play(out:indDict.playBus, group:playGroup);
+    });
   }
   freeIndividual {|phenome|
     var freed = all.at(phenome.identityHash);
     all.removeAt(phenome.identityHash);
     ^freed;
   }
+}
+
+PSListenSynthController : PSController {
+  /* Handle a large number of simultaneous synths being digitally listened to
+  */
+  var <fitnessPollInterval;
+  var <listenGroup;
+  var <worker;
+  classvar <listenSynth = \ps_listen_eight_hundred;
+  *new {|server, bus, numChannels=2, q, fitnessPollInterval=1|
+    ^super.newCopyArgs(bus, numChannels, q).init(
+      server, fitnessPollInterval);
+  }
+  init {|serverOrGroup, thisFitnessPollInterval|
+    var clock;
+    super.init(serverOrGroup);
+    fitnessPollInterval = thisFitnessPollInterval;
+    q.push({listenGroup = Group.after(playGroup);});
+    clock = TempoClock.new(fitnessPollInterval.reciprocal, 1);
+    worker = Routine.new({loop {this.updateFitnesses; 1.wait;}}).play(clock);
+  }
+  getIndividualDict {|phenome|
+    ^(\phenome: phenome,
+      \playBus: Bus.audio(server, numChannels),
+      \listenBus: Bus.control(server, 1)
+    )
+  }
+  actuallyPlay {|indDict|
+    q.push({
+      indDict.phenome.play(out:indDict.playBus, group:playGroup);
+      Synth(this.class.listenSynth, this.getListenSynthArgs);
+    });
+  }
+  getListenSynthArgs{|indDict|
+    ^[\in, indDict.playBus];
+  }
+  freeIndividual {|phenome|
+    var freed = super.freeIndividual(phenome);
+    ^freed;
+  }
   updateFitnesses {
-    NotYetImplementedError.new.throw;
+    all.do({|id, phenome|
+      ['tick', id, phenome].postln;
+    });
   }
 }
 
-PSListenInstrController : PSController {
-  var <fitnessPollInterval;
-  var <listenGroup;
-  var <worker;
-  *new {|server, phenoFactory, bus, numChannels=2, q, fitnessPollInterval=1|
-    ^super.newCopyArgs(phenoFactory, bus, numChannels, q).init(
-      server, fitnessPollInterval);
-  }
-  init {|serverOrGroup, thisFitnessPollInterval|
-    super.init(serverOrGroup);
-    fitnessPollInterval = thisFitnessPollInterval;
-    q.push({listenGroup = Group.after(playGroup);});
-  }
-}
-
-//Factory instances 
-PSPhenosynthFactory {}
-
-PSInstrPhenosynthFactory : PSPhenosynthFactory {}
-
 PSServerQueue {
-  //a queue to service instructions, waiting on sync from a particular server
+  /*a queue to service instructions, waiting on sync from a particular server
+  
+  I know this looks overblown, but it sure does stop the wacky, unpredictable
+  explosions I was having before.
+  */
   var <server;
   var <fifo;
   var <worker;

phenosynth/listensynthdefs.sc

+// based on MCLD's example MCLD_Genetic tests
+
+PSBasicJudgeSynths {
+  *initClass{
+  	StartUp.add{
+  		// This judge is one of the simplest I can think of (for demo purposes) 
+  		// - evaluates closeness of pitch to a reference value (800 Hz).
+  		SynthDef.writeOnce(\ps_listen_eight_hundred, { |in, out, active=0, t_reset=0, targetpitch=800|
+  			var testsig, comparison, integral, freq, hasFreq;
+  			testsig = LeakDC.ar(Mix.ar(In.ar(in, 1)));
+  			# freq, hasFreq = Pitch.kr(testsig);
+  			comparison = hasFreq.if(
+  			  (200 - ((freq - targetpitch).abs)).max(0),
+  			  0).poll(1, \foo);
+  			 // "0" if hasFreq==false because we don't want to encourage quietness
+
+  			// Divide by the server's control rate to bring it within a sensible range.
+  			comparison = comparison / ControlRate.ir;
+			
+  			// Default coefficient of 1.0 = no leak. When t_reset briefly hits nonzero, the integrator drains.
+  			integral = Integrator.kr(comparison * active, if(t_reset>0, 0, 1));
+  			Out.kr(out, integral);
+  		});
+		}
+	}
+}

phenosynth/phenomes.sc

+PSPhenome {
+  var <chromosome;
+  var <>fitness=0;
+  var <age;
+  
+  *new{|chromosome|
+    ^super.new.init(chromosome);
+  }
+  init {|chromosome|
+    this.chromosome = chromosome;
+  }
+  chromosome_ {|newChromosome|
+    chromosome = newChromosome;
+  }
+  play {|out|
+    NotYetImplementedError.new.throw;
+  }
+}
+
+PSSynthDefPhenome : PSPhenome{
+  //A phenome mapping a chromosome to the inputs of a Synth
+  classvar <synthdef = \ps_listen_eight_hundred;
+  classvar <genomeSize = 3;
+  classvar <map;
+  *initClass {
+    Startup.add {
+      this.setUpSynthDefs;
+    }
+  }
+  setUpSynthDefs {
+    SynthDef.writeOnce(
+      \ps_listen_eight_hundred,
+      {|out=0, gate=0, t_reset=0, pitch, ffreq, rq|
+        var env;
+        var time = 1;
+        env = EnvGen.kr(
+          Env.asr(time/2, 1, time/2, 'linear'),
+          gate: gate//,
+          //doneAction: 2
+        );
+        Out.ar(Resonz.ar(
+          Saw.ar(pitch),
+            ffreq,   //cutoff
+            rq       //inverse bandwidth
+          )*env
+        );
+      }
+    );
+    map = (
+      \pitch: \midfreq.asSpec,
+      \ffreq: \midfreq.asSpec,
+      \rq: \rq.asSpec
+    );
+  }
+  play {|out, group|
+    var mappedArgs;
+    mappedArgs = this.chromosomeAsSynthArgs;
+    ^Synth.new(
+      this.class.synthdef,
+      args: [\out, out] ++ mappedArgs,
+      target: group
+    )
+  }
+  chromosomeAsSynthArgs {
+    ^all {: [keySpec[0], keySpec[1].map(val)],
+      keySpec <- map.asSortedArray,
+      val <- chromosome
+    };
+  }
+}
+  

phenosynth/playsynthdefs.sc

+PSBasicPlaySynths {
+  *initClass{
+  	StartUp.add({
+  		SynthDef.writeOnce(
+  		  \ps_listen_eight_hundred,
+  		  { |out=0, gate=0, t_reset=0, pitch=800, ffreq=500, rq=0.5|
+          var env;
+          var time = 1;
+          env = EnvGen.kr(
+            Env.asr(time/2, 1, time/2, 'linear'),
+            gate: gate//,
+            //doneAction: 2
+          );
+          Out.ar(Resonz.ar(
+            Saw.ar(pitch),
+              ffreq,   //cutoff
+              rq       //inverse bandwidth
+            )*env
+          );
+        }
+      );
+		});
+	}
+}