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

I was wondering the same. Normally a parameter is associated with a control widget, but I suppose you figured out a way to add a parameter without a widget. Not sure if any of the Stoermelder modules would know how to map to it.

Let me rephrase that. Mapping to all parameters of an entire module ought to work fine. But I don’t see how a Stoermelder module like uMap would map to just the hidden parameter so you could take control via CV.

Ah, good point, the param widget does need to be visible for that mapping ui to work. I do make/made param widgets for this, but, yeah they are not normally visible…

Thanks for your answers.

I understand that the parameters need to be exposed with a dedicated control widget in order to be available for being mapped to a knob or a midi controller. I think I will be able to change modules code to expose parameters.

But do you know if the Grayscale Supercell module is open source ? I can’t find the repository.

Otherwise i will stick with the 8face solution to switch mode (only available from contextual menu) with a knob (mappable). One mode = one 8face preset.

Thanks!

This is one of those things I find myself coming back to repeatedly of late… mainly due to bodging things elsewhere …and specifically being able to trigger send feedback > now as a one-hit from an externally-mapped button on MIDI-CAT & then disable feedback altogether - due to an inescapable latency issue that creates feedback oscillation on my motorised faders. (The bodge being a RaspberryPi4 acting as a MIDI-merger to get around my issues with midi device indexing on patch load when you have a LOT of separate MIDI interfaces/controllers, and happen to want to be working in a fixed-rack/headless scenario live - no UI access)

Could I request a MIDI-CAT expander please @stoermelder that can essentially allow you to have the MIDI output feedback device and channel all configured, but toggle sending feedback on and off, or only when an incoming gate is high :pray: ??

That sounds like a very special case for an expander moule.
A while ago I wrote an unreleased MIDI script processor which could easily used for things like that. Maybe it is time to add it to my development build on GitHub though I’m not sure if there were better solutions developed in the meantime?

I would like even just to be able to initialize any or several modules with a trigger signal.

F’rinstance, zeroing out analog shift registers or S&H modules. If I have several of them, I have to hunt them down and right-click each one of them to get back to a startup state. It would be so convenient if I could do it with the “Reset” O/P signal from a clock module. As it is, I have to assemble an elaborate means of temporarily by-passing inputs and rapidly stepping through them to reload them with all zero values.

This is eminently do-able, and I can see the use, although I don’t know offhand if anyone’s written a module for it. If not, I can easily patch this together using bits of something else that I’m working on. Let me know if there are any specific features you think would be helpful. My first thought is:

  • allow selection of up to 8 or 10 external modules with some sort of visual indicator (the prototype/proof of concept would just list them in text displays);
  • expose individual triggers for each module as well as a global trigger;
  • probably provide one trigger group for reset and another for randomize. (Anybody think a third group for triggered bypass would be worth it?)

Would it be helpful to have a latching “shield” button for each module that keeps the selection but exempts that module from the global trigger(s)?

Instead of assigning multiple target modules to one Initializer module, how about making it a 1HP module with just an input jack, a toggle switch to disable the incoming trigger, and a manual button, in that order from top down. It initializes only the target module to its immediate right (or left, if you give it a pair of LEDs to show which side, à la stoermelder’s STRIP). This way you could still easily see which modules are being initialized by following the patch cords from the reset trigger O/P, and it’s as expandable as you need.

1 Like

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