File: thx.ck

package info (click to toggle)
chuck 1.5.5.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 41,056 kB
  • sloc: cpp: 123,473; ansic: 35,893; javascript: 2,111; yacc: 609; makefile: 457; python: 174; perl: 86
file content (165 lines) | stat: -rw-r--r-- 5,590 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//--------------------------------------------------------------------
// name: thx.ck
// desc: emulation of the original THX Deep Note
//       (by Dr. James Andy Moorer)
//
// author: Perry R. Cook (https://www.cs.princeton.edu/~prc/)
//         Ge Wang (https://ccrma.stanford.edu/~ge/)
//
// Perry R. Cook (Jan 8, 2007) -- original ChucK version
// Ge Wang -- modified final chord to align with original Deep Note
//         -- added beginning "chaotic" section
//         -- time-driven loops (was counter-driven loops)
//
// THX resources from /Artful Design/:
//     https://artful.design/thx/
//
// Andy Moorer's personal account
//.    http://www.jamminpower.org/THX.html
//
// -------------------------------------------------------------------
// Ge, Fall 2017: from Andy Mooorer:
// -------------------------------------------------------------------
// OK - I dug out the original program. Here are the frequency
// bounds of the cluster:
// #define LOCLUST 40.0
// #define HICLUST 350.0
//
// I had started with them in a more narrow range, but then 
// widened it. With the randomness, they never get anywhere 
// near the limits.  And here are the pitches in the final
// chord for all 30 voices:
//
// double freqs[NOSCS], initialfreqs[NOSCS],
// finalfreqs[] =
// { 1800.0, 1800.0, 1800.0,
//   1500.0, 1500.0,
//   1200.0, 1200.0, 1200.0, 1200.0,
//   900.0, 900.0, 900.0, 900.0,
//   600.0, 600.0, 600.0,
//   300.0, 300.0, 300.0, 300.0,
//   150.0, 150.0, 150.0, 150.0,
//   75.0, 75.0, 75.0
//   37.5, 37.5, 37.5,
// };
//
// Note that in the final chord, they are detuned a bit by 
// injecting a bit of randomness to make sure they don't fuse
// totally. Several people have commented that the chord 
// sounds "bigger" than an equivalent orchestra or organ 
// chord (like the big chord in the Bm fugue which was my
// inspiration). I believe this is because of the just 
// temperament of the chord. Moving the thirds and fifths 
// to equal temperament just doesn't have the same impact. 
//
// Let me know if you have any other questions. I am really 
// excited to see your new book. It looks like great fun!
// -A
//--------------------------------------------------------------------
 
// 30 target frequencies, corresponding to pitches in a big chord:
// D1,  D2, D3,  D4,  D5,  A5,  D6,   F#6,  A6
[ 37.5, 75, 150, 300, 600, 900, 1200, 1500, 1800,
  37.5, 75, 150, 300, 600, 900, 1200, 1500, 1800,
  37.5, 75, 150, 300, 600, 900, 1200,       1800,
            150, 300,      900, 1200  
] @=> float targets[];

// initial frequencies
float initials[30];
// for the initial "wavering" in the steady state
float initialsBase[30];
float randomRates[30];

// parameters (play with these to control timing)
12.5::second => dur initialHold; // initial steady segment
6.0::second => dur sweepTime; // duration over which to change freq
5.5::second => dur targetHold; // duration to hold target chord
6.0::second => dur decayTime; // duration for reverb tail to decay to 0

// sound objects
SawOsc saw[30]; // sawtooth waveform (30 of them)
Gain gainL[30]; // left gain (volume)
Gain gainR[30]; // right gain (volume)
// connect stereo reverberators to output
NRev reverbL => dac.left;
NRev reverbR => dac.right;
// set the amount of reverb
0.075 => reverbL.mix => reverbR.mix;

// for each sawtooth: connect, compute frequency trajectory
for( 0 => int i; i < 30; i++ )
{
    // connect sound objects (left channel)
    saw[i] => gainL[i] => reverbL;
    // connect sound objects (right channel)
    saw[i] => gainR[i] => reverbR;
    // randomize initial frequencies
    Math.random2f( 160.0, 360.0 ) => initials[i] 
               => initialsBase[i] => saw[i].freq;
    // initial gain for each sawtooth generator
    0.1 => saw[i].gain;
    // randomize gain (volume)
    Math.random2f( 0.0, 1.0 ) => gainL[i].gain;
    // right.gain is 1-left.gain -- effectively panning in stereo
    1.0 - gainL[i].gain() => gainR[i].gain;
    // rate at which to waver the initial voices
    Math.random2f(.1,1) => randomRates[i];
}

// hold steady cluster (initial chaotic random frequencies)
now + initialHold => time end;
// fade in from silence
while( now < end )
{
    // percentage (should go from 0 to 1)
    1 - (end-now) / initialHold => float progress;
    // for each sawtooth
    for( 0 => int i; i < 30; i++ ) {
        // set gradually decaying values to volume
        0.1 * Math.pow(progress,3) => saw[i].gain;
        // waver the voices
        initialsBase[i] + (1.25-progress)*.5*initialsBase[i]*Math.sin(now/second*randomRates[i])
             => initials[i] => saw[i].freq;
    }
    // advance time
    10::ms => now;
}

// when to stop
now + sweepTime => end;
// sweep freqs towards target freqs
while( now < end )
{
    // percentage (should go from 0 to 1)
    1 - (end-now)/sweepTime => float progress;
    // for each sawtooth
    for( 0 => int i; i < 30; i++ ) {
        // update frequency by delta, towards target
        initials[i] + (targets[i]-initials[i])*progress => saw[i].freq;
    }
    // advance time
    10::ms => now;
}

// at this point: reached target freqs; briefly hold
targetHold => now;

// when to stop
now + decayTime => end;
// chord decay (fade to silence)
while( now < end )
{
    // percentage (should go from 1 to 0)
    (end-now) / decayTime => float progress;
    // for each sawtooth
    for( 0 => int i; i < 30; i++ ) {
        // set gradually decaying values to volume
        0.1 * progress => saw[i].gain;
    }
    // advance time
    10::ms => now;
}

// wait for reverb tail before ending
5::second => now;