View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide

Cottle Chapter 8 - Additive or Fourier Synthesis, Random Numbers, Debugging and Postln, CPU usage

Home   How To   Code Pool   Public Library   Theory   Events
[Cottle examples]

8. Additive or Fourier Synthesis, Random Numbers, Debugging and Postln, CPU usage


//8.1 message chains

[30, 23, 87].choose; //choose one member of the array

20.rand; //choose a number between 0 and 20

71.midicps; //convert the midi number to cycles per second (Hz).

//Now all of them combined in several lines of code. 

a = [30, 56, 32]; //store array in variable a
b = a.choose; //choose a value from a and store it in b
c = b.rand; //choose a random number from 0 to b
d = c.midicps; //calculate the cps given c as a midi number
d	 //post the results

// .postln is now the default behavior of the interpreter. In sc2,
// you would have to state .postln explicitly to see the results.
// sc3 posts the results of the last expression automatically.

//8.2 message chain

[30, 56, 32].choose.rand.midicps;

// using postln causes the result to be printed twice. Why?
// .postln prints it once, and returns the result to the interpreter.
// Then the interpreter prints it.

[30, 56, 32].choose.rand.midicps.postln;

//8.3 Wavetable
// Wavetable editor has not been implemented in sc3 yet.
// However, wavetables can be used. A buffer has to be allocated
// and filled using methods similar to Signal methods in sc2.

s = Server.internal;
s.boot;

(
var buffer, har, harmArray;

buffer = Buffer.alloc(s, 2048, 1);	// 2048 sample buffer
harmArray = Array.series(24, 1, 1);
buffer.sine1(1 / harmArray);	// reciprocal of [1..24]: sawtooth partials

{ Osc.ar(buffer.bufnum, 200, 0, 0.5) }.scope;
)


//8.4 normalize

[1.0, 0.44, 0.49, 0.16, 0.38, 0.59, 
	0.20, 0.03, 0.11, 0.06, 0.47].normalizeSum;

//8.5 random spectra

(
{ Mix.ar(
	SinOsc.ar(
		[72, 135, 173, 239, 267, 306, 355, 473, 512, 572, 626],
		0, //phase
		[0.25, 0.11, 0.12, 0.04, 0.1, 0.15, 0.05, 0.01, 0.03, 0.02, 0.12]
	))}.scope;
)

//8.6 bell

(
{ Mix.ar(
	SinOsc.ar(
		[986, 329, 875, 498, 754, 476, 332, 354], 
		0,
		[0.6, 0.7, 0.4, 0.5, 0.7, 0.8, 0.9, 0.4].normalizeSum
	))}.scope;
)

//Random Numbers, Perception

8.7 rand

//10.rand;

10.0.rand;

//8.8 test array

var testArray;
testArray = Array.fill(12, {100.rand});
testArray;

//8.9 function error

var testArray;
testArray = Array.fill(12, 100.rand);
testArray;
 
//8.10 random seed

var testArray;
thisThread.randSeed = 5;
testArray = Array.fill(12, {100.rand});
testArray;

//Bell Array
 
//8.11 random frequencies

(
{
var harmArray, ampArray, fund; //declare two variables
	//in the first array store 12 values between 1 and 5.0
	//in the second store 12 values between 0 and 1.0, 
	//normalize the second array (ampArray) so as not
	//to exceed 1.0
fund = 200; 
harmArray = Array.fill(12, {1 + 4.0.rand}); 
ampArray = Array.fill(12, {1.0.rand}).normalizeSum;

Mix.ar(SinOsc.ar(harmArray * fund, 0, ampArray));
}.scope;

)

//Debugging, Postln, Comments

//8.12 postln, post

// What happens when you do .postln or other client-side activities
// within a ugenfunc or SynthDef function?

// New users of sc3 often expect that the server will do those things
// whenever the synth is started on the server, or that they will be
// executed repeatedly while the synth is playing. Not true.

// They are executed once, when the ugenfunc is evaluated. Then
// they are forgotten.

(
{
var harmArray, ampArray, fund; //declare two variables
	//in the first array store 12 values between 1 and 5.0
	//in the second store 12 values between 0 and 1.0, 
	//normalize the second array (ampArray) so as not
	//to exceed 1.0
fund = 200; 
harmArray = Array.fill(12, {1 + 4.0.rand}); 
ampArray = Array.fill(12, {1.0.rand}).normalizeSum;

harmArray.postln;
ampArray.postln;

Mix.ar(SinOsc.ar(harmArray * fund, 0, ampArray));
}.scope;

)

//8.13 3 bells

// As in the crotales example, we can't pass the frequency arrays
// into the synth at play time. Therefore, we must create a separate
// synthdef per frequency array.

// Make the synthdefs.
(

var fArray1, fArray2, fArray3, aArray;
var env;

//declare variables and fill the arrays with random values

fArray1 = Array.fill(12, {1 + 4.0.rand});
fArray2 = Array.fill(12, {1 + 4.0.rand});
fArray3 = Array.fill(12, {1 + 4.0.rand});
aArray = Array.fill(12, {1.0.rand}).normalizeSum;

//the perc envelope uses only an attack and a decay
env = Env.perc(0.0001, 2); 

[fArray1, fArray2, fArray3].do({ |freqs, i|	// or: arg freqs, i;
	SynthDef("ch8-bell" ++ i, {
			// again, [0, 1] here expands a mono signal into stereo
		Out.ar([0, 1], Mix.ar(
			SinOsc.ar(
				freqs * 200, 
				0, 
				aArray
			)
		)*EnvGen.kr(env, doneAction:2));
			// spawn is removed, as it is meaningless inside a synthdef
	}).send(s);
});
)

// Now, the spawn routine.
(
r = Task({
		// the 3.rand chooses one of the synthdefs randomly
		// alternate syntax: Synth.new(["ch8-bell0", "ch8-bell1", "ch8-bell2"].choose, target:s)
	inf.do({
		Synth.new("ch8-bell" ++ 3.rand, target:s);
		3.wait;
	});
}).play(SystemClock);
)

r.stop;

//8.14 arrays and math

a = [1, 5, 7, 3, 8];
(a + 34);
(a*1.23);

//Random Bell Patch

//8.15 random bells

// essentially, this must take the same form as 8.13.
// here I combine the synthdef creation and playback routine into
// the same code block. Note the beginning of the routine.

// Random bells
( 
var fArray, aArray, totalBells = 6, baseFreq = 400, totalHarm = 12;
var env, bell, nextEvent = 1.5, envAttack = 0.0001, envDecay = 1;

//create an array of arrays and store it in fArray. 
//totalBells will determine the number of possible freq arrays.
//fill each array with 24 random ratios between 1.0 and 5.0

fArray = Array.fill(totalBells, 
				{Array.fill(totalHarm, {1 + 4.0.rand})})*baseFreq;
aArray = Array.fill(12, {1.0.rand}).normalizeSum;

env = Env.perc(envAttack, envDecay); 

fArray.do({ |freqs, i|	// or: arg freqs, i;
	SynthDef("ch8-bell" ++ i, {
			// again, [0, 1] here expands a mono signal into stereo
		Out.ar([0, 1], Mix.ar(
			SinOsc.ar(
				freqs * 200, 
				0, 
				aArray
			)
		)*EnvGen.kr(env, doneAction:2));
			// spawn is removed, as it is meaningless inside a synthdef
	}).send(s);
});

r = Task({
	inf.do({
			// by starting with the .wait, I ensure there will be enough time
			// for the server to receive the synthdefs. Try reversing these
			// two lines. What is the result? Why?
		nextEvent.wait;
		Synth("ch8-bell" ++ totalBells.rand, target:s);
	});
}).play(SystemClock);
)

r.stop;

//CPU Usage

//8.16 CPU usage 

(
var fArray, aArray;

fArray = Array.series(100, 1, 1);
aArray = (1/fArray).normalizeSum;
{ Mix.ar(SinOsc.ar(fArray*200, 0, aArray)) }.scope;
)

{ Saw.ar(200, 0.5) }.scope;

//Expanded Examples 

//8.17 harmonic spectra

{ Blip.ar(100, LFNoise0.ar([20, 21], 10, 11), 0.3) }.scope;

//8.18 inharmonic spectra

(
var freqArray, env;
env = Env.perc(6, 0.1, 0.2); //envelope: (attack, decay, level)
						//level should not exceed 1.0

// this will demonstrate how to pass a frequency into a synthdef at play time.
// A very common operation when playing notes at a specific pitch. Rather important.

SynthDef("ch8-fun", { arg freq, pan;
	Out.ar(0, Pan2.ar(
		SinOsc.ar(
			freq,
			mul: EnvGen.kr(env, doneAction:2)
			//mul SinOsc by these values
			//there is a .75 percent chance that
			//the value is 0, therefor no sound
		),
		pan)
	)
}).send(s);

r = Task({
	inf.do({
			// if we choose a nonzero value, play the synth.
			// this is effectively the same as Cottle's spawn, except
			// without wasting ugens by creating a synth with amplitude 0.

		if([0, 0, 0, 0.1].choose > 0,
				// synth.head makes sure the sound happens before the scope
				// see the [Order-of-execution] helpfile for more details.
			{ Synth.head(s, "ch8-fun", [\freq, 600 + 1000.rand, //random freq between 600 and 1600
				\pan, 1.0.rand2]) }
		);
		0.3.wait;
	});
}).play(SystemClock);

s.scope;
)

r.stop;

// try substituting the following, shorter syntax for the if call above. Equivalent in functionality.
		
//		if(0.25.coin,	// 1 in 4 chance of returning "true"



Link to this Page