1. IMPORTANT:
    We launched a new online community and this space is now closed. This community will be available as a read-only resources until further notice.
    JOIN US HERE

AHDSR knob basics - go easy on me

Discussion in 'Scripting Workshop' started by ben_horwood, Oct 31, 2011.

  1. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Hi there,

    I've borrowed the following script to create a set of AHDSR knobs on my instrument.

    Code:
    on init
    	make_perfview
    
    	declare $count
    
    	declare ui_knob $Attack (0,1000000,1)
    	declare ui_knob $Hold (0,1000000,1)
    	declare ui_knob $Decay (0,1000000,1)
    	declare ui_knob $Sustain (0,1000000,1)
    	declare ui_knob $Release (0,1000000,1)
    	
    	set_knob_unit($Attack, $KNOB_UNIT_MS)
    	set_knob_unit($Hold, $KNOB_UNIT_MS)
    	set_knob_unit($Decay, $KNOB_UNIT_MS)
    	set_knob_unit($Sustain, $KNOB_UNIT_DB)
    	set_knob_unit($Release, $KNOB_UNIT_MS)
    	
    	$Attack := get_engine_par($ENGINE_PAR_ATTACK, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Hold := get_engine_par($ENGINE_PAR_HOLD, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Decay := get_engine_par($ENGINE_PAR_DECAY, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Sustain := get_engine_par($ENGINE_PAR_SUSTAIN, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Release := get_engine_par($ENGINE_PAR_RELEASE, 0, find_mod(0,"ENV_AHDSR"), -1)
    	
    	make_persistent($Attack)
    	make_persistent($Hold)
    	make_persistent($Decay)
    	make_persistent($Sustain)
    	make_persistent($Release)
    	
    	read_persistent_var($Attack)
    	read_persistent_var($Hold)
    	read_persistent_var($Decay)
    	read_persistent_var($Sustain)
    	read_persistent_var($Release)
    	
    	set_knob_label($Attack, get_engine_par_disp($ENGINE_PAR_ATTACK, 0, find_mod(0,"ENV_AHDSR"), -1))
    	set_knob_label($Hold, get_engine_par_disp($ENGINE_PAR_HOLD, 0, find_mod(0,"ENV_AHDSR"), -1))
    	set_knob_label($Decay, get_engine_par_disp($ENGINE_PAR_DECAY, 0, find_mod(0,"ENV_AHDSR"), -1))
    	set_knob_label($Sustain, get_engine_par_disp($ENGINE_PAR_SUSTAIN, 0, find_mod(0,"ENV_AHDSR"), -1))
    	set_knob_label($Release, get_engine_par_disp($ENGINE_PAR_RELEASE, 0, find_mod(0,"ENV_AHDSR"), -1))
    	
    	message("")
    end on
    
    
    on ui_control ($Attack)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_ATTACK, $Attack, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_knob_label($Attack, get_engine_par_disp($ENGINE_PAR_ATTACK, 0, 0, -1))
    end on
    
    on ui_control ($Hold)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_HOLD, $Hold, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_knob_label($Hold, get_engine_par_disp($ENGINE_PAR_HOLD, 0, 0, -1))
    end on
    
    on ui_control ($Decay)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_DECAY, $Decay, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_knob_label($Decay, get_engine_par_disp($ENGINE_PAR_DECAY, 0, 0, -1))
    end on
    
    on ui_control ($Sustain)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_SUSTAIN, $Sustain, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_knob_label($Sustain, get_engine_par_disp($ENGINE_PAR_SUSTAIN, 0, 0, -1))
    end on
    
    on ui_control ($Release)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_RELEASE, $Release, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_knob_label($Release, get_engine_par_disp($ENGINE_PAR_RELEASE, 0, 0, -1))
    end on
    
    This works okay... but I'd like to chang the maximum values.
    So instead of going to 15.0k ms, I would like them to go up to 30.0k ms
    The problem is, that I can't see where they are deriving their max values from?

    I'm pretty sure that the 2nd value is supposed to represent the maximum value, but I can't understand how this works?

    E.g. Take the following declaration:
    This knob's range goes up to 15.0k ms
    Code:
    declare ui_knob $Attack (0,1000000,1)
    Why is that?

    I tried making a similar knob with the GUIGenerator, and with the following code managed to make a knob that goes up to 30.0k ms sucessfully
    Code:
    declare ui_knob $K0(0,30000,1)
    But changing this value has no affect on the script above.

    Please help me.
     
  2. paoling

    paoling NI Product Owner

    Messages:
    26
    Well, you simply can't change the AHDSR's release knob to more than 15ms. What you did with the GUI Generator, was a generic knob with a custom range and a MS_UNIT set.

    The only easy way to emulate a release of more than 15ms is using the fade_out(EVENT_ID,TIME,1) function; but you need to tweak a bit the "on release" callback.
     
  3. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Nevermind.
    I found the Kontakt Script Language Manual.pdf in my Kontakt 3 install.
    Which is FAR more explainatory & useful than the KSP Reference Manual that came with Kontakt 4.
     
  4. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Attack is limited to max value of 15 seconds, Decay and Release are limited to 25 seconds. That's how it is.

    The knob value range is defined with (0,1000000,1) line. The maximum value of ANY engine parameter is 1000000 (in KSP integer arithmetic), and the actual values are derived from that. So you simply CANNOT go above 15 secs for attack and 25 for decay/release.

    The range (0,30000,1) most certainly won't go to 30 seconds for $ENGINE_PAR_ATTACK engine parameter. Not even if you do a multiplication of it, because the maximum value that $ENGINE_PAR_ATTACK parameter will recognize and receive is 1000000, which equates to 15 seconds.


    Sorry.
     
  5. paoling

    paoling NI Product Owner

    Messages:
    26
    Ops, in my answer I mistyped the release with the attack; as Evildragon says there's no way to increase attack time to more than 15 seconds. The only way to obtain this is emulating the AHDSR behaviour with change_vol() function, but you need a loop, wait and maybe using the ATFade function from Big Bob's library (so, it's a bit tricky to write).
    ---
    Maybe you could obtain some interesting results with the flexible envelope.
     
  6. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Ok, that's weird. :confused:
    Why such inconsistency?

    No need to be sorry. It's not YOUR fault. ;)

    That's okay. It's not absolutely critical for what I am trying to achieve.
    It's just that the synth I am trying to recreate is hypothetically capable of such vaules.
    However I have not come across a practical instance of this case yet.
    Although there is probably one hiding in there, just to mess with my head. :p

    What is the flexible envelope?
    Can you please explain this to me and/or point me to some useful reading on the topic. :)
     
  7. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    There's a flexible multipoint envelope which you can use to emulate whichever envelope you want (you have a choice of AHDSR, DBD, and flexible envelope when you add modulators). It can do much longer stage times, at the cost of some additional CPU. Check this out for some things I found out during my Kontakt sessions (Paoling, you might be interested in this as well!):


    http://minus.com/mbixubTPwO
     
  8. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Errr, thanks. :S
    But I am afraid that it is going to take me WAY too long to understand what it is doing and why, since I am only just coming to grips with the basics. :(
     
  9. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Save it for a bit later then, you're gonna be grateful ;)

    For now, continue using regular envelopes, then, and live with their restrictions (i.e. Curve parameter works only on Attack and not any other time variable parameter).
     
  10. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Yeah, will do.
    I'm sure it will come in handy at some point, so thanks. ;)

    BTW is there any way to have the knobs able to receive typed values?
    Because sometimes I find it easier just to input a number, rather than try and twist the knobs.
     
  11. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Scripted knobs cannot take in typed values. The only scripted control which takes in typed values is ui_value_edit.
     
  12. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Okay. So I'd have to fake that too, if I wanted these! :/
     
  13. paoling

    paoling NI Product Owner

    Messages:
    26
    Thank you so much Evildragon the example was very very useful...!
     
  14. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    So going by the code above, is there any way to replace the knobs in the code with that "ui_value_edit" instead?

    It's just that I really hate the lack of precision that the knobs offer.
    I can't handle the fact that I can only get -99.7db or -100.9db value, for example, from a knob.

    I want a -100db exactly dammit. :D
     
  15. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Of course, just replace "declare ui_knob" with "declare ui_value_edit".

    But bear in mind that in that case the following commands should be removed if you have them: set_knob_label(), set_knob_defval(), set_knob_unit().
     
  16. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Oh, so it is that easy.

    I didn't want to try that in case I screwed it up. :D
     
  17. Big Bob

    Big Bob Forum Member

    Messages:
    606
    BTW Mario,

    It seems to me I remember some hoopla about K5 was supposed to include typed-value-entry for knobs. I wonder what happened to that? Must have gone the way of persistent string arrays ;-)

    Rejoice,

    Bob
     
  18. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    No, there was no talk about that, Bob - the talk was about text string entry control, ui_text_edit.

    Even then - I don't think you can type a string value and convert it to engine parameter value ;)
     
  19. ben_horwood

    ben_horwood Forum Member

    Messages:
    402
    Ok, so I've managed to get this far.

    Code:
    on init
    	make_perfview
    
    	declare $count
    
    	declare ui_value_edit $Attack (0,100000,1000) {max value 15ms}
    	declare ui_value_edit $Hold (0,100000,1000) {max value 15ms}
    	declare ui_value_edit $Decay (0,1000000,1000) {max value 25ms}
    	declare ui_value_edit $Sustain (-100,0,1) {goes to -00db for some reason?}
    	declare ui_value_edit $Release (0,1000000,1000) {max value 25ms}
    
    	$Attack := get_engine_par($ENGINE_PAR_ATTACK, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Hold := get_engine_par($ENGINE_PAR_HOLD, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Decay := get_engine_par($ENGINE_PAR_DECAY, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Sustain := get_engine_par($ENGINE_PAR_SUSTAIN, 0, find_mod(0,"ENV_AHDSR"), -1)
    	$Release := get_engine_par($ENGINE_PAR_RELEASE, 0, find_mod(0,"ENV_AHDSR"), -1)
    
    	make_persistent($Attack)
    	make_persistent($Hold)
    	make_persistent($Decay)
    	make_persistent($Sustain)
    	make_persistent($Release)
    
    	read_persistent_var($Attack)
    	read_persistent_var($Hold)
    	read_persistent_var($Decay)
    	read_persistent_var($Sustain)
    	read_persistent_var($Release)
    
    	message("")
    end on
    
    on ui_control ($Attack)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_ATTACK, $Attack, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_engine_par($ENGINE_PAR_ATTACK,$Attack,0,0,-1)
    end on
    
    on ui_control ($Hold)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_HOLD, $Hold, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_engine_par($ENGINE_PAR_HOLD,$Hold,0,0,-1)
    end on
    
    on ui_control ($Decay)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_DECAY, $Decay, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_engine_par($ENGINE_PAR_DECAY,$Decay,0,0,-1)
    end on
    
    on ui_control ($Sustain)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_SUSTAIN, $Sustain, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_engine_par($ENGINE_PAR_SUSTAIN,$Sustain,0,0,-1)
    end on
    
    on ui_control ($Release)
    	$count := 0
    	while ($count < $NUM_GROUPS)
    		set_engine_par($ENGINE_PAR_RELEASE, $Release, $count, find_mod(0,"ENV_AHDSR"), -1)
    		inc($count)
    	end while
    
    	set_engine_par($ENGINE_PAR_RELEASE,$Release,0,0,-1)
    end on
    
    Which seems to work for the most part...

    When I adjust the AHDSR the instrument editor it updates the text of the ui_value_edit boxes.
    But the problem that I am having is typing values into the ui_value_edit boxes gives me ""random" unrelated values.
    e.g. Typing 15.000 into the $Attack edit box will give me .29ms?

    The other thing that I am having difficulaty with is getting a -100db to 0db range for the sustain parameter?
    It seems that no matter what I declare for the ui_value_edit sustain box it still wants to revert to -oo to 0db?
     
  20. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    This is down to the structure of KSP. You cannot type the exact value in dB. You can only type an integer value from 0 to 1000000 (engine parameter value range). The actual parameter value is translated internally by Kontakt's script compiler. That's just how it is, and there's no help about it.

    For Sustain value edit, you want to have 0 to 1000000 range for the value edit to cover the full range of the Sustain knob. -100 as engine parameter value does not exist.


    To clarify, all Kontakt's parameters are represented in KSP by an engine parameter value, from 0 to 1000000. Each parameter has its own "taper" - so each actual parameter value (as you see it on a knob in Kontakt) gets a certain engine parameter value range. This means that you cannot link to the parameter value directly by using its real world value, you have to use the engine parameter values as a "translator" of sorts. It's a crude way of working, but it's down to integer-only nature of KSP.