What's new

Convolution parameters don't update in real-time on the UI

Hey guys,

I scripted some controls for the Convolution reverb. They all do work. However, displaying their current value to the user via get_engine_par_disp won't work in real time. One can clearly hear that in the background Kontakt needs some time to update the Convolution reverb before it can show the current value of the knobs.

That kinda sucks though. I'm fine with Kontakt taking a second for the new settings to apply, but when moving a slider the value should be displayed in real-time as it usually would via get_engine_par_disp. How do I fix that?


Code:
            on ui_control ($fx_ir_sli_predelay)
                set_engine_par($ENGINE_PAR_IRC_PREDELAY, $fx_ir_sli_predelay, -1, 0, $NI_SEND_BUS)
                
                { show knob values }
                %id[33] := $NI_CALLBACK_ID
                set_control_par_str(get_ui_id($fx_ir_lbl_predelay), $CONTROL_PAR_TEXT, get_engine_par_disp($ENGINE_PAR_IRC_PREDELAY, -1, 0, $NI_SEND_BUS) & " ms")

                wait($wait_time)
                if(%id[33] = $NI_CALLBACK_ID)
                    set_control_par_str(get_ui_id($fx_ir_lbl_predelay), $CONTROL_PAR_TEXT, "PRE DELAY")
                end if
            end on
 
Some parameters in the convolution module have they update done asynchronously, so you will never get the values displayed changing in real time.
There are work arounds but the convolution parameters themselves will still be updated asynchronously and the user will hear a gap between values while updating.
 
That's good to know and I'm ok with the asynchronous updating of the engine.
What would be a workaround to display the Pre-delay value in real time? It has values from 1ms to 300ms but it's not linear. It's logarithmic I guess? I need to calculate it based on the values of the slider itself (0 to 1000000), I guess?
 
That's good to know and I'm ok with the asynchronous updating of the engine.
What would be a workaround to display the Pre-delay value in real time? It has values from 1ms to 300ms but it's not linear. It's logarithmic I guess? I need to calculate it based on the values of the slider itself (0 to 1000000), I guess?
maybe you could put the get_engine_par_disp in the A_SYNC callback,
{not tried it, but i did put other stuff in the A_SYNC, like updating and changing parameters when loading a new effect in the slot}
 
Thanks for the idea. But I couldn't get it to work.
I guess I have to calculate the values 0 to 300ms myself instead of using get_engine_par_disp. I'm a bit clueless on how to do that though. I asked ChatGPT for help but its code returns me incorrect values
Code:
            on ui_control ($fx_ir_sli_predelay)           
                set_engine_par($ENGINE_PAR_IRC_PREDELAY, $fx_ir_sli_predelay, -1, 6, $NI_INSERT_BUS)

                ~i := real(fx_ir_sli_predelay)
                ~a := 300.0 * log((~i / 1000000.0) * 22014612.57 + 1.0) / log(2.0)


                set_control_par_str(get_ui_id($fx_ir_lbl_predelay), $CONTROL_PAR_TEXT, ~a & " ms")

            end on
 
Some parameters in the convolution module have they update done asynchronously, so you will never get the values displayed changing in real time.
There are work arounds but the convolution parameters themselves will still be updated asynchronously and the user will hear a gap between values while updating.
what would be such a workaround?
 
A lookup table with values pre-baked in.
For that you need to identify which math expression was used in the parameter you want.
(Yes, Native Instrument could kindly retrieve these expressions ONCE and put them in the manual forever, but... maybe one day, or a psychopath will go over all of them and publish what the math expressions, or curves, are - one thing they are not [except the from script and maybe one more I am missing]: linear)
Ok, with the rant out of the way - because, why not - it is a two step process:

1) figure out what the math expression that generates the values shown by the know are
2) use whatever you want to create the lookup table (goal is to use the math expression from 1 to generate the values and store them in an array in your script)

The breakdown:
[coming soon]
 
The funny thing is it was perfectly smooth in kontakt 5 but not anymore in k6, so they (NI) broke something along the way.
 
Here's the formula for convolution predelay parameter:

Code:
on init
    declare ui_knob $Predelay (0, 1000000, 1)
    set_knob_unit($Predelay, $KNOB_UNIT_MS)

    declare ~val
    declare $res

    declare const ~MIN_COEF := 0.6931471805599453
    declare const ~MAX_COEF := 5.7104270173748696
    declare const ~OFFSET   := 2.0

    declare const ~RANGE    := ~MAX_COEF - ~MIN_COEF
end on

on ui_control ($Predelay)
    ~val := int_to_real($Predelay) * 0.000001    { normalize to 0.0 ... 1.0 }
    ~val := (~val * ~RANGE) + ~MIN_COEF          { linear interpolation between ~MIN_COEF and ~MAX_COEF }
    ~val := pow(~NI_MATH_E, ~val) - ~OFFSET      { e^x - ~OFFSET }

    $res := real_to_int(round(~val * 100.0))     { convert to integer with two extra digits of precision }

    { take care of value display (optional, can always just print with one less decimal of precision, then it's simpler) }
    if ($res < 10)
        set_knob_label($Predelay, $res / 100 & ".0" & $res mod 100)
    else
        if ($res < 100)
            set_knob_label($Predelay, $res / 100 & "." & $res mod 100)
        else
            set_knob_label($Predelay, $res / 100 & "." & ($res / 10) mod 10)
        end if
    end if

    set_engine_par($ENGINE_PAR_IRC_PREDELAY, $Predelay, -1, 0, 1)
end on
 
Oh wow, thanks Evil Dragon! In the meantime, I got my hands on an Excel sheet with the knob values translated to the unit values. However, I couldn't get it to run properly. I got the function wrong, I guess.

https://1drv.ms/x/s!AuR3gXt_OrZZgtpOXI82oFN4AaANYQ?e=DSI4G0

How did you come up with the min_coef and max_coef? That's the key to return the correct values?

If I want to use your code to emulate the convolution reverb LPF/HPF cutoff as a lookup array what would I need to change?
 
So I tried to alternate the script for the convolution highpass/ lowpass but I'm failing at it. I asked ChatGPT for help too but with no luck.

Right now it return me values from 16 to 136 Hz. Can you help me with the math?


Code:
                 ~val := int_to_real($fx_ir_sli_hipass) * 0.000001          { normalize to 0.0 ... 1.0 }
                   ~val := 20000.0 * pow(10.0, (~val * 3.0 - log10(20.0))) / 20.0   { convert to frequency range between 20 Hz and 20 kHz }
                   $res := real_to_int(round(log10(~val / 20.0) / log10(1000.0) * 120.0))   { convert to integer with logarithmic scaling }


                   { take care of value display (optional) }
                set_control_par_str(get_ui_id($fx_ir_lbl_hipass), $CONTROL_PAR_TEXT, $res & " Hz")
 
Here's an update that covers the filter frequency of convolution parameter:

Code:
on init
    declare ui_knob $Predelay (0, 1000000, 1)
    set_knob_unit($Predelay, $KNOB_UNIT_MS)
    declare ui_knob $Freq (0, 1000000, 1)
    set_knob_unit($Freq, $KNOB_UNIT_HZ)

    declare ~val
    declare $res

    declare const ~MIN_COEF    := 0.6931471805599453
    declare const ~MAX_COEF    := 5.7104270173748696
    declare const ~OFFSET      := 2.0
    declare const ~MIN_EQ_COEF := 2.99573227355399
    declare const ~MAX_EQ_COEF := 9.90348755253612

    declare const ~RANGE    := ~MAX_COEF - ~MIN_COEF
    declare const ~EQ_RANGE := ~MAX_EQ_COEF - ~MIN_EQ_COEF
end on


on ui_control ($Predelay)
    ~val := int_to_real($Predelay) * 0.000001    { normalize to 0.0 ... 1.0 }
    ~val := (~val * ~RANGE) + ~MIN_COEF          { linear interpolation between ~MIN_COEF and ~MAX_COEF }
    ~val := pow(~NI_MATH_E, ~val) - ~OFFSET      { e^x - ~OFFSET }

    $res := real_to_int(round(~val * 100.0))     { convert to integer with two "decimals" }

    { take care of value display (optional, can always just print with one less decimal of precision, then it's simpler) }
    if ($res < 10)
        set_knob_label($Predelay, $res / 100 & ".0" & $res mod 100)
    else
        if ($res < 100)
            set_knob_label($Predelay, $res / 100 & "." & $res mod 100)
        else
            set_knob_label($Predelay, $res / 100 & "." & ($res / 10) mod 10)
        end if
    end if

    set_engine_par($ENGINE_PAR_IRC_PREDELAY, $Predelay, -1, 0, 1)
end on

on ui_control ($Freq)
    ~val := int_to_real($Freq) * 0.000001        { normalize to 0.0 ... 1.0 }
    ~val := (~val * ~EQ_RANGE) + ~MIN_EQ_COEF    { linear interpolation between ~MIN_EQ_COEF and ~MAX_EQ_COEF }
    ~val := pow(~NI_MATH_E, ~val)                { e^x }

    $res := real_to_int(round(~val * 10.0))      { convert to integer with one "decimal" }

    { take care of value display }
    if ($res < 10000)
        set_knob_label($Freq, $res / 10 & "." & $res mod 10)
    else
        { add 500 to $res for rounding purposes - matches close to how the value is displayed in Kontakt }
        set_knob_label($Freq, $res / 10000 & "." & (($res + 500) / 1000) mod 10 & "k")
    end if

    set_engine_par($ENGINE_PAR_IRC_FREQ_LOWPASS_LR, $Freq, -1, 0, 1)
end on
 
In Koala I use these

{ ********** KOALA DEFINES ***************************** } define E2V.FILT_FREQ_BASE(in, f_min, f_max) := ((pow(2.0, int_to_real(in) * (log2(f_max/f_min)/pow(10.0, 6.0)))) * f_min) define V2E.FILT_FREQ_BASE(in, f_min, f_max) := real_to_int(((pow(10.0, 6.0) / log2(f_max/f_min)) * log2(in / f_min))) function E2V.EqFreq(in) -> return return := E2V.FILT_FREQ_BASE(in, 20.0, 20000.0) end function
 
Thank you so much Evil Dragon! I don't really get the math and how you came up with it but I'm glad it works now. Is there any way to "buy you a coffee" as thank you for your support?

@polypx Koala looks interesting but sounds like a new language you'd need to learn because it's not 100% KSP.
 
In Koala I use these

{ ********** KOALA DEFINES ***************************** } define E2V.FILT_FREQ_BASE(in, f_min, f_max) := ((pow(2.0, int_to_real(in) * (log2(f_max/f_min)/pow(10.0, 6.0)))) * f_min) define V2E.FILT_FREQ_BASE(in, f_min, f_max) := real_to_int(((pow(10.0, 6.0) / log2(f_max/f_min)) * log2(in / f_min))) function E2V.EqFreq(in) -> return return := E2V.FILT_FREQ_BASE(in, 20.0, 20000.0) end function
Right, so you can see how what I posted above would be quite a bit more efficient :)
 
Top Bottom