intro to CrucialLibrary for SC Server

//      intro to CrucialLibrary for SC Server
//      _version: 180102, written 031209
// _tested on sc3.3, sc3.4, sc3.9

// note: to make it work with supercollider versions newer than 3.4,
//one need to install the cruciallib quark.  the library is no longer
//included with the standard sc distribution.

Quarks.install("crucial-library");
//then recompile

//--

//this library is made by crucial (a.k.a. felix/timeblind).  it's huge and
//this tutorial will only scratch a bit on the surface.
//check helpfiles for more info.

//the two main concepts of this library are instruments and patches,
//here called [Instr] and [Patch].  an Instr is defined once and
//automatically loaded into the global Library.  an Instr can be
//accessed from anywhere and is most often used in combo with
//Patch.  Patch logically plays and combines instruments.

//at startup and after recompile the Library is usually pretty empty.
Library.postTree;

//anything can be stored in Library, but from here on we will
//concentrate on Instr.  the following line will create and save an
//Instr in Library at address \test.
Instr(\test, {BrownNoise.ar(0.1)});

//again dump content of Library.  it should list our test instrument
//under Instr.
Library.postTree;

//to play the Instr at address \test, look it up in the Library using
//the .at method and then .play.
s.boot;
a= Instr.at(\test).play;
a.stop

//this line will overwrite our Instr with another UGen graph.
Instr(\test, {WhiteNoise.ar(0.1)});
a= Instr.at(\test).play;
a.stop

//so the Library is sort of a global repository for Instr.  the Instr
//address can also be multi level like in the following example.
//note the brackets.
Library.clear;
Instr([\testNoises, \brown], {BrownNoise.ar(0.1)});
Instr([\testNoises, \pink], {PinkNoise.ar(0.1)});
Instr([\testNoises, \white, \mono], {WhiteNoise.ar(0.1)});
Instr([\testNoises, \white, \stereo], {WhiteNoise.ar([0.1, 0.1])});
Library.postTree;

//multi level addressed Instr are accessed in this way...
a= Instr.at([\testNoises, \pink]).play;
a.stop
a= Instr.at([\testNoises, \white, \stereo]).play
a.stop
a= Instr.at([\testNoises, \white, \mono]).play
a.stop

//--

//when calling an Instr object it first searches the Library.  if no
//matching name is found, it tries to load a file with the same name
//as the Instr address from the current Instr.dir folder.  this is
//Document.dir++"Instr/" by default, which usually means a folder
//called Instr inside your main supercollider application directory.
//you can of course override this classvar as you like by pointing it
//to another folder.
Library.clear;
Instr.dir= "~/Documents/SuperCollider/myInstrFolder";

//now put a rtf, txt or scd file called "nameless" containing a valid
//Instr into the directory specified above.  the Instr in the file could
//look like this: Instr(\nameless, {SinOsc.ar([400,404])});
//if nothing is found Instr.at(\nameless) nil is returned.
a= Instr.at(\nameless).play     //(try twice - breaks the first time)
a.stop

//--

//to make more interesting Instr you will need arguments.  here is
//an extended version of the testNoise Instr with two arguments.
Instr(\testNoise, {|freq= 1000, mul= 0.5| LPF.ar(WhiteNoise.ar(mul), freq)});

//relying on argument defaults will set noise amplitude to 0.5 and
//low-pass filter's frequency to 1000
a= Instr.at(\testNoise).play;
a.stop

//(btw, as you may have noted there is no need for Out or In when
//using Instr.  CrucialLibrary is smart and takes care of it for you.
//this also makes porting old SC2 code fairly easy.)

//there are a few different ways of supplying arguments.  version 1:
a= {Instr.at(\testNoise).ar(250, 1)}.play;
a.free

//supplying arguments version 2:
a= {Instr.ar(\testNoise, [6000, 0.2])}.play;
a.free

//it is also possible to modulate inlets with other ugens!
a= {Instr.at(\testNoise).ar(FSinOsc.kr(1, 0, 500, 1000), 0.4)}.play;
a.free

//so as you see Instr also works within functions like {}.play and
//SynthDef.  but then you do need an Out etc.
(
SynthDef(\testNoiseDef, {
        Out.ar(0, Instr.at(\testNoise).ar(650, 1));
}).add
)
a= Synth(\testNoiseDef)
a.free

//anyway, now we can play many instances of the same Instr - all
//with their individual settings - at the same time.
(
a= {
        Mix.ar([
                Instr.ar(\testNoise, [FSinOsc.kr(1.2, 0, 100, 500), 0.25]),
                Instr.ar(\testNoise, [9000, LFPulse.kr(2, 0, 0.2, 0.1)]),
                Instr.ar(\testNoise, [150])
        ]);
}.play
)
a.free

//--

//Patch takes an Instr as an argument and plays it.  normally you
//don't play Instr directly like we have above.  create a Patch object
//and supply an Instr name argument instead.
Instr(\testSaw, {Saw.ar(100, 0.1)});    //create an Instr
a= Patch(\testSaw).play;        //play the Instr
a.stop

//and with arguments...  note the array.
Instr(\testSaw, {|freq, mul| Saw.ar(freq, mul)});
a= Patch(\testSaw, [50, 0.1]).play;
a.stop

//as shortcut and for testing, soundfunctions can also be directly
//written in a Patch like this...  but that will actually build an Instr
//with a random name.
a= Patch({|freq, mul| Saw.ar(freq, mul)}, [150, 0.1]).play;
a.stop

//--

//the following lines creates two Instr, one effect and one sound
//source, and then patches them together.
Instr(\testNoise, {WhiteNoise.ar(0.2)});
Instr(\sweepfilter, {|audio| LPF.ar(audio, LFTri.kr(0.3, 0, 300, 800))});
a= Patch(\sweepfilter, [Patch(\testNoise)]).play
a.stop

//this will create one noisy Instr and two simple effects
(
Instr(\testNoise2, {|mul= 1.0| BrownNoise.ar(mul)});
Instr([\efx, \lowpass], {|audio, freq= 1000| LPF.ar(audio, freq)});
Instr([\efx, \delay], {|audio, delaytime= 1|
        audio+CombN.ar(audio, 2, delaytime, 2)});
)

//let us first play the above running a repeating burst of noise
//through a lowpass effect.  cutoff frequency is controlled with the
//mouse.
(
var source, cutoff;
source= Patch(\testNoise2, [Patch({LFPulse.kr(2, 0, 0.1, 0.3)})]);
cutoff= Patch({MouseX.kr(60, 20000, 'exponential')});
a= Patch([\efx, \lowpass], [source, cutoff]).play
)
a.stop

//secondly we add the delay Instr
(
var source, cutoff;
source= Patch(\testNoise2, [Patch({LFPulse.kr(2, 0, 0.1, 0.3)})]);
cutoff= Patch({MouseX.kr(60, 20000, 'exponential')});
a= Patch([\efx, \delay], [
        Patch([\efx, \lowpass], [source, cutoff]),
        0.4     //delaytime in seconds
]).play
)
a.stop

//last let us run the above example through another instance of the
//delay with a very short delaytime (0.01).  i.e. same delay Instr
//used twice but with different settings.
(
var source, cutoff;
source= Patch(\testNoise2, [Patch({LFPulse.kr(2, 0, 0.1, 0.3)})]);
cutoff= Patch({MouseX.kr(60, 20000, 'exponential')});
a= Patch([\efx, \delay], [
        Patch([\efx, \delay], [
                Patch([\efx, \lowpass], [source, cutoff]),
                0.4     //delaytime in seconds
        ]),
        0.01    //delaytime in seconds
]).play
)
a.stop

//--

//multiple channels are easy to deal with when using Instr/Patch.
//here the argument Patch with two Mouse objects will expand the
//SinOsc Patch into two.
(
a= Patch({|freq= 400| SinOsc.ar(freq, 0, 0.1)}, [
        Patch({[
                MouseX.kr(60, 20000, 'exponential'),
                MouseY.kr(60, 20000, 'exponential')
        ]})
]).play
)
a.stop

//and a stereo effect with a mono Patch argument will work in the
//same way.
(
a= Patch({|audio| audio*LFNoise0.kr([3, 2], 0.5, 0.5)}, [
        Patch({SinOsc.ar(350, 0, 0.3)}) //mono sound source
]).play
)
a.stop

//also Crucial's NumChannels and Mono classes are sometimes
//useful.  here mixing down from 7 to 2 channels.
(
var n= 7;
a= Patch({|audio|
        NumChannels.ar(
                audio*LFNoise0.kr(
                        Array.fill(n, {3.0.rrand(9.0).round(1.667)}),
                        0.4,
                        0.3
                ).max(0.0),
                2
        )
}, [
        Patch({SinOsc.ar(Array.series(n, 90, 90), 0, 0.1)})
]).play
)
a.stop

//--

//then there is PlayerMixer for mixing patches
(
Instr(\testSine, {|freq= 1000, mul= 0.1| SinOsc.ar(freq, 0, mul)});
a= PlayerMixer([
        Patch(\testSine, [400, 0.1]),
        Patch(\testSine, [600, 0.08]),
        Patch(\testSine, [1000, 0.06])
]);
)
a.play
a.stop

//--

//and almost everything in this library can easily be gui:fied
a.gui;  //playermixer from above

Library.postTree;
Library.clear;