fix: .plot should accept minval/maxval as an array for the channels: [0,0,0]
[supercollider.git] / examples / pieces / microhelix_study.scd
blob7367f402b974eb6a1b7535e4f0692a2abed8273f
2 //a study for recreating key (sound) aspects of data.microhelix by Ryoji Ikeda
3 //Batuhan Bozkurt 2009 http://www.earslap.com
5         //rhythm logic: very crude, times are 1/8's mixed with 1/4's (equal probability) plus 1/16. 
6         //when 1/16 is chosen, we make sure they come in doubles so it sounds rhytmically nicer.
7         //we create 2 instances of these trigs so glitching sound will be independent between speakers.
8         //we use TDuty with Demand UGens to create this kind of sophisticated triggers (easier and more 
9         //flexible dealing with pure noise generators).
10         var ctrigs = 
11         {
12                 TDuty.ar
13                 (
14                         Dxrand(((1/8!8) ++ (1/4!8) ++ [Dseq([1/16, 1/16], Drand([1, 2], inf))]) * 1.25, inf), //see above logic
15                         0, 
16                         Dwhite(0.5, 1, inf) //trigger values are between 0.5, 1
17                 )
18         }!2; 
19         
20         //the glitch/clicking noises: basically enveloped PinkNoise through bandpass filter with high rq (low q)
21         //here I'm modulating the decay time of the noise envelope with a LFNoise1.
22         //there are many diferent ways of approaching this, needs experimentation,
23         //dynamic center freq, rq etc. can be employed for example. This sounded nice to me, however.
24         
25         var clicks = 
26                 BPF.ar
27                 (
28                         PinkNoise.ar(Decay.ar(ctrigs, 0.001 * LFNoise1.ar(4).abs)), //decay time is modulated
29                         15000, 
30                         0.9, 
31                         25 * LFNoise1.ar(8).range(0, 1) //extreme amplification of glitches. 
32                 ).fold(-1, 1); //folding them back into [-1, 1] range (foldback distortion). I think it sounds nice.
33         
34         //i don't really know what to call this sound, so its names snd1. 2 sine oscillators tuned by ear, with filtered
35         //background noise. nothing fancy. we will random-pan and envelope this later on.
36         var snd1 = 
37                 LPF.ar
38                 (
39                         SinOsc.ar(44.midicps, 0, 0.5) + SinOsc.ar(90.midicps, 0, 0.6), 
40                         32.midicps * 2
41                 ) + HPF.ar(LPF.ar(WhiteNoise.ar(0.008), 12000), 2400);
42         
43         //the shaker-like sound in the background. in the original piece, the rhytmical logic for this sound
44         //is a lot more different, I'm using the same ctrigs here, for the sake of simplicity (and I'm a bit lazy).
45         //its basically an enveloped WhiteNoise through a bandpass filter with a random freq 
46         var hiNoise = 
47                 BPF.ar
48                 (
49                         WhiteNoise.ar(Decay2.ar(ctrigs * LFNoise1.ar(8, 0.5, 0.5), 0.02, 0.1) * 0.05), 
50                         TRand.ar(12000, 15000, ctrigs), 
51                         0.9
52                 );
53         
54         //the bass/kicky sound needs a lot of tuning (sound and envelope, some best be left to post processing maybe)
55         //2 sine oscillators, one has a low freq nad the other is an upper harmonic (quieter)
56         //the original piece uses a completely different (and very better) rhytmical logic for this sound.
57         //I'm using our ctrigs impulse generator here (first channel) for the sake of clarity.
58         //but since I don't want every trigger to activate this, I'm multiplying the trigger with
59         //a noise generator (rounded, so its -1, 0, or 1). so not all trigs pass through.
60         
61         //the sine oscillator uses a trick I've learned from Josh Parmenter. the freq arguments of the
62         //sine oscillators are set to zero, and they are driven by the phase argument with a sweep.
63         //this makes the oscillator phase resettable which is very useful for having uniform attacks
64         //for low frequency kick-like sounds. or else, the envelope sounding the oscillators can kick
65         //in at any phase and the attack of the kicks will vary (in a bad way). here I reset the phase  
66         //of the sine at each trigger. the speed of the phase driver ([52.8, 740]) determines the 
67         //frequency of the oscillator here. I'm adding a phase of pi/3 to it so the phase resets to 
68         //pi/3 instead of zero which produces an additional glitch. this is all about glitch after all right?
69         
70         var trigMod = LFNoise0.ar(8).round;
71         
72         var bass = 
73                 SinOsc.ar(0, (Sweep.ar(ctrigs[0] * trigMod, 2pi * [52.8, 740]) + (pi/3)).wrap(-pi, pi), [2, 0.05]).mean.tanh * 
74                 EnvGen.ar(Env([0, 0.5, 0.4, 0], [0, 0.2, 0.01], -5), (ctrigs[0] * trigMod).abs)!2; 
75         
76         //panning and enveloping of snd1. I don't want to use every trigger for the envelope of this so
77         //i again multiply it with a random generator. not all trigs pass through. and some random panning.
78         snd1 = 
79                 Pan2.ar
80                 (
81                         snd1 * EnvGen.ar(Env([0, 1, 0.6, 0], [0.0001, 0.4, 0.01]), ctrigs * LFNoise0.ar(8)), 
82                         TRand.ar(-1, 1, ctrigs)
83                 );
84         
85         //i'm using limiter here because i'm lazy. one needs to get the
86         //gain scheduling right for a better sound. this especially kills the bass
87         //sometimes. I'm also boosting the freqs around 15000Hz.
88         //delete some sounds from the input of MidEQ to hear the sounds
89         //(glitches, bass etc) in isolation.
90         Limiter.ar(MidEQ.ar(clicks + snd1 + hiNoise + bass, 14000, 0.7, 8));
91 }.play;