Class for using midi controller in live coding

I am trying to implement a class for easy and quick access to my MidiMIX controller (sliders, knobs) in order to insert them in Node definitions (Ndef) during live coding sessions. I first tried to write the midi values to control buses and access them through environment variables which worked. Now I am trying to implement classes that do the same and return the actual value of the control bus when calling the class with the corresponding argument.

MidiMIX {

	classvar sBuses, kBuses;

	*init {

		var s=Server.default;

		MIDIIn.connectAll;

		sBuses=9.collect({Bus.control(s, 1)});
		kBuses=8.collect({3.collect({Bus.control(s, 1)})});

		MIDIdef.cc(\controlers, {
			arg val, num, char, src;
			var sNums = [19,23,27,31,49,53,57,61,62];
			var kNums = [[16,17,18],[20,21,22],[24,25,26],[28,29,30],[46,47,48],[50,51,52],[54,55,56],[58,59,60]];
			// [val, num, char, src].postln;
			val=(val/127);

			sNums.do({
				arg item, index;
				if (num==item) {sBuses[index].value=val
				}
			});

			kNums.do({
				arg item1, index1;
				item1.do({
					arg item2, index2;
					if (num==item2) {kBuses[index1][index2].value=val
					}
				})
			})
		});

		MIDIdef.cc(\controlers).permanent = true;
	}

	*returnsBus {
		^sBuses;
	}

	*returnkBus {
		^kBuses;
	}
}

MSlider : MidiMIX {

	*new{
		arg num=1;
		var bus = super.returnsBus[num];
		^bus.kr

	}
}

MKnob : MidiMIX {

	*new{
		arg col=1, row=1;
		var bus = super.returnkBus[col][row];
		^bus.kr

	}
}

Now when I call MSlider(1) inside a Ndef, the control buffer outputs 0 and is not updated by the midi controller. Any values written in a control bus should be accessible outside the scope of the class, right? Any clues?

Thank you!

Here, there are two potential points of failure:

  1. Writing to the bus (maybe it isn’t locating the bus according to controller number, or maybe the bus-set operation is failing).
  2. Reading from the bus.

… and you can’t proceed without determining which is the real problem.

To test the first, I would turn on server OSC dumping (Server menu, or s.dumpOSC(1)), move the control, and see if it posts messages. If it doesn’t post messages, then the problem is in the MIDIdef logic.

To test the second, I would do Ndef(\myNdefName).trace and look for the In UGen that is reading the bus. Make sure it’s the right bus index. (That’s pretty much the only problem you can have with control buses – kr buses are not subject to order-of-execution problems.)

BTW it will be more CPU-efficient to write one MIDIdef per controller, instead of one big MIDIdef for everybody. For every CC message coming in, you’re searching through all (8*3)+9 = 33 controller numbers, but it’s guaranteed that at most 1 could match = 32 wasted iterations for every message. The MIDIFunc / MIDIdef lookup is designed to be faster than O(n) looping.

sNums.do { |ccnum, index|
	MIDIdef.cc(("slider" ++ ccnum).asSymbol, { |val|
		sBuses[index].set(val/127);
	}, ccnum).permanent_(true);  // this cc will respond *only* to one ccnum
};

… and similar for kNums.

hjh

1 Like

Thanks! I decided to go with environment variables since they are visually more distinct and helpful during live coding.

(
s.waitForBoot({

	var sNums = [19,23,27,31,49,53,57,61,62];
	var kNums = [[16,17,18],[20,21,22],[24,25,26],[28,29,30],[46,47,48],[50,51,52],[54,55,56],[58,59,60]];

	var sliders=9.collect({Bus.control(s, 1)});
	var knobs=8.collect({3.collect({Bus.control(s, 1)})});

	MIDIIn.connectAll;

	sNums.do { |ccnum, index|
		MIDIdef.cc(("slider" ++ ccnum).asSymbol, { |val|
			sliders[index].set((val/127)**4);
		}, ccnum).permanent_(true);
	};

	kNums.do { |item, index1|
		item.do { |ccnum, index2|
			MIDIdef.cc(("knob" ++ ccnum).asSymbol, { |val|
				knobs[index1][index2].set(val/127);
		}, ccnum).permanent_(true);
		}

	};

	~slider = {
		arg num;
		sliders[num-1].kr.lag
	};

	~knob = {
		arg num; //I prefer to just give one argument
		var col=(num/10).asInteger;
		var row=num%10;
		knobs[col-1][row-1].kr.lag
	}
})
)

I have another question though: how the functions ~slider and ~knob can access the arrays with the control buffers? Aren’t the local variables sliders and knobs destroyed after the evaluation of the code block?

They’re not. Because the ~knob and ~slider functions use variables belonging to the enclosing scope, the enclosing scope has to continue to exist, until those functions are unreachable.

hjh

sclang supports closures. functional programming - What is a 'Closure'? - Stack Overflow

1 Like