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 | }; |