sclang: array primitives - respect mutability when changing object.
[supercollider.git] / examples / research_and_tools / SeminaireMusical.scd
blob233bd6002ae76f3cb9b5dc9f0e569bc464da348d
2 // SŽminaire Musical Final 2007
3 // julian rohrhuber
4 // renate wieser
7 var dec, v_spiel, v_klang, v_neu, v_play, v_guess, v_display, v_sargam;
8 var f_setButtons, f_neu, f_play, f_guess, f_playSargam, f_freeSargam;
9 var spiel="sm_sine", freqs, index;
10 var intervalle, freq0, note0, rates, nameList, amp=0.5, sargamSynth;
12 var mode=\european, allowBase=true;
13 var eurNotes, indNotes, chords, chordKeys;
15 eurNotes = #["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "Bb", "B", "C'"];
16 indNotes = #["Sa", "re", "Re", "ga", "Ga", "Ma", "ma", "Pa", "dha", "Dha", "ni", "Ni", "Sa'"];
19 chords = IdentityDictionary[
20         'major' -> #[0, 2, 4],
21         'minor' -> #[0, 2b, 4],
22         '7th' -> #[0, 2, 4, 6b],
23         'minor7' -> #[0, 2b, 4, 6b],
24         'sixth' -> #[0, 2, 4, 5],
25         'minor6' -> #[0, 2b, 4, 5],
26         'aug' -> #[0, 2, 4s],
27         'aug7' -> #[0, 2, 4s, 6b],
28         'dim' -> #[0, 2b, 4b],
29         'dim7' -> #[0, 2b, 4b, 6bb],
30         '7th 5b' -> #[0, 2, 4b, 6b],
31         'min7 5b' -> #[0, 2b, 4, 6b],
32         'ninth' -> #[0, 2, 4, 6b, 8],
33         'minor9' -> #[0, 2b, 4, 6b, 8],
34         'major9' -> #[0, 2, 4, 6, 8], 
35         '11th' -> #[0, 2, 4, 6b, 8, 10],
36         'dim9' -> #[0, 2, 5, 8],
37         'added9' -> #[0, 2, 4, 8],
38         'added4' -> #[0, 2, 4, 10],
39         'sus' -> #[0, 3, 4],
40         'sus9' -> #[0, 1, 4],
41         '7 sus4' -> #[0, 3, 4, 6b],
42         '7 sus9' -> #[0, 1, 4, 6b],
43         'fifth' -> #[0, 4]
45 chordKeys = chords.keys.asArray;
47 // GUI
49 w = Window("seminaire_musical", Rect(10, 250, 750, 250), false);
51 w.view.decorator = dec = FlowLayout(w.view.bounds);
52 dec.shift(20, 20);
53 v_spiel = ListView(w, Rect(0,0, 100, 130))
54         /*.items_(
55                 ["noten", "frequenzen", "noten + oktave","intervalle","times","beatings", "akkorde"]
56         );*/
57         .items_(
58                 ["notes", "frequencies", "notes + octaves","intervals","times","beatings", "chords"]
59         );
60 dec.shift(5);
61 v_klang = ListView(w, Rect(0,0, 100, 120)).items_(["sinus", "noise", "pulse", "bell", "piano", "timbre"]);
62 dec.shift(20);
63 v_neu = Button(w, Rect(0,0, 70, 35)).states_([["next try", Color.black, Color.clear]]);
64 v_play = Button(w, Rect(0,0, 50, 35)).states_([["play", Color.black, Color.clear]]);
65 SCSlider(w, Rect(0,0, 200, 35)).value_(0.5).action_({ |v| amp = v.value });
66 Button(w, Rect(0,0, 90, 18))
67         .states_([["boot server", Color.black, Color.clear]]).action_({ Server.default.boot });
69 dec.shift(-425, 50);
71 Button(w, Rect(0,0, 70,20))
72         .states_([["europe","india","numerical"], Color.black, Color.clear].flop)
73         .action_({ |v| mode = [\european, \indian, \numerical][v.value.asInteger]; f_neu.value; });
75 v_sargam = Button(w, Rect(0,0, 50, 20))
76         .states_([["sargam", Color.black, Color.clear], ["sargam", Color.red, Color.clear]]);
78 Button(w, Rect(0,0, 180, 20))
79         .states_([["including low tones", Color.black, Color.clear],["excluding low tones", Color.black, Color.clear]])
80         .action_({ |v|
81                 allowBase = v.value < 1;
82         });
83         
84 v_display = StaticText(w, Rect(0,0,80,20)); //.font_(Font("Helvetica", 18));
85 dec.shift(300, -80);
89 dec.nextLine;
90 dec.shift(20, 30);
92 StaticText(w, Rect(0,0, 300,20))
93         .string_("find the corresponding value here:")
94         .align_(\left);
96 dec.nextLine;   
97 dec.shift(20, 0);
99 v_guess = Array.fill(13, {
100         Button(w, Rect(0,0, 50, 35)).font_(Font("Monaco", 9));
104 w.front;
106 // funktionen
108 f_setButtons = { arg werte;
109         v_guess.do { |but, i|
110                 but.states_([[werte[i].asString, Color.black, Color.clear]]);
111                 but.refresh;
112         };
113         
116 f_setButtons.("" ! 13);
117 f_neu = {
118         var sargamIsPlaying, minFreq, octaves;
119         sargamIsPlaying = v_sargam.value > 0;
120         if(allowBase) { 
121                 minFreq = 60;
122                 octaves = #[0.5, 1, 2, 4];
123         } { 
124                 minFreq = 200;
125                 octaves = #[1, 2, 4];
126         };
127         
128         index = 13.rand;
129         switch(v_spiel.value,
130         0, {
131                 if(mode == \indian) {
132                         
133                         freqs = #[240, 256, 270, 288, 300, 320, 337.5, 360, 384, 400, 432, 450, 480];
134                         // see: http://www.soundofindia.com/showarticle.asp?in_article_id=-446619640
135                         nameList = indNotes; 
136                 } {
137                         freqs = ((0..12) + 60).midicps; 
138                         if(mode == \numerical) { nameList = (0..12) } {ÊnameList = eurNotes };
139                 };
140                 freq0 = freqs[0];
141                 
142         },
143         1, {
144                 nameList = {Êexprand(minFreq, 15000).round(1) } ! 13;
145                 freqs = nameList;
146                 freq0 = 440;
147         },
148         2, {
149                 if(mode == \indian) {
150                         freqs = #[240, 256, 270, 288, 300, 320, 337.5, 360, 384, 400, 432, 450, 480];
151                         nameList = indNotes;
152                 } { 
153                         freqs = ((0..12) + 60).midicps; 
154                         if(mode == \numerical) { nameList = (0..12) } { nameList = eurNotes };
155                 };
156                 freq0 = freqs[0];
157                 freqs = freqs * ({ octaves.choose } ! 12);
158                 
159                 
160         },
161         3, {
162                 if(mode == \european) {
163                         freq0 = ((0..12).choose + 60).midicps * octaves.choose;
164                         intervalle = (1..12).midiratio.insert(9, 7/4); // insert 7/4
165                         freqs = freq0 * intervalle;
166                         nameList = 
167                 #["-sekund", "+sekund", "-terz", "+terz", "quart", "tritone", "quint", 
168                         "-sext", "+sext", "7/4", "-sept", "sept", "oktav"
169                 ];
170                 } {
171                         freq0 =  #[240, 256, 270, 288, 300, 320, 337.5, 360, 384, 400, 432, 450].choose;
172                         freq0 = freq0 * octaves.choose;
173                         intervalle = [16/15, 9/8, 6/5, 5/4, 4/3, 17/12, 3/2, 8/5, 5/3, 7/4, 16/9, 15/8, 2/1];
174                         freqs = freq0 * intervalle;
175                         nameList = 
176                 #["16/15", "9/8", "6/5", "5/4", "4/3", "17/12", "3/2", "8/5", "5/3", "7/4",
177                         "16/9", "15/8", "2/1"];
178                 };
179         },
180         4, {
181                 freq0 = rrand(1600, 8000);
182                 freqs = freq0 ! 13;
183                 rates = { exprand(0.9, 19) } ! 13;
184                 nameList = rates.round(0.01);
185                 
186         },
187         5, {
188                 freq0 = ((0..11).choose + 60).midicps * octaves.choose;
189                 freqs = freq0 ! 13;
190                 rates = { exprand(0.9, 40) } ! 13;
191                 nameList = rates.round(0.01);
192                 
193         },
194         6, {
195                 note0 = (0..11).choose + #[-12, 0].choose;
196                 freq0 = (note0 + 60).midicps;
197                 freqs = freq0 ! 13;
198                 nameList = chordKeys[ (0..chordKeys.size-1).scramble.keep(13)].sort;
199         
200         });
201         f_setButtons.(nameList);
202         f_freeSargam.value;
203         if(sargamIsPlaying) { f_playSargam.value };
206 f_neu.value; // init
208 f_play = { |i|
209         switch(v_spiel.value,
210         3, { // intervall
211                         fork {
212                                 Synth(spiel, [\freq, freq0, \amp, amp]);
213                                 0.4.wait;
214                                 Synth(spiel, [\freq, freq0 * intervalle[i], \amp, amp]);
215                         }
216                 },
217         4, { // rates
218                         Synth("sm_click", [\freq, freq0, \rate, rates[i], \amp, amp])
219                 },
220         5, { // beatings
221                         if(spiel.postln == "sm_string") {
222                                 Synth("sm_string", [\freq, freq0,  \amp, amp]);
223                                 Synth("sm_string", [\freq, freq0 + rates[i], \amp, amp])
224                         } {
225                                 Synth("sm_beatings", [\freq, freq0, \rate, rates[i], \amp, amp])
226                         }
227                 },
228         6, { // akkorde
229                 fork {
230                         var strum = [0, 0.04].choose.rand;
231                         var notes = chords[nameList[i]].degreeToKey(#[0, 2, 4, 5, 7, 9, 11]) + note0;
232                         notes.do { |val|
233                                 Synth(spiel, [\freq, (val + 60).midicps, \amp, amp * 0.5]);
234                                 strum.wait;
235                         }
236                 }
237         
238         },
239                 {
240                         Synth(spiel, [\freq, freqs[i], \amp, amp])
241                 }
242         )
243         
246 f_playSargam = {Êv_sargam.valueAction = 1 };
247 f_freeSargam = {Êv_sargam.valueAction = 0 };
249 // funktionen zu buttons
250 v_neu.action = f_neu;
251 v_play.action = { f_play.value(index) };
252 v_spiel.action = f_neu;
253 v_guess.do {|but, i|
254         but.action = { 
255                 if(index == i) {
256                         but.states = [[but.states[0][0], Color.red, Color.yellow]];
257                 };
258                 f_play.value(i);
259                 v_display.string = "note:" + (freqs[i].cpsmidi - 60).round(0.1);
260         };
262 v_klang.action = {|view| 
263         spiel = 
264 ["sm_sine", "sm_noise", "sm_pulse", "sm_bell", "sm_string", "sm_timbremix"][view.value.asInteger]
266 v_sargam.action = { |view| 
267         if(view.value == 0) 
268                 { sargamSynth.release } { sargamSynth = Synth(\sm_sargam, [\freq, freq0, \amp, amp]) 
269         };
272 w.view.keyDownAction = { arg view, char;
273         f_play.value(char.ascii - 48 % 13);
276 w.onClose = { f_freeSargam.value };
278 // synthdefs
280 SynthDef("sm_sine", { arg freq=440, amp=0.5;
281         amp = AmpComp.kr(freq) * amp;
282         Out.ar(0,
283                 Pan2.ar(
284                         SinOsc.ar(freq) * EnvGen.kr(Env.perc(0.03, Rand(0.3, 2), amp), doneAction:2),
285                         Rand(-0.5, 0.5)
286                 )
287         )
289 }).add;
291 SynthDef("sm_noise", { arg freq=440, amp=0.5;
292         var u;
293         amp = AmpComp.kr(freq) * amp;
294         u = BPF.ar(PinkNoise.ar(20), freq, Rand(0.1, 0.005));
295         Out.ar(0,
296                 Pan2.ar(
297                         u * EnvGen.kr(
298                                 Env.linen(Rand(0.03, 0.2), Rand(0.3, 2), Rand(0.3, 0.8), amp), 
299                                 doneAction:2
300                         ),
301                         Rand(-0.5, 0.5)
302                 )
303         )
305 }).add;
307 SynthDef("sm_pulse", { arg freq=440, amp=0.5;
308         var u;
309         amp = AmpComp.kr(freq) * amp;
310         u = Pulse.ar(freq, Rand(0.3, 0.5), 0.3);
311         Out.ar(0,
312                 Pan2.ar(
313                         u * EnvGen.kr(Env.perc(0.03, Rand(0.3, 2), amp), doneAction:2),
314                         Rand(-0.5, 0.5)
315                 )
316         )
318 }).add;
320 SynthDef("sm_bell", { arg freq=440, amp=0.5;
321         n = 5;
322         amp = AmpComp.kr(freq) * amp;
323         Out.ar(0,
324                 Pan2.ar(
325                         Klang.ar(`[
326                                 [1] ++ ({ rrand(1.2, 5.8) } ! (n-1)), 
327                                 [1] ++ ({ rrand(0.1, 0.3) } ! (n-1)).sort.reverse,
328                         { pi.rand } ! n
329                         ], freq)
330                          * EnvGen.kr(Env.perc(0.03, Rand(0.3, 2), amp * 0.5), doneAction:2),
331                 Rand(-0.5, 0.5)
332                 )
333         )
335 }).add;
338 SynthDef("sm_string", { arg freq=440, amp=0.5;
339         var u, exc, sustain;
340         amp = AmpComp.kr(freq) * amp;
341         exc = LFNoise2.ar(3000, Decay2.ar(Impulse.ar(0.0001, 0, 0.5), 0.006, 0.02));
342         sustain = Rand(1, 1.5);
343         u = LPF.ar(
344                 CombN.ar(exc, 0.04, (freq * (1+[0, 0.001])).reciprocal, sustain).sum,
345                 min(20000, freq * Rand(3, 2.3) ! 2)
346         ).sum;
347         
348         //DetectSilence.ar(u, doneAction:2);
349         amp = amp * EnvGen.kr(Env.linen(0, sustain, 0.1), doneAction:2);
350         Out.ar(0,
351                 Pan2.ar(u * amp, freq.cpsmidi % 12 / 6 - 1)
352         );
354 }).add;
357 SynthDef("sm_click", { arg freq=440, rate=1, amp=0.5;
358         var u;
359         amp = AmpComp.kr(freq) * amp;
360         u = SinOsc.ar(freq) * Decay.kr(Impulse.kr(rate, 0, 40), 0.002, 0.04);
361         Out.ar(0,
362                 Pan2.ar(
363                         u * EnvGen.kr(Env.linen(0, min(2, IRand(4, 8) / rate), 0.1, amp), doneAction:2),
364                         Rand(-0.5, 0.5)
365                 )
366         )
368 }).add;
370 SynthDef("sm_beatings", { arg freq=440, rate=1, amp=0.5;
371         var u;
372         amp = AmpComp.kr(freq) * amp;
373         u = SinOsc.ar([0, rate] + freq).sum * 0.3;
374         Out.ar(0,
375                 Pan2.ar(
376                         u * EnvGen.kr(Env.linen(0.01, max(Rand(0.3, 2),  3 / rate), 0.5, amp), doneAction:2),
377                         Rand(-0.5, 0.5)
378                 )
379         )
381 }).add;
383 SynthDef("sm_sargam", { arg freq=440, amp=0.5, gate=1.0;
384         var u;
385         amp = AmpComp.kr(freq) * amp * 0.5;
386         u = Pulse.ar(freq * [0.5, 1] +.t [Rand(0.2, 0.5), Rand(0.2, 0.5).neg], 
387                 LFNoise1.kr(0.11, 0.05, 0.4), 0.15).sum;
388         u = RLPF.ar(u, LFNoise2.kr(0.3, 0.2, 1) * 5000, 0.5);
389         Out.ar(0,
390                         u * EnvGen.kr(Env.asr(1.0, amp, 1.0), gate, doneAction:2)
391         )
393 }).add;
395 SynthDef("sm_timbremix", { arg freq=440, amp=0.5;
396         var n = 10, u;
397         freq = freq * ([1.0] ++ { ExpRand(1.0, 3.0) }.dup(18));
398         amp = amp * 0.4 * (AmpComp.ir(freq) * ([1.0] ++ (1..n).reciprocal));
399         Out.ar(0,
400                 Pan2.ar(
401                         u = sum(
402                                 SinOsc.ar(freq, 0, amp)
403                         ) * EnvGen.kr(Env.perc(0.03, Rand(0.3, 2))),
404                         
405                         Rand(-0.5, 0.5)
406                 )
407         );
408         DetectSilence.ar(u, doneAction:2);
410 }).add;