How to force a formatter function w/o a value change?

I really need some reliable way to cause a formatter function associated with a control to execute and update the display without changing the value.

The reason is that I have presets that have a Master control that scales 2 or more child controls. However, the hardware does not actually change/store the child values - it applies the scaling in real time as needed.

To mimic that, when the preset Master control is changed, I want to have the child controls refresh their display by taking the current value and applying the scaling factor set by the Master.

I got it working somewhat by doing a message:setValue(currentValue) but today it seems to be not working at all. I also tried doing a full setRange() with no success. Simply directly invoking the formatter: fmt34(valueObject, value) doesn’t help.
I also tried the control:repaint(), but that also does nothing.

I’ve tried variations on 3.5, 3.54, and 3.6.0b

So right now I’m confused with no obvious approach

1 Like

I have similar issues when for instance the range of patch numbers should reflect the chosen patch bank. F.i. In the first bank, numbers should range from 0-127, but when changing bank it should show numbers 128-255 on my Roland synth.
I resolve it by using parameterMap get and set commands, where I change the value to one higher and immediately set it back one lower. This does the trick of retriggering the format function.

So it’s not actually without a value change, as you requested.
But in my case nothing is sent by these 2 changes (it is a virtual parameter, so you can control whether it should send something or not) so this does not do any harm.

Yes, that’s how I’ve done it before, but it involves the extra step of adding code to onChange() to prevent sending the up/down value when generated internally versus via incoming sysex or the encoders.

2 Likes

I was wondering about a situation when you needed to call formatter if value stays the same. Now, I get it. I suggest to trigger formatter when any of the “range” attributes is modified? Would that work for you?

Martin - I will give that a try, So - min, max, default. If I set default that should work since the formatter will be displaying information based on the current “value” parameter which is not the same as the default.

I will be home later today and give it a try after I fix the fence in the yard (our dog got out last night and I had to chase it down at 10pm in the evening. lol crazy time.

1 Like

In the example below, assume a change of value in bankSelect from 9 to 10. This forces to move from a bank 9 where the range was 1-128 into a bank 10 with a range that runs 129-255. In the 9th bank, MIDI range = 0-127. In the 10th bank, MIDI range = 0-126 !

When working with ctrlValue:setRange this doesn’t refresh the parameter value from parameter 12033 (corresponds with control #4) from let’s say 20 into 147. Instead control 4 shows 128.

function bankSelect(valueObject, value)
 value = value + 1
local bankGroup = { -- MSB, LSB and ranges for my XV5080 banks
  {87,64,128},{87,65,128},{87,66,128},{87,67,128},{87,68,128},{87,69,128},{87,70,128},{1,0,128}, -- preset A-G + GM
  {89,6,128},{89,7,255},{89,10,128},{89,11,255},{89,14,128},{89,15,255},{89,18,128} -- SR-JV 4, 6, 8, 10
   }
  midi.sendControlChange (devPort, channel, 0, bankGroup[value][1])
  midi.sendControlChange (devPort, channel, 32, bankGroup[value][2])
  local control = controls.get (4) -- patch number
  local valueObjects = control:getValues ()
  local ctrlValue = control:getValue ("")
  local message = ctrlValue:getMessage ()
  if bankGroup[value][3] > 128 then
    message:setMax (bankGroup[value][3]-129) -- set max MIDI value
    ctrlValue:setRange (129, bankGroup[value][3], 0, false)
  else
    message:setMax (bankGroup[value][3]-1) -- set max MIDI value
    ctrlValue:setRange (1, bankGroup[value][3], 0, false)
  end
end

The old trick works: this changes a shown value 20 of control 4 (or parameter 12033) into a refreshed value of 147 when bankGroup changes from 9 to 10

function bankSelect(valueObject, value)
  value = value + 1
local bankGroup = { -- MSB, LSB and ranges for my XV5080 banks
  {87,64,128},{87,65,128},{87,66,128},{87,67,128},{87,68,128},{87,69,128},{87,70,128},{1,0,128}, -- preset A-G + GM
  {89,6,128},{89,7,255},{89,10,128},{89,11,255},{89,14,128},{89,15,255},{89,18,128} -- SR-JV 4, 6, 8, 10
   }
  midi.sendControlChange (devPort, channel, 0, bankGroup[value][1])
  midi.sendControlChange (devPort, channel, 32, bankGroup[value][2])
  local control = controls.get (4) -- patch number
  local valueObjects = control:getValues ()
  local ctrlValue = control:getValue ("")
  local message = ctrlValue:getMessage ()
    ctrlValue:setRange (0, bankGroup[value][3], 0, true) -- set both display and MIDI min max
  if bankGroup[value][3] > 128 then
    ctrlValue:setMin (129) 
    message:setMax (bankGroup[value][3]-129) -- set max MIDI value
  else
    ctrlValue:setMin (1) -- set min display value
    message:setMax (bankGroup[value][3]-1) -- set max MIDI value
  end
  parameterMap.set (deviceId, PT_VIRTUAL, 12033,parameterMap.get (deviceId, PT_VIRTUAL, 12033)+1) -- refresh the shown value
  parameterMap.set (deviceId, PT_VIRTUAL, 12033,parameterMap.get (deviceId, PT_VIRTUAL, 12033)-1)

The formatter is now called (as of firmware v3.6.0) whenever a message or value range is changed. Additionally, changing a default value also triggers the formatter.