SHOW:
|
|
- or go back to the newest paste.
1 | - | Sure! I'll message you it in a sec, I am making cup noodle atm :P |
1 | + | |
2 | - | |
2 | + | |
3 | - | EDIT: I can't message u :( |
3 | + | |
4 | - | |
4 | + | |
5 | - | Lol it's ok, I think I can paste it here, here you go :) |
5 | + | |
6 | - | |
6 | + | |
7 | s.waitForBoot { | |
8 | ||
9 | var win, rootDropdown, scaleDropdown, notes, scaleNames, minOct, maxOct, synthTypeDropdown, relKnob, relKnobValue, atkKnob, atkKnobValue, highNoteKnob, highVolKnobValue, lowNoteKnob, lowVolKnobValue, highPanKnob, highPanValue, lowPanKnob, lowPanValue, scaleSnappingButton, recordButton; | |
10 | ||
11 | var font, fontbold, fontboldlarge, maincolor, titlecolor, pink; | |
12 | ||
13 | // Initial Settings | |
14 | ~rootNote = 0; // C | |
15 | ~scale = Scale.major; | |
16 | ~minOctave = 4; | |
17 | ~maxOctave = 6; | |
18 | ~lowNoteVol = 0.05; | |
19 | ~highNoteVol = 0.05; | |
20 | ~attack = 0.0; | |
21 | ~release = 3.0; | |
22 | ~lowNotePan = 0; | |
23 | ~highNotePan = 0; | |
24 | ~isScaleSnappingEnabled = true; | |
25 | ||
26 | // Scale Snapping and Octave Clamping | |
27 | ~snapToScale = { |midiNote| | |
28 | if (~isScaleSnappingEnabled, { | |
29 | var baseNote, snapped, octave, minNote, maxNote; | |
30 | ||
31 | // Snap to scale | |
32 | octave = midiNote.div(12); | |
33 | baseNote = midiNote % 12; | |
34 | snapped = ~scale.degrees.collect { |deg| (octave * 12 + deg + (~rootNote % 12)) }; | |
35 | snapped = snapped ++ snapped.collect { |n| n + 12 }; // add octave above | |
36 | ||
37 | snapped = snapped.sort({ |a, b| (a - midiNote).abs < (b - midiNote).abs }).first; | |
38 | ||
39 | minNote = ~minOctave * 12; | |
40 | maxNote = (~maxOctave + 1) * 12 - 1; | |
41 | ||
42 | while { snapped < minNote } { | |
43 | snapped = snapped + 12; | |
44 | }; | |
45 | while { snapped > maxNote } { | |
46 | snapped = snapped - 12; | |
47 | }; | |
48 | ||
49 | snapped; | |
50 | }, { | |
51 | midiNote; | |
52 | }); | |
53 | }; | |
54 | ||
55 | ~volumeForNote = { |note| | |
56 | var minNote = ~minOctave * 12; | |
57 | var maxNote = ((~maxOctave + 1) * 12) - 1; | |
58 | ||
59 | var normalized = (note - minNote) / (maxNote - minNote); | |
60 | normalized = normalized.clip(0, 1); | |
61 | ||
62 | ~lowNoteVol + (normalized * (~highNoteVol - ~lowNoteVol)); | |
63 | }; | |
64 | ||
65 | ~panForNote = { |note| | |
66 | var minNote = ~minOctave * 12; | |
67 | var maxNote = ((~maxOctave + 1) * 12) - 1; | |
68 | ||
69 | var normalized = (note - minNote) / (maxNote - minNote); | |
70 | normalized = normalized.clip(0, 1); | |
71 | ||
72 | ~lowNotePan + (normalized * (~highNotePan - ~lowNotePan)); | |
73 | }; | |
74 | ||
75 | // Recording | |
76 | ~recordingPath = nil; | |
77 | ~isRecording = false; | |
78 | ||
79 | // Output folder on Desktop | |
80 | ~recordingFolder = Platform.userHomeDir +/+ "Desktop/Supercollider/Recs"; | |
81 | File.mkdir(~recordingFolder); | |
82 | ||
83 | ~makeTimestampedPath = { | |
84 | var timestamp = Date.localtime.stamp; | |
85 | ~recordingFolder +/+ ("recording_" ++ timestamp ++ ".wav"); | |
86 | }; | |
87 | ||
88 | ~stopRecording = { | |
89 | if (~isRecording) { | |
90 | "Stopping recording".postln; | |
91 | s.stopRecording; | |
92 | }; | |
93 | }; | |
94 | ||
95 | // Synth | |
96 | ||
97 | SynthDef(\simpleSynth, { | |
98 | |freq=440, amp=0.2, dur=1, atk=0.01, rel=0.3, out=0, waveType=0, pan=0| | |
99 | var env = EnvGen.kr(Env.perc(atk, rel), doneAction: 2); | |
100 | var sig; | |
101 | ||
102 | sig = Select.ar(waveType, [ | |
103 | SinOsc.ar(freq), | |
104 | Saw.ar(freq), | |
105 | Pulse.ar(freq), | |
106 | LFTri.ar(freq), | |
107 | WhiteNoise.ar, | |
108 | PinkNoise.ar | |
109 | ]) * amp * env; | |
110 | ||
111 | Out.ar(out, Pan2.ar(sig, pan)); | |
112 | }).add; | |
113 | ||
114 | s.sync; | |
115 | ||
116 | // MIDI source debugging | |
117 | MIDIClient.sources.do({ |src, i| | |
118 | ("[" ++ i ++ "] " ++ src.device).postln; | |
119 | }); | |
120 | ||
121 | // MIDI Note Handler | |
122 | MIDIdef.noteOn(\fromIanniX, { |vel, note, chan, src| | |
123 | var snapped = ~snapToScale.(note); | |
124 | var amp = ~volumeForNote.(snapped); | |
125 | var pan = ~panForNote.(snapped); | |
126 | ||
127 | if (~isScaleSnappingEnabled){ | |
128 | ("MIDI Note: " ++ note ++ " → Snapped: " ++ snapped ++ " | Amp: " ++ amp ++ " | Pan: " ++ pan).postln; | |
129 | } { | |
130 | ("MIDI Note: " ++ note ++ " | Amp: " ++ amp ++ " | Pan: " ++ pan).postln; | |
131 | }; | |
132 | ||
133 | Synth(\simpleSynth, [ | |
134 | \freq, snapped.midicps, | |
135 | \amp, amp, | |
136 | \atk, ~attack, | |
137 | \rel, ~release, | |
138 | \out, 0, | |
139 | \waveType, ~currentWaveType, | |
140 | \pan, pan | |
141 | ]); | |
142 | ||
143 | }); | |
144 | ||
145 | // GUI (Gooeyyyy) | |
146 | win = Window("Control Suite", Rect(100, 100, 380, 720), false); | |
147 | win.background = Color.white; | |
148 | win.alwaysOnTop = true; | |
149 | ||
150 | font = Font("Courier New", 12); | |
151 | fontbold = Font("Courier New", 12, true); | |
152 | fontboldlarge = Font("Courier New", 20, true); | |
153 | maincolor = Color(0.4, 0.404, 0.569); | |
154 | titlecolor = Color(0.749, 0.392, 0.471); | |
155 | pink = Color(0.941, 0.631, 0.694); | |
156 | ||
157 | y = 10; | |
158 | ||
159 | // Wavetype Section | |
160 | StaticText(win, Rect(10, y, 360, 20)) | |
161 | .string_("Wave Type") | |
162 | .background_(Color(0.922, 0.682, 0.733)) | |
163 | .stringColor_(Color.white) | |
164 | .align_(\center) | |
165 | .font_(fontbold); | |
166 | ||
167 | y = y + 25; | |
168 | ||
169 | StaticText(win, Rect(10, y, 100, 20)) | |
170 | .string_("Synth Type:") | |
171 | .background_(Color.clear) | |
172 | .stringColor_(maincolor) | |
173 | .font_(font); | |
174 | ||
175 | synthTypeDropdown = PopUpMenu(win, Rect(110, y, 150, 20)); | |
176 | synthTypeDropdown.items = ["Sine", "Saw", "Square", "Triangle", "White Noise", "Pink Noise"]; | |
177 | synthTypeDropdown.font = font; | |
178 | synthTypeDropdown.value = 0; | |
179 | synthTypeDropdown.action = { | |
180 | ~currentWaveType = synthTypeDropdown.value; | |
181 | }; | |
182 | ||
183 | y = y + 40; | |
184 | ||
185 | // Scale Section | |
186 | StaticText(win, Rect(10, y, 360, 20)) | |
187 | .string_("Scale") | |
188 | .background_(Color(0.682, 0.808, 0.922)) | |
189 | .stringColor_(Color.white) | |
190 | .align_(\center) | |
191 | .font_(fontbold); | |
192 | ||
193 | // Scale Snap Toggle | |
194 | scaleSnappingButton = Button(win, Rect(320, y, 50, 20)); | |
195 | scaleSnappingButton.states = [["Off", maincolor, Color.white], ["On", Color.white, Color(0.682, 0.808, 0.922)]]; | |
196 | scaleSnappingButton.value = ~isScaleSnappingEnabled.binaryValue; | |
197 | scaleSnappingButton.font = fontbold; | |
198 | scaleSnappingButton.action = { | |
199 | ~isScaleSnappingEnabled = ~isScaleSnappingEnabled.not; | |
200 | scaleSnappingButton.value = ~isScaleSnappingEnabled.binaryValue; | |
201 | }; | |
202 | ||
203 | y = y + 25; | |
204 | ||
205 | StaticText(win, Rect(10, y, 100, 20)) | |
206 | .string_("Root Note:") | |
207 | .background_(Color.clear) | |
208 | .stringColor_(maincolor) | |
209 | .font_(font); | |
210 | ||
211 | rootDropdown = PopUpMenu(win, Rect(110, y, 150, 20)); | |
212 | rootDropdown.items = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; | |
213 | rootDropdown.value = 0; | |
214 | rootDropdown.font = font; | |
215 | rootDropdown.action = { ~rootNote = rootDropdown.value; }; | |
216 | ||
217 | y = y + 30; | |
218 | ||
219 | StaticText(win, Rect(10, y, 100, 20)) | |
220 | .string_("Scale:") | |
221 | .background_(Color.clear) | |
222 | .stringColor_(maincolor) | |
223 | .font_(font); | |
224 | ||
225 | scaleDropdown = PopUpMenu(win, Rect(110, y, 150, 20)); | |
226 | scaleDropdown.items = Scale.names.asArray; | |
227 | scaleDropdown.value = scaleDropdown.items.indexOf("major"); | |
228 | scaleDropdown.action = { | |
229 | ~scale = Scale.at(scaleDropdown.items[scaleDropdown.value]); | |
230 | }; | |
231 | scaleDropdown.font = font; | |
232 | ||
233 | y = y + 40; | |
234 | ||
235 | // Octave Clamp Section | |
236 | StaticText(win, Rect(10, y, 360, 20)) | |
237 | .string_("Octave Clamp") | |
238 | .background_(Color(0.69, 0.682, 0.922)) | |
239 | .stringColor_(Color.white) | |
240 | .align_(\center) | |
241 | .font_(fontbold); | |
242 | ||
243 | y = y + 25; | |
244 | ||
245 | StaticText(win, Rect(10, y, 100, 20)) | |
246 | .string_("Min Octave:") | |
247 | .background_(Color.clear) | |
248 | .stringColor_(maincolor) | |
249 | .font_(font); | |
250 | minOct = NumberBox(win, Rect(110, y, 50, 20)) | |
251 | .value_(~minOctave) | |
252 | .font_(font) | |
253 | .action_({ |nb| | |
254 | ~minOctave = nb.value.round.clip(0, 9); | |
255 | if (~minOctave > ~maxOctave) { | |
256 | ~minOctave = ~maxOctave; nb.value = ~minOctave; | |
257 | }; | |
258 | }); | |
259 | ||
260 | StaticText(win, Rect(180, y, 100, 20)) | |
261 | .string_("Max Octave:") | |
262 | .background_(Color.clear) | |
263 | .stringColor_(maincolor) | |
264 | .font_(font); | |
265 | maxOct = NumberBox(win, Rect(280, y, 50, 20)) | |
266 | .value_(~maxOctave) | |
267 | .font_(font) | |
268 | .action_({ |nb| | |
269 | ~maxOctave = nb.value.round.clip(0, 9); | |
270 | if (~maxOctave < ~minOctave) { | |
271 | ~maxOctave = ~minOctave; nb.value = ~maxOctave; | |
272 | }; | |
273 | }); | |
274 | ||
275 | y = y + 40; | |
276 | ||
277 | // Linear Volume Control Section | |
278 | StaticText(win, Rect(10, y, 360, 20)) | |
279 | .string_("Linear Volume Control") | |
280 | .background_(Color(0.631, 0.89, 0.812)) | |
281 | .stringColor_(Color.white) | |
282 | .align_(\center) | |
283 | .font_(fontbold); | |
284 | ||
285 | y = y + 25; | |
286 | ||
287 | // Low Note Volume | |
288 | StaticText(win, Rect(40, y, 120, 20)) | |
289 | .string_("Lowest Note Vol:") | |
290 | .font_(font) | |
291 | .background_(Color.clear) | |
292 | .stringColor_(maincolor); | |
293 | lowNoteKnob = Knob(win, Rect(80, y + 20, 40, 40)) | |
294 | .value_(0.25) | |
295 | .font_(font) | |
296 | .action_({ |knob| | |
297 | ~lowNoteVol = knob.value * 0.2; | |
298 | lowVolKnobValue.value = ~lowNoteVol; | |
299 | }); | |
300 | lowVolKnobValue = NumberBox(win, Rect(75, y + 65, 50, 20)) | |
301 | .value_(~lowNoteVol) | |
302 | .enabled_(false) | |
303 | .font_(font); | |
304 | ||
305 | // High Note Volume | |
306 | StaticText(win, Rect(200, y, 140, 20)) | |
307 | .string_("Highest Note Vol:") | |
308 | .background_(Color.clear) | |
309 | .font_(font) | |
310 | .stringColor_(maincolor); | |
311 | highNoteKnob = Knob(win, Rect(240, y + 20, 40, 40)) | |
312 | .value_(0.25) | |
313 | .font_(font) | |
314 | .action_({ |knob| | |
315 | ~highNoteVol = knob.value * 0.2; | |
316 | highVolKnobValue.value = ~highNoteVol; | |
317 | }); | |
318 | highVolKnobValue = NumberBox(win, Rect(235, y + 65, 50, 20)) | |
319 | .value_(~highNoteVol) | |
320 | .font_(font) | |
321 | .enabled_(false); | |
322 | ||
323 | y = y + 100; | |
324 | ||
325 | // Envelope Section | |
326 | StaticText(win, Rect(10, y, 360, 20)) | |
327 | .string_("Envelope") | |
328 | .background_(Color(0.949, 0.831, 0.596)) | |
329 | .stringColor_(Color.white) | |
330 | .align_(\center) | |
331 | .font_(fontbold); | |
332 | ||
333 | y = y + 25; | |
334 | ||
335 | // Attack | |
336 | StaticText(win, Rect(75, y, 100, 20)) | |
337 | .string_("Attack:") | |
338 | .font_(font) | |
339 | .background_(Color.clear) | |
340 | .stringColor_(maincolor); | |
341 | atkKnob = Knob(win, Rect(80, y + 20, 40, 40)) | |
342 | .value_(~attack) | |
343 | .font_(font) | |
344 | .action_({ |knob| | |
345 | ~attack = knob.value.clip(0,1); | |
346 | atkKnobValue.value = ~attack; | |
347 | }); | |
348 | atkKnobValue = NumberBox(win, Rect(75, y + 65, 50, 20)) | |
349 | .value_(~attack) | |
350 | .font_(font) | |
351 | .enabled_(false); | |
352 | ||
353 | // Release | |
354 | StaticText(win, Rect(230, y, 100, 20)) | |
355 | .string_("Release:") | |
356 | .font_(font) | |
357 | .background_(Color.clear) | |
358 | .stringColor_(maincolor); | |
359 | relKnob = Knob(win, Rect(240, y + 20, 40, 40)) | |
360 | .value_(0.6) | |
361 | .font_(font) | |
362 | .action_({ |knob| | |
363 | ~release = knob.value * 5; | |
364 | relKnobValue.value = ~release; | |
365 | }); | |
366 | relKnobValue = NumberBox(win, Rect(235, y + 65, 50, 20)) | |
367 | .value_(~release) | |
368 | .font_(font) | |
369 | .enabled_(false); | |
370 | ||
371 | y = y + 108; | |
372 | ||
373 | // Pan Control Section | |
374 | StaticText(win, Rect(10, y, 360, 20)) | |
375 | .string_("Linear Panning Control") | |
376 | .background_(Color(0.643, 0.706, 0.871)) | |
377 | .stringColor_(Color.white) | |
378 | .align_(\center) | |
379 | .font_(fontbold); | |
380 | ||
381 | y = y + 25; | |
382 | ||
383 | // Low Note Pan | |
384 | StaticText(win, Rect(40, y, 120, 20)) | |
385 | .string_("Lowest Note Pan:") | |
386 | .font_(font) | |
387 | .background_(Color.clear) | |
388 | .stringColor_(maincolor); | |
389 | lowPanKnob = Knob(win, Rect(80, y + 20, 40, 40)) | |
390 | .value_(0.5) | |
391 | .action_({ |knob| | |
392 | ~lowNotePan = (knob.value * 2) - 1; | |
393 | lowPanValue.value = ~lowNotePan; | |
394 | }); | |
395 | lowPanValue = NumberBox(win, Rect(75, y + 65, 50, 20)) | |
396 | .value_(~lowNotePan) | |
397 | .enabled_(false) | |
398 | .font_(font); | |
399 | ||
400 | // High Note Pan | |
401 | StaticText(win, Rect(200, y, 140, 20)) | |
402 | .string_("Highest Note Pan:") | |
403 | .background_(Color.clear) | |
404 | .font_(font) | |
405 | .stringColor_(maincolor); | |
406 | highPanKnob = Knob(win, Rect(240, y + 20, 40, 40)) | |
407 | .value_(0.5) | |
408 | .action_({ |knob| | |
409 | ~highNotePan = (knob.value * 2) - 1; | |
410 | highPanValue.value = ~highNotePan; | |
411 | }); | |
412 | highPanValue = NumberBox(win, Rect(235, y + 65, 50, 20)) | |
413 | .value_(~highNotePan) | |
414 | .font_(font) | |
415 | .enabled_(false); | |
416 | ||
417 | y = y + 108; | |
418 | ||
419 | // Recording Button Section | |
420 | StaticText(win, Rect(10, y, 360, 30)) | |
421 | .string_("Recording Control") | |
422 | .background_(Color.clear) | |
423 | .stringColor_(titlecolor) | |
424 | .align_(\top, \center) | |
425 | .font_(fontboldlarge); | |
426 | ||
427 | y = y + 28; | |
428 | ||
429 | recordButton = Button(win, Rect(140, y, 100, 40)); | |
430 | recordButton.states = [["REC", maincolor, Color.white], ["STOP", Color.white, pink]]; | |
431 | recordButton.font = fontbold; | |
432 | recordButton.action = { | |
433 | if (~isRecording.not) { | |
434 | ~recordingPath = ~makeTimestampedPath.(); | |
435 | ("Starting recording to: " ++ ~recordingPath).postln; | |
436 | ||
437 | s.record( | |
438 | path: ~recordingPath, | |
439 | headerFormat: "wav", | |
440 | sampleFormat: "int16" | |
441 | ); | |
442 | ||
443 | ~isRecording = true; | |
444 | } { | |
445 | ~stopRecording.(); | |
446 | ~isRecording = false; | |
447 | }; | |
448 | }; | |
449 | ||
450 | //Knob Colors | |
451 | lowNoteKnob.color = [Color.white, Color(0.639, 0.651, 0.89), Color.white, Color(0.639, 0.651, 0.89)]; | |
452 | highNoteKnob.color = [Color.white, Color(0.639, 0.773, 0.89), Color.white, Color(0.639, 0.773, 0.89)]; | |
453 | atkKnob.color = [Color.white, Color(0.89, 0.643, 0.678), Color.white, Color(0.89, 0.643, 0.678)]; | |
454 | relKnob.color = [Color.white, Color(0.639, 0.89, 0.824), Color.white, Color(0.639, 0.89, 0.824)]; | |
455 | lowPanKnob.color = [Color.white, Color(0.643, 0.706, 0.871), Color(0.714, 0.643, 0.871), Color(0.643, 0.706, 0.871)]; | |
456 | highPanKnob.color = [Color.white, Color(0.714, 0.643, 0.871), Color(0.643, 0.706, 0.871), Color(0.643, 0.706, 0.871)]; | |
457 | ||
458 | win.front; | |
459 | ||
460 | "Setup complete, GUI should appear".postln; | |
461 | }; |