Is there a way to access a right click menu item with a knob or CV?

You can try my STROKE module for that: It has an experimental command for sending any hotkey to a specific module in the patch. Right now you can’t use the same hotkey for multiple “special commands” of STROKE but at least you can configure the resets in one place.

3 Likes

I like that! Less fiddly than multiple selects. It sounds like STROKE should also work (there’s always a @stoermelder :slight_smile: ) but I’ll probably follow that design if no one does it first. 2HP since it needs an input jack, and I’ll plan to squeeze in three groups of those three controls (randomize, reset, and bypass–on reflection I do think triggered bypass would be useful).

The only thing that concerns me is that it wouldn’t work with modules that have expanders on both sides. (I don’t think there’s an easy general way to climb the “expander tree” to the root module–and besides which someone might want to modify the expander anyway). Not sure it’s worth changing the design dramatically for this very unusual case, but I could add a “select” button that linked to a remote module and had its own readout on the LED line (thinking a red light in the center).

Also, I’ll check STRIP, but what happens if there are modules on both sides? I don’t love hidden rules like “prefer right.” I guess “affect both” is the sensible behavior and provides at least one step towards batch functionality.

I vote yes! Please lets see your midi-scripting-thing.

I got what i was looking for with ModScript, but for now, all I wanted was for some knobs to control NRPN’s and CC’s for 8, 10 and 14 bit MIDI controls out of rack.

ModScript script
config.frameDivider = 32
config.bufferSize = 1

lastvalue = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

function NewPatchMaster(id)
	mod = Module(id)
	mod.params = {
		["U1"] = {index = 0, min = 0.000000, max = 1.000000, default = 0.000000},
		["U2"] = {index = 1, min = 0.000000, max = 1.000000, default = 0.000000},
		["U3"] = {index = 2, min = 0.000000, max = 1.000000, default = 0.000000},
		["U4"] = {index = 3, min = 0.000000, max = 1.000000, default = 0.000000},
		["U5"] = {index = 4, min = 0.000000, max = 1.000000, default = 0.000000},
		["U6"] = {index = 5, min = 0.000000, max = 1.000000, default = 0.000000},
		["No name"] = {index = 6, min = 0.000000, max = 1.000000, default = 0.000000},
		["No name"] = {index = 7, min = 0.000000, max = 1.000000, default = 0.000000},
	}
	mod.inputs = {
	}
	mod.outputs = {
	}
	mod.lights = {
		{index = 0},
		{index = 1},
		{index = 2},
		{index = 3},
		{index = 4},
		{index = 5},
		{index = 6},
		{index = 7},
		{index = 8},
		{index = 9},
		{index = 10},
		{index = 11},
		{index = 12},
		{index = 13},
		{index = 14},
		{index = 15},
		{index = 16},
		{index = 17},
		{index = 18},
		{index = 19},
		{index = 20},
		{index = 21},
		{index = 22},
		{index = 23},
		{index = 24},
		{index = 25},
		{index = 26},
		{index = 27},
		{index = 28},
		{index = 29},
		{index = 30},
		{index = 31},
		{index = 32},
		{index = 33},
		{index = 34},
		{index = 35},
		{index = 36},
		{index = 37},
		{index = 38},
		{index = 39},
		{index = 40},
		{index = 41},
		{index = 42},
		{index = 43},
		{index = 44},
		{index = 45},
		{index = 46},
		{index = 47},
		{index = 48},
		{index = 49},
		{index = 50},
		{index = 51},
		{index = 52},
		{index = 53},
		{index = 54},
		{index = 55},
		{index = 56},
		{index = 57},
		{index = 58},
		{index = 59},
		{index = 60},
		{index = 61},
		{index = 62},
		{index = 63},
		{index = 64},
		{index = 65},
		{index = 66},
		{index = 67},
		{index = 68},
		{index = 69},
		{index = 70},
		{index = 71},
	}
	return mod
end

myPM = NewPatchMaster(0x6775c924dc493)
myVPM = NewPatchMaster(0xf53a3a11ee620)

function process(block)

	value = myPM:getParam("U1")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[0]) then
		lastvalue[0] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x48) 	--NRPN 48h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myPM:getParam("U2")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[1]) then
		lastvalue[1] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x49) 	--NRPN 49h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myPM:getParam("U3")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[2]) then
		lastvalue[2] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x4A) 	--NRPN 4Ah
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myPM:getParam("U4")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[3]) then
		lastvalue[3] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x4B) 	--NRPN 4Bh
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myPM:getParam("U5")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[4]) then
		lastvalue[4] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x4C) 	--NRPN 4Ch
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myPM:getParam("U6")
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[5]) then
		lastvalue[5] = value
		scaled = value * 100
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x4D) 	--NRPN 4Dh
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myVPM:getParam(0)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[6]) then
		lastvalue[6] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x40) 	--NRPN 40h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myVPM:getParam(1)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[7]) then
		lastvalue[7] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x41) 	--NRPN 41h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myVPM:getParam(2)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[8]) then
		lastvalue[8] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x42) 	--NRPN 42h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myVPM:getParam(3)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[9]) then
		lastvalue[9] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x43) 	--NRPN 43h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	value =  myVPM:getParam(4)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[10]) then
		lastvalue[10] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x44) 	--NRPN 40h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end

	
	value =  myVPM:getParam(5)
	if value <= 0.01 then 
		value = 0 
	end 
	if (value ~= lastvalue[11]) then
		lastvalue[11] = value
		scaled = value * 200
		upper = math.floor(scaled / 8)
		lower = scaled % 8
		sendMidiMessage(CC, 98, 0x45) 	--NRPN 40h
		sendMidiMessage(CC, 63, lower)  --Lower 3 bits of 8 bit value to CC#63
		sendMidiMessage(CC, 6, upper)   --Upper 5 bits of 8 bit value to CC#6
	end


end

I’m working on optimizing my naive code, but there are many different kinds of controllers on the device.

grouping controls
  for i = 0, 5 do
    local value = myVPM:getParam(i)
    if value <= 0.01 then
      value = 0
    end

    if value ~= lastvalue[i + 12] then
      lastvalue[i + 12] = value
      scaled = value * 200
      upper, lower = ??????????

      sendMidiMessage(CC, 98, 0x40 + i)
      sendMidiMessage(CC, 63, lower)
      sendMidiMessage(CC, 6, upper)
    end
  end

This is interesting, because it is an real world example. Right now you can’t achive the same thing with my module, because is was designed as a “MIDI processor”, it won’t do anything without an incoming MIDI message. I will think about it.
Some details:

I was just trying STROKE out last night! Is there any chance you can allow multiple special commands for the same hotkey?

I had a thought - Can you add a gate input to each row as an alternate way of activating the action? It would be handy to have CV control over many of those special commands. Perhaps it makes more sense as new module, but I can see how it could work for this one.

This limitation did come from preparing the module for key-sequences instead of simple hotkeys - this got never implemented. But I will look if I can add some kind of „linking“ multiple command’s together.

The problem is that CV signals live in the audio threads and most of the commands are GUI related which makes things quite tricky. I will think about it but I won’t promise anything.

1 Like

Hmm. I could not get STROKE to send a ⌘-I to initialize a shift register. It appears to come out as just an “I”.

detailed version: When I right-click on it to inspect Modules > Send hotkey to module (experimental) > Learn hotkey, The correct target module name (“ML Modules Shift Register”) appears, but below that it says “Hotkey: I”.

If it did work, I would still be wishing for a trigger input.

I’m sorry, I must have made this mistake with the key modifiers on Mac a dozen times… it is fixed now :zipper_mouth_face:

3 Likes

No arm64 build? Please add that to your build pipeline…

I’m using Azure pipelines for the build and as far as I know this is not supported. If I’m wrong I appreciate any links on the topic - I don’t want to move to GitHub Workflows for the time being.

1 Like

something like this?

Ah. That worked. Yay! I see also that you can re-use the same hotkey on different [parts?] of a STROKE module to accomplish different things at the same time. e.g. “Module: Send hotkey” on one, and “CV: Trigger” on the next one. Saves a few steps in Resetting and Initializing.

The only problem now is this:

Screen Shot 2023-10-07 at 19.29.29

If I let it update PackOne, it goes back to the version that doesn’t work. Is this the sort of thing that fixes itself when the Library is updated?

Meanwhile I’ll hang onto a copy of “Stoermelder-P1-2.0.24223eb-mac-x64-20231007.vcvplugin” in case I forget and “Update all”.

1 Like