fix: .plot should accept minval/maxval as an array for the channels: [0,0,0]
[supercollider.git] / examples / pieces / Termite_College.scd
blobd501969229bdeec7151637f5037275c33761a90c
1 // Termite College
2 // (Tim Walters) (CC 2006)
4 // Features recursively generated phase modulation trees
6 s.serverRunning.not.if({ s.boot });
9 var pmSynthDefFactory;
10                 
11 pmSynthDefFactory = {
12         |name, out = 0, flatness = 6, maxOperators = 32, flavor = 0, attack = 4.0, release = 4.0, 
13                 sustain = 1.0, trace = false|
14         var freqFuncs, freqModFunc, depthFunc, minNumModulatorsFunc;
15         var maxNumModulatorsFunc, levelSpec, pmtree;
17         // This does timbral shifts by modulating volume of individual operators
18         depthFunc = { LinExp.kr(LFNoise1.kr(exprand(0.1, 2.0)), -1, 1, 0.1, 1.0) };
19         
20         // Three flavors
21         freqFuncs = [
22                 { exprand(0.125, 4000) },
23                 { exprand(40, 4000) },
24                 { exprand(80, 500) }
25         ];
26         
27         // Destabilize oscillators a bit.
28         // Would need a LinExp for proper tuning, but here
29         // we don't care.
30         freqModFunc = { BrownNoise.kr.range(0.998, 0.998.reciprocal) };
31         
32         // This could be something more interesting
33         minNumModulatorsFunc = {
34                 |level|
35                 1
36         };
37         
38         // Tree thins out slowly with increasing depth
39         maxNumModulatorsFunc = {
40                 |level|
41                 (level.reciprocal.sqrt * flatness).asInteger + 1;
42         };
43         
44         // Generate tree of given size. If trace is true, dumps representation to post window.
45         pmtree = {
46                 |level, size, trace = false|
47                 var numModulators, numSubOperators, modGroup, thisUnitDepth, freq, freqMod;
48                 var output, newSizes;
49                 
50                 // Keep root oscillator in normal note range
51                 freq = (level == 1).if(
52                         { exprand(80, 500) },
53                         { freqFuncs.wrapAt(flavor).value }
54                 );
55                 trace.if({
56                         (level - 1).do({ "-".post });
57                         freq.postln;
58                 });
59                 freqMod = freqModFunc.value;
60                 numSubOperators = size - 1;
61                 
62                 // Keep root oscillator at appropriate volume (don't modulate)
63                 thisUnitDepth = (level == 1).if(
64                         { -18.dbamp * AmpComp.kr(freq) }, 
65                         { depthFunc.value }
66                 );
67                 
68                 // Choose number of tree branches, based on current level
69                 numModulators = (
70                         minNumModulatorsFunc.value(level)..maxNumModulatorsFunc.value(level)
71                 ).choose.min(numSubOperators);
72                 
73                 // Recursively generate subtrees, dividing up size among them.
74                 (size == 0).if({
75                         output = 0
76                 }, {
77                         (numModulators == 0).if({
78                                 modGroup = 0
79                         }, {
80                                 newSizes = 0 ! numModulators;
81                                 numSubOperators.do({
82                                         var index;
83                                         index = rrand(0, numModulators - 1);
84                                         newSizes[index] = newSizes[index] + 1;
85                                 });
86                                 modGroup = Mix.ar({
87                                         |i|
88                                         pmtree.value(level + 1, newSizes[i], trace) 
89                                 } ! numModulators);
90                         });
91                         
92                         // Return sine oscillator with subtrees summed as phase modulator
93                         output = SinOsc.ar(freq * freqMod, modGroup, thisUnitDepth);
94                 });
95                 output
96         };
97         
98         // Generate synthdef from tree. Use audio rate panning for stereo, 
99         // with modulated width.
100         SynthDef.new(name ? \PMTree, {
101                 |attack = 4.0, release = 4.0, sustain = 1.0, gate = 1.0|
102                 var env;
103                 env = EnvGen.kr(Env.asr(attack, sustain, release, curve: [4, -4]), gate, doneAction: 2);
104                 Out.ar(out, Pan2.ar(
105                         pmtree.value(1, maxOperators, trace), 
106                         SinOsc.ar(freqFuncs.wrapAt(flavor).value, 0, depthFunc.value, rrand(-0.5, 0.5)),
107                         env
108                 ));
109         })
112 ~synths = Set[];
114 ~effectBus = Bus.audio(s, 2);
116 // Add some reverb.
117 ~effect = {
118         |gate = 1.0|
119         var env;
120         env = EnvGen.ar(Env.asr(0, 1.0, 16.0), gate, doneAction: 2);
121         GVerb.ar(
122                 // Tame those pesky upper mids.
123                 BPeakEQ.ar(In.ar(~effectBus.index, 2), 4000.0, 0.5, -6),
124                 roomsize: 50, 
125                 earlyreflevel: -9.dbamp, 
126                 taillevel: -12.dbamp,
127                 mul: env
128         )
129 }.play;
131 // Player generates and plays synths of random size and flavor.
132 ~player = Task({
133         var synthName, polyphony;
134         polyphony = 4;
135         inf.do({
136                 |i|
137                 var targetNumSynths;
138                 synthName = ("PMTree" ++ (i % 100).asString).asSymbol;
139                 targetNumSynths = rrand(1, polyphony - 1);
140                 {~synths.size > targetNumSynths}.while({
141                         ~synths.remove(~synths.choose.release);
142                 });
143                 ~synths.add(
144                         pmSynthDefFactory.value(
145                                 synthName, 
146                                 ~effectBus.index, 
147                                 exprand(2.0, 9.0).asInteger, 
148                                 exprand(4.0, 33.0).asInteger,
149                                 [0, 0, 0, 0, 1, 2].choose,
150                                 exprand(1.0, 8.0), 
151                                 exprand(4.0, 16.0), 
152                                 4.0
153                         ).play
154                 );
155                 exprand(12.0, 36.0).wait;
156         });
159 ~player.start;
163 // Execute to finish.
164 ~player.stop;           
165 ~synths.do({ |synth| ~synths.remove(synth.release) });
166 ~effect.release;
170 // Execute after sound has completely died away.
171 ~effectBus.free;