Advice needed for using a 'relative' MIDI controller

Hi there,

I’ve got an old MIDI controller (a “Hercules DJ Console”) that I’d like to use with SC. Some of the controls send what I think are called ‘relative’ controls - a downward move sends a stream of CC values of ‘127’, while an upward move sends a stream of ‘1’ values. So it only sends values of 1 or 127. Obviously I can’t use a regular Spec to map those values.

I made a little test:

MIDIIn.connectAll;

m = MIDIIn.findPort("Hercules DJ Console", "Port 2");

f = 220; // freq

(
MIDIdef.cc(\midiCC,
	{ |val, cc, chan, src|
		if(cc == 46, {
			if(val == 1, { f = f + 1 });
			if(val == 127, { f = f - 1 });
			x.set(\freq, f);
			f.postln;
		});
	},
	srcID: m.uid,
);
)

x = Synth(\default, [\freq, f]);

(
// when done
x.release;
MIDIdef.cc(\midiCC).free;
)

This is pretty bad for actual use.

I might be wrong, but I think that when using relative MIDI controls, you have to take the speed of change into account, so faster moves will make bigger changes. I’m not sure if that’s even correct, let alone how to implement it.

Also, I’ve got no idea how to map the incoming 1 or 127 value to an arbitrary range (like for example, the range in \freq.asSpec).

Meanwhile, I’ve also started using MKtl for some projects, and hoped it might have something built in. Unfortunately, I can’t find anything in the docs. A solution using either MIDIdef, MKtl or both would be good.

Does anyone have any strategies for using a ‘relative’ MIDI controller with SC?

Thanks very much,
Jim

I would add lower and upper limits to both input and output values. So when the knob reaches its upper or lower limit, the value stays the same till the direction is reversed. Also, since freq is exponential, map the value to a defined frequency range using linexp or similar method.

~step = 0.25; // set the resolution of knob 'click', lower values means higher resolution and vice versa
f = 220.cpsmidi;
if(val == 1, { f = (f + ~step).min(127) });
if(val == 127, { f = (f - ~step).max(0) });
x.set(\freq, f.linexp(0, 127, 50, 5000)); // freq in the range 50 - 5000

Or you can use the midicps method

~step = 0.25; // set the resolution of knob 'click'
f = 220.cpsmidi;
f.linlin(0, 127, 28, 100); // map to midinote in the range 28 (e 2) to midinote 128 (e 8)
if(val == 1, { f = (f + ~step).min(127) });
if(val == 127, { f = (f - ~step).max(0) });
x.set(\freq, f.midicps); // convert midinote to frequency

Speed of change could be implemented by mapping the last delta time (or mean of x last delta times) in a given range to the ~step value.

I recently did this sort of thing by using control specs (in this case associated with some gui knobs) and a combination like value = spec.map(spec.unmap(value) + delta) for my attempt to build an arturia minilab 3 minimoog . i had found that 127 steps is far too few for a filter cutoff control, so i switched the knobs to “relative” mode and added a way for the arturia shift button to switch the delta to a finer step.

the idea is that you have a control spec that maps a value from a 0-1 range to the linear or exponential range you want. then you have a way to keep track of a ranged value (say 440 hz). you unmap that value back to the 0-1 range, add a small and consistent delta to it, and then map it back to 448 or something. deltas can be kept “simple” and linear without having to worry about the actual range you are controlling (especially important for exponential controls where a step of 2 hz is coarse near 20 and veeeery fine near 20,000)

(i would probably implement this differently now, so maybe just use it as a rough sketch of an idea. the overall approach should work without a gui too.)

Hi @Thor_Madsen, @apiarian,

Thanks very much for your replies. Both approaches are very interesting. I’ll have to experiment and play with it, but these pointers will be very useful. Again, thanks a lot. :+1:t3:

Jim