-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathSCLOrkSynths.sc
445 lines (374 loc) · 11.5 KB
/
SCLOrkSynths.sc
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
SCLOrkSynths {
classvar <window, bankPathsArray, patternPathsArray, <synthDictionary, <folderPath;
// add all SynthDefs (checks if server is on)
// optional: force reboot server to ensure right memSize and stuff?
*initClass {
/*
Creates a Dictionary of Dictionaries holding relevant info about each synth:
Dictionary[
(synthName1 ->
Dictionary[
(bank -> bank),
(synthPath -> path),
(patternPath -> path)
],
),
(synthName2 ->
Dictionary[
(bank -> bank),
(synthPath -> path),
(patternPath -> path)
],
),
... etc
]
*/
// Check for right path of SCLOrkSynths folder
Quarks.installedPaths.do({ |p|
var path = PathName.new(p);
if( path.fileName.asString == "SCLOrkSynths", {
folderPath = path.fullPath;
})
});
// Construct main dictionary
SCLOrkSynths.prCreateMainDict;
}
*prCreateMainDict {
// list of bank PathNames -- one for each bank folder
bankPathsArray = PathName.new(folderPath ++ "/SynthDefs").folders;
// list of pattern PathNames -- one for each pbind file
patternPathsArray = PathName.new(folderPath ++ "/pbind-demos").files;
// this is the single big dictionary
synthDictionary = Dictionary.new;
// iterate through list of bank PathNames
bankPathsArray.do({ arg bankPath;
var bankName = bankPath.folderName;
// iterate over array of file Paths in bankPath to
// populate inner Dictionaries, one for each synth.
bankPath.files.do({ arg synthPath;
var synthName = synthPath.fileNameWithoutExtension.asSymbol;
var patternPath;
// find path for the corresponding pattern
patternPathsArray.do({ arg p;
var pName = p.fileNameWithoutExtension.drop(-5).asSymbol;
if(pName === synthName, { patternPath = p.fullPath });
});
// add everything in:
synthDictionary.add(
synthName -> Dictionary.newFrom(
[
\synthPath, synthPath.fullPath,
\bank, bankPath.folderName,
\patternPath, patternPath
]
)
)
});
});
}
// Load all SynthDefs and Pbindefs:
*load {
Server.default.waitForBoot({
synthDictionary.keysValuesDo({ arg synthName, synthDic;
synthDic[\patternPath].asString.load;
synthDic[\synthPath].asString.load;
PathName(synthDic[\synthPath]).fileName.postln;
});
"-\n-\n-\nSCLOrkSynths now loaded.".postln;
"synthDictionary was created.".postln;
(synthDictionary.size.asString ++ " SynthDefs available.").postln;
" "
});
}
// true or false?
// we check for the presence of one unique synthdef and one unique pattern from the collection to tell if it's been loaded.
*isLoaded {
^if(
SynthDescLib.global.synthDescs.at(\defaultB).notNil
and: Pbindef.all.keys.includes(\ikedaBass),
{ true }, { false }
);
}
// Create GUI
*gui {
var header;
var gap = 5;
var margin = 10;
var banksMenu;
var currentBank = \drums;
var numberOfColumns = 5;
var numberOfRows = 10;
var windowWidth = 800;
var windowHeight = 480;
var buttonWidth = windowWidth - (margin * 2) - (numberOfColumns * (gap - 1)) / numberOfColumns;
var buttonHeight = 25; //(windowHeight * 0.75) / numberOfRows;
var buttonArray;
// var bankNameArray = (Document.current.dir ++ "/*.scd").resolveRelative.pathMatch.collect({ arg path; path.basename.drop(-4).asSymbol });
var activeButton;
var footer1, footer2;
var currentSynth;
var currentSynthText;
var playButton;
// might move this out of here as a separate listBanks method
var banks = PathName.new(folderPath +/+ "SynthDefs").folders.collect({ arg p; p.folderName.asSymbol});
// Load all SynthDefs and Patterns
if( window.isNil, {
// only call .load at GUI start up if we are sure SCLOrkSynths is not loaded
if( SCLOrkSynths.isLoaded, {
"Looks like SCLOrkSynths is already loaded. Opening GUI now...".postln;
}, {
SCLOrkSynths.load;
});
window = Window.new(
name: "SCLOrkSynths",
bounds: Rect.new(
left: 100,
top: 100,
width: windowWidth,
height: windowHeight
),
resizable: false
);
window.front;
window.view.decorator = FlowLayout.new(
bounds: window.view.bounds,
margin: Point.new(margin, margin),
gap: Point.new(gap, gap)
);
// header is just the area where drop down menu sits
header = CompositeView.new(window, Rect.new(0, 0, windowWidth - (margin * 2), 50));
// StaticText goes first so EZPopUpMenu stays on top
StaticText.new(
parent: header,
bounds: Rect(0, 0, header.bounds.width, header.bounds.height))
.string_("SCLOrkSynths")
// .background_(Color.green(0.5, 0.2))
.align_(\topRight)
.font_(Font(Font.default, size: 24, bold: true));
banksMenu = EZPopUpMenu.new(
parentView: header,
bounds: Rect.new(0, 10, 185, 30),
label: "bank: ",
items: banks,
globalAction: { arg menu;
var count = 0;
// ["bank menu action", menu.value, menu.item].postln;
currentBank = menu.item; // currentBank holds a symbol, not a number
// clean up buttons
buttonArray.do({arg button;
button.string = " ";
});
// kind of works, but not alphabetical -- fix that later
synthDictionary.keys.asArray.sort.do({ arg synthName;
var synthDic = synthDictionary[synthName.asSymbol];
var indexDownByColumn = count % numberOfRows * numberOfColumns + count.div(numberOfRows);
if(synthDic[\bank].asSymbol===currentBank.asSymbol,
{
buttonArray[indexDownByColumn].string = synthName.asString;
count = count + 1;
}
)
});
},
initVal: banks.indexOf(currentBank.asSymbol),
initAction: false, // because buttonArray does not exist yet
labelWidth: 50
);
// header.background = Color.rand;
buttonArray = 50.collect({ arg count;
Button.new(
parent: window.view,
bounds: Point.new(buttonWidth, buttonHeight),
)
.action_({ arg button;
// In case user selects new synth during a running demo, STOP that demo right away:
playButton.valueAction = 0;
// Now update currentSynth
currentSynthText.string = button.string;
currentSynth = button.string;
});
});
// now that buttonArray exists, we can run EZPopUpMenu action to initialize button labels:
banksMenu.valueAction = currentBank;
footer1 = CompositeView.new(window, Rect.new(0, 300, windowWidth - (margin * 2), 50));
currentSynthText = StaticText.new(
parent: footer1,
bounds: Rect(0, 0, footer1.bounds.width, footer1.bounds.height))
.string_("click on a button to choose a SynthDef")
.background_(Color.gray(0.5, 0.2))
.align_(\center)
.font_(Font(Font.default, size: 24, bold: true))
.front;
// empty space
/* StaticText.new(
parent: window,
bounds: Rect.new(0, 300, windowWidth / 3 - (margin * 2), 50)
);
*/
footer2 = CompositeView.new(window, Rect.new(0, 0, windowWidth - (margin * 2), 50));
// placeholder button
Button.new(
parent: footer2,
bounds: Rect.new(
left: 0,
top: 0,
width: footer2.bounds.width / 9 * 2,
height: 50
)
)
.string_("open more demos")
// .font_(Font(Font.default.name, 18))
.action_({ arg button;
// button.value.postln;
SCLOrkSynths.prOpenMoreDemos(currentSynth);
})
.front;
// play button
playButton = Button.new(
parent: footer2,
bounds: Rect.new(
left: footer2.bounds.width / 3,
top: 0,
width: footer2.bounds.width / 3,
height: 50
)
)
// .string_("play demo")
.states_([
["play demo", Color.black, Color.green],
["stop", Color.white, Color.red]
])
.font_(Font(Font.default.name, 18))
.action_({ arg button;
// expensive solution to avoid FAILURE IN SERVER messages...
// one unique Ndef per demo
if((button.value==1),
{
Ndef(currentSynth.asSymbol).resume;
Ndef(currentSynth.asSymbol,
Pspawner({ arg sp;
sp.seq(Pdef(currentSynth.asSymbol));
0.1.wait;
{ button.value = 0 }.defer;
})
).play.fadeTime_(0).quant_(0);
},{
Ndef(currentSynth.asSymbol).pause;
Ndef(currentSynth.asSymbol).stop(0.1);
}
);
})
.front;
// 'show me the code' button
Button.new(
parent: footer2,
bounds: Rect.new(
left: footer2.bounds.width / 9 * 7,
top: 0,
width: footer2.bounds.width / 9 * 2,
height: 50
)
)
.string_("show me the code")
// .font_(Font(Font.default.name, 18))
.action_({ arg button;
// String.readNew(File.new(synthCode, "r"));
if(currentSynth.notNil,
{
SCLOrkSynths.showMeTheCode(currentSynth);
},
{
"Select a synth first!".postln;
}
);
})
.front;
window.onClose_({ window = nil });
"opening SCLOrkSynths gui".postln;
}, {
"SCLOrkSynths GUI already open".postln;
try { Window.find(\SCLOrkSynths).front };
});
}
*showMeTheCode { |synth|
var sPath, pPath;
var sString, pString, pStringLastAscii;
sPath = synthDictionary[synth.asSymbol][\synthPath];
pPath = synthDictionary[synth.asSymbol][\patternPath];
sString = String.readNew(File.new(sPath, "r"));
pString = String.readNew(File.new(pPath, "r"));
while(
{pStringLastAscii != 41}, // while this is *not* a ")" (ascii 41)
// remove last characters until reaching the parenthesis:
{
pString = pString.drop(-1);
pStringLastAscii = pString.last.ascii;
}
);
Document.new(
title: synth.asString ++ " code",
string:
"// SynthDef\n(\n"
++
sString
++
"\n\n);\n// Pattern demo\n(\n"
++
pString
++
".play;\n);"
).front;
}
*prOpenMoreDemos { | synth |
var extraDemosPath;
var pString, pStringLastAscii;
extraDemosPath = SCLOrkSynths.folderPath +/+ "pbind-demos" +/+ synth.asString ++ "-demo-extras.scd";
// Check if there is an demos-extra files for this SynthDef before proceeding
if( PathName(extraDemosPath).isFile, {
pString = String.readNew(File.new(extraDemosPath, "r"));
while(
{pStringLastAscii != 41}, // while this is *not* a ")" (ascii 41)
// remove last characters until reaching the parenthesis:
{
pString = pString.drop(-1);
pStringLastAscii = pString.last.ascii;
}
);
Document.new(
title: synth.asString ++ " extra demos",
string: pString
).front;
}, {
"There are no additional demos for this SynthDef.".postln;
});
}
// Prints directory of all synthDefs
*directory {
var list = synthDictionary.keys.asArray.sort;
list.do({ |k| k.postln; });
(list.size.asString ++ " Synth Definitions available").postln;
}
*synthArgs { | synth |
var sPath, sString, synthArgs, beg, end;
if( synthDictionary.keys.includes(synth.asSymbol), {
sPath = synthDictionary[synth.asSymbol][\synthPath];
sString = String.readNew(File.new(sPath, "r"));
beg = sString.find("SynthDef("); // where does SynthDef code actually begins?
sString = sString.drop(beg); // clip off any comments before SynthDef code
beg = sString.find("arg"); // index of keyword arg
end = sString.find(";"); // index of semicolon at end of arg list
sString = sString[beg..end].drop(3);
sString = sString.replace(",", "\n");
sString = sString.replace("\t", " "); // remove any tab characters
synthArgs = sString.split($\n); // we'll print one per line, including any comments
("SynthDef " ++ synth.asString ++ " args:\n").postln; // header text
synthArgs.do({ |i| i.postln }); // post one per line
^synthArgs; // return list of args juts in case
}, {
"WARNING: SynthDef does not exist. Mispelled name?".postln;
});
}
*args { | synth |
SCLOrkSynths.synthArgs(synth);
}
} // end of SCLOrkSynths class definition