A small utility: computer MIDI keyboard supporting MIDI output

Hi there,

I have made a small utility: computer MIDI keyboard supporting MIDI output. Would it be useful for many users if I released it as a Quark? Or is there already a Quark or library with this feature?

(
MIDIClient.init;
~midiOut = MIDIOut(0);
~musicalTyping = { |ch = 0, midiPitchOffset = 48, onVelocity = 63, offVelocity = 63, left = 0, bottom = 0|
	s.waitForBoot{
		var k, n, kLabel, intervals, bIndecise, x;
		var w, kView, bView, k1, k2, k3, k4, b, bs1, bs2, bs3, bs4, bs;
		var bLay, kLay, ctl, kAct, lastPressedButton, pressedOrReleased;

		k = ();
		[
			\z, \s, \x, \d, \c, \v, \g, \b, \h, \n, \j, \m, ',',
			\l, '.', ';', '/', \q, \2, \w, \3, \e, \4, \r,
			\t, \6, \y, \7, \u, \i, \9, \o, \0, \p, '-', '[', ']'
		].do { |item, index|
			k.add(item -> index)
		};
		n = k.size;

		kLabel = {|parent, label, width|
			label = if ("nil".matchRegexp(label.asString)) { "" } { label };
			StaticText(parent, width@30)
			.string_(" " ++ label)
			.align_(\topLeft)
			.font_(Font("Arial", 9))
			.stringColor_(Color.grey(0.2))
		};

		intervals = [
			[18, 20, 22, \, 25, 27, \, 30, 32, 34, \],
			[17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, \],
			[\, 1, 3, \, 6, 8, 10, \, 13, 15, \],
			[0, 2, 4, 5, 7, 9, 11, 12, 14, 16]
		];

		bIndecise = intervals.flat;

		x = { |i|
			{ |g = 0|
				LFTri.ar((i + 48 + [0, 0.15]).midicps)
				* 0.1
				* Env.adsr.kr(gate:g);
			}.play
		}!n;

		ctl = { |i=0, gate=0|
			if(i.class.asSymbol == \Integer) { x[i].set(\g, gate) }
		};

		w = Window("Computer MIDI Keyboard", Rect(left, bottom, 500, 370));
		w.front
		.onClose_{ { |i| x[i].free }!n };

		kView = w.view;
		bView = w.view;

		b = { |parent, label, width|
			var lableIndex;
			Button(parent, width@30)
			.states_([
				[label + midiPitchOffset, Color.white, Color.grey(0.5, 0.5)],
				[label + midiPitchOffset, Color.white, Color.grey(0.2, 0.5)]
			])
			.mouseDownAction_{
				lastPressedButton = label;
				lableIndex = bIndecise.find([label]);
				ctl.(label, 1);
				bs[lableIndex].value = 1;
				~midiOut.noteOn(ch, label + midiPitchOffset, onVelocity)
			}
			.action_{				
				lastPressedButton = nil;
				ctl.(label, 0);
				bs[lableIndex].value = 0;				
				~midiOut.noteOff(ch, label + midiPitchOffset, onVelocity)
			}
		};

		kLay = kView.addFlowLayout;

		kLabel.(kView, "", 30);
		kLabel.(kView, "", 30);
		intervals[0].collect { |i| kLabel.(kView, k.findKeyForValue(i), 30) };
		kLabel.(kView, "⌫", 30);

		kLay.nextLine;

		kLabel.(kView, "⇥", 50);
		intervals[1].collect { |i| kLabel.(kView, k.findKeyForValue(i), 30) };

		kLay.nextLine;

		kLabel.(kView, "⇪", 60);
		intervals[2].collect { |i| kLabel.(kView, k.findKeyForValue(i), 30) };
		kLabel.(kView, "⏎", 50);

		kLay.nextLine;

		kLabel.(kView, "⇧", 74);
		intervals[3].collect { |i| kLabel.(kView, k.findKeyForValue(i), 30) };
		kLabel.(kView, "⇧", 74);

		bLay = bView.addFlowLayout;

		b.(bView, \, 30);
		b.(bView, \, 30);
		bs1 = intervals[0].collect { |i| b.(bView, i, 30) };
		b.(bView, \, 50);

		bLay.nextLine;

		b.(bView, \, 50);
		bs2 = intervals[1].collect { |i| b.(bView, i, 30) };

		bLay.nextLine;


		b.(bView, \, 60);
		bs3 = intervals[2].collect { |i| b.(bView, i, 30) };
		b.(bView, \, 54);

		bLay.nextLine;

		b.(bView, \, 74);
		bs4 = intervals[3].collect { |i| b.(bView, i, 30) };
		b.(bView, \, 74);

		bLay.nextLine;

		bs = bs1 ++ bs2 ++ bs3 ++ bs4;
		kAct = { |character, v|
			character = character.asSymbol;
			if(k.trueAt(character) != false) {
				var kToChromaticInterval, bIndex;
				kToChromaticInterval = k[character];
				bIndex = bIndecise.find([kToChromaticInterval]);
				ctl.(kToChromaticInterval, v);
				bs[bIndex].value = v;
				if (v == 0) {
					~midiOut.noteOff(ch, kToChromaticInterval + midiPitchOffset, onVelocity)
				} {
					~midiOut.noteOn(ch, kToChromaticInterval + midiPitchOffset, offVelocity)
				}
			}
		};
		StaticText(w, 500@220).string_(
			"Assgined MIDI output device:" + MIDIClient.destinations[~midiOut.port].device + "\n" ++
			"Assgined MIDI output name:" + MIDIClient.destinations[~midiOut.port].name + "\n" ++
			"Assgined MIDI output uid:" + MIDIClient.destinations[~midiOut.port].uid + "\n\n" ++
			"output channel:" + (ch + 1) ++ "\n\n" ++
			"note on velocity:" + onVelocity ++ "\n" ++
			"note off velocity:" + offVelocity ++ "\n\n" ++
			"Press, hold and then release the keys for the numbered buttons using your\n"	++
			"computer keyboard. Alternatively, you might press, hold, and then release each\n" ++
			"button using the left button of the computermouse. The multi-touch on\n" ++
			"touchscreen may function, but it hasn't been tested.");
		
		pressedOrReleased = ();
		w.view
		.keyDownAction_{ |view, char|
			if (pressedOrReleased[char] != 1) { kAct.(char, 1) };
			pressedOrReleased.add(char -> 1)
		}
		.keyUpAction_{ |view, char, mod, unicode, keycode, key|
			kAct.(char, 0);
			pressedOrReleased.add(char -> 0)
		}
		.mouseUpAction_{ //|view, x, y, modifiers, buttonNumber|
			var lastPressedButtonK = k.findKeyForValue(lastPressedButton);
			if(lastPressedButton != nil) { kAct.(lastPressedButtonK, 0) }
		}
	}
};
~musicalTyping.()
)
1 Like

I made one of these as well at some point but I didn’t make it a quark. We should compare them. I’ll DM you

1 Like

Thanks! I will wait for your DM.