GAPmatch a parameter estimation system that uses genetic algorithms


Inherits from: Object


GAPmatch is a fast parameter estimation system that intends to work at near-interactive speeds to aid sound design and maybe live performances. To achieve this, it uses a fast search technique called "Genetic Algorithm" which mimics the evolutionary continuum that we observe in nature. It wraps a GAWorkbench instance for a specific purpose.


In terms of its functionality in the SC environment, parameter estimation means this: You have a SynthDef that is  known to be capable of synthesizing a particular type of sound, but finding the right parameters is a time consuming and in some situations a tedious task. Or you have built a raw SynthDef for a synthesis algorithm which exposes a few dozens of parameters and it's impossible to conduct them at that state. Or you are trying to tune a physical modelling instrument... Situations can vary. This parameter estimation system basically takes an input sound, an input SynthDef and its parameter ranges, and it tries to find the parameters for this particular synthesizer so that the output of the synth is faithful to the attributes of your input sound. From that stage on, you can alter the  parameters by yourself to change the synthesized sound in various ways.


Execution of genetic algorithms demands heavy computing resources, and this class can parallelize the task across multiple scservers on your local machine and networked machines.


An article that describes the inner workings of the system in depth will be provided when I have the permission to do so.


(You need to have BatUGens and BatPVUgens installed from sc3-plugins for this to work)


See also: GAWorkbench


Creation / Class Methods


*new (argPoolSize, argSynthDef, argSampleLoc, argSynthParams, argParamSpace, argNumThreads, argServers)

All arguments except argServers must be provided.

argPoolSize - Pool size for the genetic algorithm (Integer). See GAWorkbench.

argSynthDef - The SynthDef whose parameter space is to be searched (SynthDef).

argSampleLoc - Path for the input sample whose attributes you are trying to match (String).

argSynthParams - Array of Symbols. The argument names of the provided SynthDef.

argParamSpace - This should be a function, returning an array of values (most likely in a random range) that match with the parameters provided with argSynthParams (i.e. in the same order). For example, if you have a hypothetical synth with two parameters with names "freq" and "amp", your argSynthParams should be [\freq, \amp] and argParamSpace might be { [rrand(10, 8000), rrand(0.0, 1.0)] }. The random ranges you provide for the parameters will directly influence the complexity of the problem (area of the search space), so if you can provide meaningful ranges for the parameters, you will find a good solution sooner.

argNumThreads - Number of simultaneous synthesizers to run and test on a single server. If you don't provide any servers, the default server will be used, so set this value to a number f synth instances you can run simultaneously on your computer. This value cannot be bigger than argPoolSize as fitness of only one generation of parameters can be tested simultaneously before the system applies other genetic operators such as crossover and mutation (Integer).

argServers - An Array of Servers. If you provide this argument, the instance will parallelize the work across all the provided servers. Make sure that:

* argNumThreads is evenly divisible by number of supplied servers,

* argPoolSize isn't smaller than (argNumThreads * argServers.size),

* argPoolSize is evenly divisible by (argNumThreads * argServers.size)

Don't worry if you make an error, in such a case, the class will post an error and stop.



Accessing Instance and Class Variables

tLatency_(arg1)

tLatency

The system runs the synthesizer and the source sound simultaneously and a third synth compares the similarity these two sounds to provide data for the GA algorithm. For accurate analysis, accurate timing for spawning these synths is necessary, so the class makes a bundle and sends them to the server with the same timestamp to make sure they are created simultaneously. This member variable defines the scheduling latency, if you happen to see "late" messages on your post window, consider increasing this value. Default is 0.1.

lastGen

Returns an array of parameters (values in argParamSpace order). These are the parameters for the latest generation and ordered from fittest to weakest. instance.lastGen[0] therefore is the fittest parameter set at any time.

defaultBuffer

The input sound buffer. You can call .play on it to hear the source sound.

gaInstance

The GAWorkbench instance this class wraps. See its help to access the internals.

Doing Some Task (optional)


startJob

Once everything is initialized, call this on the instance to start the GA search operation.


pauseJob

Pauses the GA search.


resumeJob

Resumes the GA search.


playBest

Plays the supplied synth with the best parameters available (lastGen[0]). If you are running the GA search on your local computer and if there is heavy CPU load, you might want to call .pauseJob prior to listening, or you will hear dropouts.


cleanUp

Frees all the server side resources the instance uses (buffers, busses, groups etc).


showGui

Shows the GUI, makes things easier.


hideGui

Hides the GUI.



Examples


//don't forget to provide your own sample below!

//(preferably an e-drum type sound, but can be other things to try)


s.boot; //boot the default server


(//2 local servers

~servers = 2.collect({|cnt| Server.new("s"++cnt, NetAddr("localhost", 51000 + cnt), Server.default.options); });

fork

({

~servers.do

({|srv|

srv.boot;

1.wait;

});

~servers.do(_.makeWindow);

}, clock: AppClock);

)


(

~sDef = SynthDef(\dSynth,

{//an electronic drum synthesizer

arg outBus, bodyIFreq = 100, bodyEFreq = 45, sweepTime = 0.1, 

bodyAttack = 0.001, bodySustain = 0.1, bodyRelease = 0.3, pMod = 0.15,

noiseAmp = 4, noiseDecay = 0.07, noiseFreq = 200, noiseRQ = 0.15,

distMix = 0.1, distAmt = 5;

var bodyEnv, fed, body, noise, noiseEnv, finOut;

var t_ktrig = Impulse.ar(0);

bodyEnv = EnvGen.ar(Env([0.1, 0.8, 0.7, 0.1], [bodyAttack, bodySustain, bodyRelease], \exponential), t_ktrig) - 0.1;

noiseEnv = EnvGen.ar(Env([0.1, noiseAmp, 0.1], [0.001, noiseDecay], \exponential), t_ktrig) - 0.1;

fed = LocalIn.ar(1);

body = SinOsc.ar

(

EnvGen.ar(Env([0, bodyIFreq, bodyEFreq], [0, sweepTime]), t_ktrig), 

(fed * pMod).fold(0, 2*pi), 

bodyEnv

);

noise = BPF.ar(PinkNoise.ar(1) * noiseEnv, noiseFreq, noiseRQ);

LocalOut.ar(body + noise);

finOut = SelectX.ar(distMix.clip(0,1), [(body + noise), ((body + noise) * (1 + distAmt)).tanh]).dup;

DetectSilence.ar(finOut, doneAction: 2);

Out.ar(outBus, finOut.mean);

});


if(a.species == GAPmatch, { a.cleanUp; });


a = GAPmatch.new

(

600, //poolSize

~sDef, //our clueless synthdef

"~/Desktop/sounds/kick.wav".standardizePath, //provide your sample here

[

\bodyIFreq,

\bodyEFreq,

\sweepTime,

\bodyAttack,

\bodySustain,

\bodyRelease,

\pMod,

\noiseAmp,

\noiseDecay,

\noiseFreq,

\noiseRQ,

\distMix,

\distAmt

], //synth parameters

{[

rrand(1, 500), //bodyIFreq

rrand(1, 500), //bodyEFreq

rrand(0, 2.0), //sweepTime

exprand(1e-5, 0.5), //bodyAttack

exprand(1e-5, 1), //bodySustain

exprand(1e-5, 1), //bodyRelease

rrand(0, 1.0), //pMod

rrand(0, 20.0), //noiseAmp

exprand(1e-3, 4), //noiseDecay

exprand(1, 15e3), //noiseFreq

rrand(0.01, 0.99), //noiseRQ

rrand(0, 1.0), //distMix

rrand(0, 20.0), //distAmt

]}, //random values for parameters limited in a range (function returning array)

100, //number of threads per server

~servers //our servers

);

)