Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- MIDIClient.init;
- MIDIIn.connectAll;
- // BECAUSE YOU KEEP COMPLAINING WHEN I UNPLUG MY HEADPHONES
- Server.default.options.numInputBusChannels = 0;
- s.waitForBoot {
- var win, rootDropdown, scaleDropdown, notes, scaleNames, minOct, maxOct, synthTypeDropdown, relKnob, relKnobValue, atkKnob, atkKnobValue, highNoteKnob, highVolKnobValue, lowNoteKnob, lowVolKnobValue, highPanKnob, highPanValue, lowPanKnob, lowPanValue, scaleSnappingButton, recordButton;
- var font, fontbold, fontboldlarge, maincolor, titlecolor, pink;
- // Initial Settings
- ~rootNote = 0; // C
- ~scale = Scale.major;
- ~minOctave = 4;
- ~maxOctave = 6;
- ~lowNoteVol = 0.05;
- ~highNoteVol = 0.05;
- ~attack = 0.0;
- ~release = 3.0;
- ~lowNotePan = 0;
- ~highNotePan = 0;
- ~isScaleSnappingEnabled = true;
- // Scale Snapping and Octave Clamping
- ~snapToScale = { |midiNote|
- if (~isScaleSnappingEnabled, {
- var baseNote, snapped, octave, minNote, maxNote;
- // Snap to scale
- octave = midiNote.div(12);
- baseNote = midiNote % 12;
- snapped = ~scale.degrees.collect { |deg| (octave * 12 + deg + (~rootNote % 12)) };
- snapped = snapped ++ snapped.collect { |n| n + 12 }; // add octave above
- snapped = snapped.sort({ |a, b| (a - midiNote).abs < (b - midiNote).abs }).first;
- minNote = ~minOctave * 12;
- maxNote = (~maxOctave + 1) * 12 - 1;
- while { snapped < minNote } {
- snapped = snapped + 12;
- };
- while { snapped > maxNote } {
- snapped = snapped - 12;
- };
- snapped;
- }, {
- midiNote;
- });
- };
- ~volumeForNote = { |note|
- var minNote = ~minOctave * 12;
- var maxNote = ((~maxOctave + 1) * 12) - 1;
- var normalized = (note - minNote) / (maxNote - minNote);
- normalized = normalized.clip(0, 1);
- ~lowNoteVol + (normalized * (~highNoteVol - ~lowNoteVol));
- };
- ~panForNote = { |note|
- var minNote = ~minOctave * 12;
- var maxNote = ((~maxOctave + 1) * 12) - 1;
- var normalized = (note - minNote) / (maxNote - minNote);
- normalized = normalized.clip(0, 1);
- ~lowNotePan + (normalized * (~highNotePan - ~lowNotePan));
- };
- // Recording
- ~recordingPath = nil;
- ~isRecording = false;
- // Output folder on Desktop
- ~recordingFolder = Platform.userHomeDir +/+ "Desktop/Supercollider/Recs";
- File.mkdir(~recordingFolder);
- ~makeTimestampedPath = {
- var timestamp = Date.localtime.stamp;
- ~recordingFolder +/+ ("recording_" ++ timestamp ++ ".wav");
- };
- ~stopRecording = {
- if (~isRecording) {
- "Stopping recording".postln;
- s.stopRecording;
- };
- };
- // Synth
- SynthDef(\simpleSynth, {
- |freq=440, amp=0.2, dur=1, atk=0.01, rel=0.3, out=0, waveType=0, pan=0|
- var env = EnvGen.kr(Env.perc(atk, rel), doneAction: 2);
- var sig;
- sig = Select.ar(waveType, [
- SinOsc.ar(freq),
- Saw.ar(freq),
- Pulse.ar(freq),
- LFTri.ar(freq),
- WhiteNoise.ar,
- PinkNoise.ar
- ]) * amp * env;
- Out.ar(out, Pan2.ar(sig, pan));
- }).add;
- s.sync;
- // MIDI source debugging
- MIDIClient.sources.do({ |src, i|
- ("[" ++ i ++ "] " ++ src.device).postln;
- });
- // MIDI Note Handler
- MIDIdef.noteOn(\fromIanniX, { |vel, note, chan, src|
- var snapped = ~snapToScale.(note);
- var amp = ~volumeForNote.(snapped);
- var pan = ~panForNote.(snapped);
- if (~isScaleSnappingEnabled){
- ("MIDI Note: " ++ note ++ " → Snapped: " ++ snapped ++ " | Amp: " ++ amp ++ " | Pan: " ++ pan).postln;
- } {
- ("MIDI Note: " ++ note ++ " | Amp: " ++ amp ++ " | Pan: " ++ pan).postln;
- };
- Synth(\simpleSynth, [
- \freq, snapped.midicps,
- \amp, amp,
- \atk, ~attack,
- \rel, ~release,
- \out, 0,
- \waveType, ~currentWaveType,
- \pan, pan
- ]);
- });
- // GUI (Gooeyyyy)
- win = Window("Control Suite", Rect(100, 100, 380, 720), false);
- win.background = Color.white;
- win.alwaysOnTop = true;
- font = Font("Courier New", 12);
- fontbold = Font("Courier New", 12, true);
- fontboldlarge = Font("Courier New", 20, true);
- maincolor = Color(0.4, 0.404, 0.569);
- titlecolor = Color(0.749, 0.392, 0.471);
- pink = Color(0.941, 0.631, 0.694);
- y = 10;
- // Wavetype Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Wave Type")
- .background_(Color(0.922, 0.682, 0.733))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- y = y + 25;
- StaticText(win, Rect(10, y, 100, 20))
- .string_("Synth Type:")
- .background_(Color.clear)
- .stringColor_(maincolor)
- .font_(font);
- synthTypeDropdown = PopUpMenu(win, Rect(110, y, 150, 20));
- synthTypeDropdown.items = ["Sine", "Saw", "Square", "Triangle", "White Noise", "Pink Noise"];
- synthTypeDropdown.font = font;
- synthTypeDropdown.value = 0;
- synthTypeDropdown.action = {
- ~currentWaveType = synthTypeDropdown.value;
- };
- y = y + 40;
- // Scale Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Scale")
- .background_(Color(0.682, 0.808, 0.922))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- // Scale Snap Toggle
- scaleSnappingButton = Button(win, Rect(320, y, 50, 20));
- scaleSnappingButton.states = [["Off", maincolor, Color.white], ["On", Color.white, Color(0.682, 0.808, 0.922)]];
- scaleSnappingButton.value = ~isScaleSnappingEnabled.binaryValue;
- scaleSnappingButton.font = fontbold;
- scaleSnappingButton.action = {
- ~isScaleSnappingEnabled = ~isScaleSnappingEnabled.not;
- scaleSnappingButton.value = ~isScaleSnappingEnabled.binaryValue;
- };
- y = y + 25;
- StaticText(win, Rect(10, y, 100, 20))
- .string_("Root Note:")
- .background_(Color.clear)
- .stringColor_(maincolor)
- .font_(font);
- rootDropdown = PopUpMenu(win, Rect(110, y, 150, 20));
- rootDropdown.items = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
- rootDropdown.value = 0;
- rootDropdown.font = font;
- rootDropdown.action = { ~rootNote = rootDropdown.value; };
- y = y + 30;
- StaticText(win, Rect(10, y, 100, 20))
- .string_("Scale:")
- .background_(Color.clear)
- .stringColor_(maincolor)
- .font_(font);
- scaleDropdown = PopUpMenu(win, Rect(110, y, 150, 20));
- scaleDropdown.items = Scale.names.asArray;
- scaleDropdown.value = scaleDropdown.items.indexOf("major");
- scaleDropdown.action = {
- ~scale = Scale.at(scaleDropdown.items[scaleDropdown.value]);
- };
- scaleDropdown.font = font;
- y = y + 40;
- // Octave Clamp Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Octave Clamp")
- .background_(Color(0.69, 0.682, 0.922))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- y = y + 25;
- StaticText(win, Rect(10, y, 100, 20))
- .string_("Min Octave:")
- .background_(Color.clear)
- .stringColor_(maincolor)
- .font_(font);
- minOct = NumberBox(win, Rect(110, y, 50, 20))
- .value_(~minOctave)
- .font_(font)
- .action_({ |nb|
- ~minOctave = nb.value.round.clip(0, 9);
- if (~minOctave > ~maxOctave) {
- ~minOctave = ~maxOctave; nb.value = ~minOctave;
- };
- });
- StaticText(win, Rect(180, y, 100, 20))
- .string_("Max Octave:")
- .background_(Color.clear)
- .stringColor_(maincolor)
- .font_(font);
- maxOct = NumberBox(win, Rect(280, y, 50, 20))
- .value_(~maxOctave)
- .font_(font)
- .action_({ |nb|
- ~maxOctave = nb.value.round.clip(0, 9);
- if (~maxOctave < ~minOctave) {
- ~maxOctave = ~minOctave; nb.value = ~maxOctave;
- };
- });
- y = y + 40;
- // Linear Volume Control Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Linear Volume Control")
- .background_(Color(0.631, 0.89, 0.812))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- y = y + 25;
- // Low Note Volume
- StaticText(win, Rect(40, y, 120, 20))
- .string_("Lowest Note Vol:")
- .font_(font)
- .background_(Color.clear)
- .stringColor_(maincolor);
- lowNoteKnob = Knob(win, Rect(80, y + 20, 40, 40))
- .value_(0.25)
- .font_(font)
- .action_({ |knob|
- ~lowNoteVol = knob.value * 0.2;
- lowVolKnobValue.value = ~lowNoteVol;
- });
- lowVolKnobValue = NumberBox(win, Rect(75, y + 65, 50, 20))
- .value_(~lowNoteVol)
- .enabled_(false)
- .font_(font);
- // High Note Volume
- StaticText(win, Rect(200, y, 140, 20))
- .string_("Highest Note Vol:")
- .background_(Color.clear)
- .font_(font)
- .stringColor_(maincolor);
- highNoteKnob = Knob(win, Rect(240, y + 20, 40, 40))
- .value_(0.25)
- .font_(font)
- .action_({ |knob|
- ~highNoteVol = knob.value * 0.2;
- highVolKnobValue.value = ~highNoteVol;
- });
- highVolKnobValue = NumberBox(win, Rect(235, y + 65, 50, 20))
- .value_(~highNoteVol)
- .font_(font)
- .enabled_(false);
- y = y + 100;
- // Envelope Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Envelope")
- .background_(Color(0.949, 0.831, 0.596))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- y = y + 25;
- // Attack
- StaticText(win, Rect(75, y, 100, 20))
- .string_("Attack:")
- .font_(font)
- .background_(Color.clear)
- .stringColor_(maincolor);
- atkKnob = Knob(win, Rect(80, y + 20, 40, 40))
- .value_(~attack)
- .font_(font)
- .action_({ |knob|
- ~attack = knob.value.clip(0,1);
- atkKnobValue.value = ~attack;
- });
- atkKnobValue = NumberBox(win, Rect(75, y + 65, 50, 20))
- .value_(~attack)
- .font_(font)
- .enabled_(false);
- // Release
- StaticText(win, Rect(230, y, 100, 20))
- .string_("Release:")
- .font_(font)
- .background_(Color.clear)
- .stringColor_(maincolor);
- relKnob = Knob(win, Rect(240, y + 20, 40, 40))
- .value_(0.6)
- .font_(font)
- .action_({ |knob|
- ~release = knob.value * 5;
- relKnobValue.value = ~release;
- });
- relKnobValue = NumberBox(win, Rect(235, y + 65, 50, 20))
- .value_(~release)
- .font_(font)
- .enabled_(false);
- y = y + 108;
- // Pan Control Section
- StaticText(win, Rect(10, y, 360, 20))
- .string_("Linear Panning Control")
- .background_(Color(0.643, 0.706, 0.871))
- .stringColor_(Color.white)
- .align_(\center)
- .font_(fontbold);
- y = y + 25;
- // Low Note Pan
- StaticText(win, Rect(40, y, 120, 20))
- .string_("Lowest Note Pan:")
- .font_(font)
- .background_(Color.clear)
- .stringColor_(maincolor);
- lowPanKnob = Knob(win, Rect(80, y + 20, 40, 40))
- .value_(0.5)
- .action_({ |knob|
- ~lowNotePan = (knob.value * 2) - 1;
- lowPanValue.value = ~lowNotePan;
- });
- lowPanValue = NumberBox(win, Rect(75, y + 65, 50, 20))
- .value_(~lowNotePan)
- .enabled_(false)
- .font_(font);
- // High Note Pan
- StaticText(win, Rect(200, y, 140, 20))
- .string_("Highest Note Pan:")
- .background_(Color.clear)
- .font_(font)
- .stringColor_(maincolor);
- highPanKnob = Knob(win, Rect(240, y + 20, 40, 40))
- .value_(0.5)
- .action_({ |knob|
- ~highNotePan = (knob.value * 2) - 1;
- highPanValue.value = ~highNotePan;
- });
- highPanValue = NumberBox(win, Rect(235, y + 65, 50, 20))
- .value_(~highNotePan)
- .font_(font)
- .enabled_(false);
- y = y + 108;
- // Recording Button Section
- StaticText(win, Rect(10, y, 360, 30))
- .string_("Recording Control")
- .background_(Color.clear)
- .stringColor_(titlecolor)
- .align_(\top, \center)
- .font_(fontboldlarge);
- y = y + 28;
- recordButton = Button(win, Rect(140, y, 100, 40));
- recordButton.states = [["REC", maincolor, Color.white], ["STOP", Color.white, pink]];
- recordButton.font = fontbold;
- recordButton.action = {
- if (~isRecording.not) {
- ~recordingPath = ~makeTimestampedPath.();
- ("Starting recording to: " ++ ~recordingPath).postln;
- s.record(
- path: ~recordingPath,
- headerFormat: "wav",
- sampleFormat: "int16"
- );
- ~isRecording = true;
- } {
- ~stopRecording.();
- ~isRecording = false;
- };
- };
- //Knob Colors
- lowNoteKnob.color = [Color.white, Color(0.639, 0.651, 0.89), Color.white, Color(0.639, 0.651, 0.89)];
- highNoteKnob.color = [Color.white, Color(0.639, 0.773, 0.89), Color.white, Color(0.639, 0.773, 0.89)];
- atkKnob.color = [Color.white, Color(0.89, 0.643, 0.678), Color.white, Color(0.89, 0.643, 0.678)];
- relKnob.color = [Color.white, Color(0.639, 0.89, 0.824), Color.white, Color(0.639, 0.89, 0.824)];
- 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)];
- 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)];
- win.front;
- "Setup complete, GUI should appear".postln;
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement