-
Notifications
You must be signed in to change notification settings - Fork 0
/
granular_synthesis.ck
189 lines (160 loc) · 5.54 KB
/
granular_synthesis.ck
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* Simple synth skeleton with 1 continuous & 1 discrete parameter
To make your own synth, edit everywhere marked TODO below
Copyright 2011 Rebecca Fiebrink
http://wekinator.cs.princeton.edu
*/
//The synth always lives in a SynthClass definition
public class SynthClass {
//Don't change this part: necessary state objects and overall envelope
OscSend xmit;
0 => int isSendingParams;
50::ms => dur rate;
Envelope e => dac;
.1::second => e.duration;
0 => e.target => e.value;
e.keyOn();
// parameters: cutoff_freq, Q, speed, pos
4 => int numParams;
float myParams[numParams];
//TODO: Add your own objects for making sound
//and patch them together, output to the envelope
0.0 => myParams[0];
1.0 => myParams[1];
0.1 => myParams[2];
0.0 => myParams[3];
SndBuf snd => NRev rev => e;
Noise noise => ADSR noise_env => Gain noise_gain => rev;
(2::second, 0::second, 1.0, 2::second) => noise_env.set;
0.03 => noise_gain.gain;
"/Users/lyang/libmapper/cyborg/bach.wav" => snd.read;
//TODO: Are your parameters discrete (integers) or continuous (real numbers)?
//This determines the learning algorithms available in the GUI
fun int[] isDiscreteArray() {
new int[numParams] @=> int a[]; //a is a temporary array whose length is the number of parameters (2 here)
1 => a[0]; //1 means this parameter is discrete
0 => a[1]; //0 means this parameter is continuous (not discrete)
0 => a[2];
0 => a[3];
return a;
}
//TODO: If you are using a discrete model, you must specify
//the number of classes of output (maximum) that you want to learn
//For example, to learn a "vowel vs. consonant" classifier, you would have 2 classes
//Or to output a class for each pitch chroma, you would have 12 classes
//It doesn't matter what number you use for a continuous parameter, since it'll be ignored
fun int[] getNumClassesArray() {
new int[numParams] @=> int a[];
2 => a[0]; //12 pitches for parameter 1
return a;
}
//TODO: For each discrete parameter, you must specify
//whether you just want the integer class label output for each parameter, or whether
//you want the output to consist of a probability distribution over all classes
fun int[] useDistributionArray() {
new int[numParams] @=> int a[];
0 => a[0]; //Let's not use a distribution (would be 1 otherwise)
return a;
}
//TODO: Give your parameters some names, which will be shown in the GUI
fun string[] getParamNamesArray() {
new string[numParams] @=> string s[];
"noise" => s[0];
"dur" => s[1];
"speed" => s[2];
"pos" => s[3];
return s;
}
//TODO: Any other setup code that should be called
//This is called by the main code, only once after initialization, like a constructor
fun void setup() {
//Spork things here if necessary, then return.
spork ~playSound();
}
fun void playSound() {
while (true) {
(myParams[1])::ms => now;
myParams[3] $ int => snd.pos;
-snd.rate() => snd.rate;
(myParams[1])::ms => now;
-snd.rate() => snd.rate;
}
}
//TODO: This gets called when the model provides us with new parameter values
//Specify how you want to use them!
//Want to do error checking here (e.g., that parameters are within the expected range,
// and that we have the expected number of parameters)
// Make sure you both use the new params to make sound AND store the values in myParams[].
fun void setParams(float params[]) {
if (params.size() >= numParams) { //always check you have at least as many params as you're expecting
params[0] => myParams[0];
if (myParams[0] == 1.0)
noise_env.keyOn();
else
noise_env.keyOff();
params[1] * 700 + 50 => myParams[1];
if (myParams[1] > 700)
700 => myParams[1];
if (myParams[1] < 50)
50 => myParams[1];
params[2] * 3 => myParams[2];
if (myParams[2] > 3)
3 => myParams[2];
if (myParams[2] < 0.1)
0.1 => myParams[2];
myParams[2] => snd.rate;
params[3] * snd.samples() => myParams[3];
if (myParams[3] < 0)
0 => myParams[3];
}
}
/* PROBABLY don't need to change anything below this line ----------------------------*/
fun int getNumParams() {
return numParams;
}
fun float[] getParams() {
return myParams;
}
//Be quiet! If you want to improve efficiency here, you could also stop
//other processing
fun void silent() {
0 => e.target;
}
//Make sound!
fun void sound() {
1 => e.target;
}
//Received when wekinator wants our params for playalong learning
fun void startGettingParams(OscSend x, dur r) {
x @=> xmit;
r => rate;
1 => isSendingParams;
spork ~sendParamsLoop();
}
//Send those parameters on at a specified rate
fun void sendParamsLoop() {
while (isSendingParams) {
sendParams();
rate => now;
}
}
//Received when wekinator wants us to stop sending those playalong params
fun void stopGettingParams() {
0 => isSendingParams;
}
//Send current parameters directly to Wekinator
fun void sendParams() {
"/realValue f" => string ss;
1 => int i;
for (1 => i; i < numParams; i++) {
ss + " f" => ss;
}
xmit.startMsg(ss);
for (0 => i; i < numParams; i++) {
xmit.addFloat(myParams[i]); //Add all params, each in its own addFloat message.
}
}
//If OSC synth, we need to instruct the synth how to get back to ChucK
fun void setOscHostAndPort(string h, int p) {
//no need to do anything, unless you're using an OSC synth like Processing or Max.
}
}