Parsing Problem

Hi

I’m currently stuck with the following problem.

I have a fader with a value range of 170–20000. I can scale this with a Lua function and send it using the SysEx editor with 4 nibbles. But how do I handle this with parsing and how do you usually deal with such large value ranges?

First question: what do you want?

do you want a fader handing that full range? Or do you want a fader with a smaller range , say until 1000 and then multiply the result by 20, knowing you’ll miss out on resolution?

I personally would handle this with 2 faders: one handling the coarse travel for the highest bits,and one fine travel handling the lowest bits.

Perhaps more important: how many significant bits do you have? I see you take in all 28 bits (47), but if these are all real nibbles ,then you only need to parse four times the lowest 4 bits, not the full 7. Then the range is 256256 max.

Hey, thanks for the quick reply and the helpful hints – I’ll definitely make some adjustments.

With the RV-70 I have the choice to either control all parameters via SysEx or via CC.
I decided to go with the SysEx editor, thinking that this way I would get more resolution than with CC 0–127. However, the problem remains the same: when doing a patch request, how can the received value range of 170–20000 be mapped into the range of, for example, CC 0–127? There must be a solution for this as a Lua function to parse exactly this SysEx range and convert it. I still have a lot to learn here.

By the way, your parsing tutorial is a great starting point – many thanks for that.

Can you verify the steps between 170 and 20.000? Is it one per ? Or perhaps some incremental scale?

I tested several values (170, 171, 3495, 3496, 14950, 14951) and with the patch request I got back exactly the same values. So yes, it seems to be “one per” – every integer value from 170 up to 20,000 is recognized and returned. This was tested using a fader with a value range of 0–16383.

I’m now using the fader in 0–1000 steps. For scaling I use the following Lua functions, and using the SysEx editor to implement the four nibbles:

-- Logarithmic Scaling
local function scaleLog(value, inMin, inMax, outMin, outMax)
    local norm = (value - inMin) / (inMax - inMin)
    if norm < 0 then norm = 0 end
    if norm > 1 then norm = 1 end
    local scaled = outMin * ((outMax / outMin) ^ norm)
    return math.floor(scaled + 0.5)
end

-- Nibbles for High Cut Filter Frequency
local function HiCFq(shift)
    return function(_, value)
        local val = scaleLog(value, 0, 1000, 170, 20000)
        return (val >> shift) & 0x0F
    end
end

HiCFqNib1 = HiCFq(0)
HiCFqNib2 = HiCFq(4)
HiCFqNib3 = HiCFq(8)
HiCFqNib4 = HiCFq(12)
1 Like

amazing.

Okay, so with 16 bits you have a range 0-65535, large enough to cover the 170-20000 range.
In fact you’ll need only 15 out of the 16 bits.

Now, for parsing you’ll need to make yet another function, doing the inverse movement. I guess you’ll have to make an exponential function this time.

I suggest you first create a 16 bit parameter, that you can address with a control you afterwards set as hidden. You probably might find a way to only use parameterMap without a hidden control, but the control will come in handy for debugging.
In the parsing you assign the 4 nibbles to their place of the 16bit parameter.

You then assign the inverse function to hidden control, so it sets the value of your visible fader.

Be aware of rounding!
The receipt of the 4 nibbles will set the hidden control, triggering the inverse function which in turn triggers the visible fader to calculate the logaritmic function setting those 4 nibbles. Due to rounding these values might be slightly different than the ones parsed, leading to slightly altered sounds. To counter this, make sure your logaritmic function is only setting the four nibbles, when the function is triggered manually and not by lua logic.

Really appreciate your solution, thanks!

It wasn’t that easy for me to implement, but ChatGPT helped me out with quite a few things.

For simplicity, I’m currently reading all values as 16-bit and mapping them directly onto the fader – and it works perfectly:

-- Inverse of scaleLog: Hz → Fader (0..1000)
local function invScaleLog(value, inMin, inMax, outMin, outMax)
    local norm = math.log(value / outMin) / math.log(outMax / outMin)
    local scaled = inMin + norm * (inMax - inMin)
    return math.floor(scaled + 0.5)
end

-- Parse an incoming response
function patch.onResponse(device, responseId, sysexBlock)

    -- read the four nibbles (positions 19..22)
    local n1 = sysexBlock:peek(19)
    local n2 = sysexBlock:peek(20)
    local n3 = sysexBlock:peek(21)
    local n4 = sysexBlock:peek(22)

    -- combine them into a single 16-bit value
    local value = (n4 << 12) | (n3 << 8) | (n2 << 4) | n1

    -- inverse scaling: map 170..20000 Hz -> 0..1000 fader
    local fader = invScaleLog(value, 0, 1000, 170, 20000)

    -- print details
    print(string.format("raw nibbles @19-22 = %X %X %X %X", n1, n2, n3, n4))
    print("combined value (Hz) = " .. value)
    print("mapped fader value (0-1000) = " .. fader)

    controls.get(1):getValue():getMessage():setValue(fader)
end

well done.
No rounding issues?

After some intensive testing, i didn’t find any rounding issues. After a patch request, all the values come back exactly as set.

Now i’ve run into some new hurdles. The way SysEx values are organized on the RV-70 is definitely not as simple as i thought.

For example, the Output Level parameter has an effect in different places depending on the algorithm. This means that the desired value in algorithm X can unintentionally influence the value in algorithm Y, depending on which one was sent last.

I’m trying to figure out a way to make sure only the visible faders are actually sending SysEx data, so this issue can be avoided.