What's new

On listener callback and real numbers

thecld

Member
Hi there,

I got a question for KSP gurus - let's say we have a table sequencer which we can use to modulate different parameters, for example volume (using MIDI, range 0-127), chorus speed (range 0-1000000) and filter cutoff (using MIDI, range 0-127). Our sequencer has range of 0-1000000, so to modulate volume and filter cutoff we have to convert the ranges and to do that I've created such macro:

Code:
macro convert_range(oldvalue,oldmin,oldmax,newmin,newmax,newvalue)
     newvalue := real_to_int(((((int_to_real(oldvalue) - int_to_real(oldmin)) * (int_to_real(newmax) - int_to_real(newmin))) / (int_to_real(oldmax) - int_to_real(oldmin))) + int_to_real(newmin)))
end macro


Is this something that could make Kontakt struggle, especially when done multiple times?

I'm asking because I'm working on an instrument and if I activate modulation of any parameter, then Kontakt's volume meter becomes slow & laggy which is weird, especially because there isn't any increase in the CPU load. Also, when I click on the wrench icon to open the instrument editor, then it becomes smooth again... :)

I've watched some videos on YT to see if other people have this problem too (don't have any other big 3rd party libraries beside the official NI stuff) and it seems like it's a normal thing for the wide instruments (wider than the standard 632 pixels). I've seen commercial videos showcasing a Kontakt instrument where the volume meter is slow & laggy, so it looks like it's just how it is or people don't care?

I'm really curious if you guys / gals noticed it or it's only me.

Cheers!
 
Hi, there.
Why not just use engine values for both volume and cutoff too?
Seems like a lot of unecessary math and floating point operations...
Is there something specific that requires you to sequence using midi ranges for those parameters?
 
Hi, there.
Why not just using engine values for both volume and cutoff too?
Seems like a lot of unecessary math and floating point operations...
Is there something specific that requires you to sequence using midi ranges for those parameters?

I'm using MIDI instead of engine values to avoid 'choppy' modulation. You can set the modulation lag when modulating MIDI, which doesn't work when modulating engine values.
 
They're not really that bad. They are about twice as slow as integer operations (you can do the test yourself with $KSP_TIMER), but nothing that would choke the listener callback. However it doesn't make sense to me to use a sequencer with 0-1000000 range if you're going to quantize it back down to 128 steps... You could do that quantization to 128 steps without floating point math, too (since the starting value is pretty large) - just divide the output from your sequencer with 7874 (1000000 / 127, roughly)...

In all honesty, I'd look at reducing your sequencer range to 0-1000, and also if you know in advance to which values you need to scale things (especially in this case where minimum value remains the same and is essentially dead weight in your calculation), it's more efficient to do a single simple division rather than a whole linear interpolation macro. Would you see the difference in action? Probably not, our CPUs are really fast these days. But it's cleaner code.
 
They're not really that bad. They are about twice as slow as integer operations (you can do the test yourself with $KSP_TIMER), but nothing that would choke the listener callback. However it doesn't make sense to me to use a sequencer with 0-1000000 range if you're going to quantize it back down to 128 steps... You could do that quantization to 128 steps without floating point math, too (since the starting value is pretty large) - just divide the output from your sequencer with 7874 (1000000 / 127, roughly)...

In all honesty, I'd look at reducing your sequencer range to 0-1000, and also if you know in advance to which values you need to scale things (especially in this case where minimum value remains the same and is essentially dead weight in your calculation), it's more efficient to do a single simple division rather than a whole linear interpolation macro. Would you see the difference in action? Probably not, our CPUs are really fast these days. But it's cleaner code.

Thanks for the info! Those 3 destinations that I wrote about are just as an example - I didn't know that the discussion will lead that way, sorry for the confusion! The reason why I'm using such a big range is because I have a matrix with 6 destinations which you can switch on & off, some has range of 0-127, some 0-1000000 but I really like your idea of simplifying the range conversion by simple division. I also have another sequencer which is bipolar and in this case I think I won't be able to simplify the conversion process because in this case the knob value (of modulated parameter) is always the 0, so the range conversion is dynamic.

After further tests I can confirm it really doesn't matter for the CPU because even if I delete parts responsible for range conversion or even whole function responsible for modulating various parameters then the CPU load isn't changing. If I don't play anything but the matrix is active, so the on listener is working and waiting for note input (the reason for that is to 'preload' the modulation to avoid any lags - I tried restricting it to do it only once each time any table is changed but it doesn't change much) CPU load is jumping between 0% and 1% and if I play a note the CPU is jumping between 6% and 11% and that's when every possible modulation is active, so I guess it's not that bad because my other much simpler scripts are usually using around 7% to 10% CPU when playing sound which seems pretty standard even for the official NI stuff.

However my computer was still extremely hot, the CPU temp was jumping like crazy, etc. It looks like it's because of animated labels. If the instrument kept running but the interface was hidden then everything worked ok, but when I maximised the window or just closed the instrument editor then it went crazy again. So what I did is I deleted parts responsible for animating labels from the on listener callback and now it works much smoother and my computer temp is closer to normal temperatures. It still seems like Kontakt is struggling with displaying wide interface (1000px) because it works smoother if I change it back to standard 632px (basically just cropping the whole interface just for testing hehe).

So, is using set_control_par(ui_id, CONTROL_PAR_PICTURE_STATE, value) in the on listener callback such a bad idea? I'm using it for animating little dots showing modulation position in sequencers and also to show which knobs are being affect by the modulation (similar to how it's in Massive, but with a simple dot).

Sorry for the long post and for any mistakes (English isn't my main language) and I'd really appreciate it if you guys could let me know what do you think about all of this.

Thanks!

EDIT: I think I'm gonna try to move as much stuff from the on listener callback to on note callback, it seems like the only valid option for now :).
 
Last edited:
Yeah while you can do animations, it's not a good idea to overdo them. :)

Not sure what your listener speed is, but in general you don't want to update GUI on every millisecond... 1/30th of a second should be quite enough (which roughly means if you run your listener at 1 millisecond interval, the fastest possible, you will want to update your GUI roughly every 34 listener ticks) :) If it's not smooth enough, maybe 1/50th of a second would be enough (50 Hz), so every 20th listener tick.
 
Yeah while you can do animations, it's not a good idea to overdo them. :)

Not sure what your listener speed is, but in general you don't want to update GUI on every millisecond... 1/30th of a second should be quite enough (which roughly means if you run your listener at 1 millisecond interval, the fastest possible, you will want to update your GUI roughly every 34 listener ticks) :) If it's not smooth enough, maybe 1/50th of a second would be enough (50 Hz), so every 20th listener tick.

My listener speed = set_listener($NI_SIGNAL_TIMER_BEAT,8), so for example if I'm correct at the BPM=80 on listener updates around 1/11th of a second (1/32 = 94ms and 1000/94 = 10.64)? If yes, then it should be efficient enough, so the problem is probably somewhere else?
 
I'm not sure why you would use a tempo-synced listener for updating graphics. You should use $NI_SIGNAL_TIMER_MS for that. Beat timer depends on host tempo, so updates will go faster or slower depending on it...
 
I'm not sure why you would use a tempo-synced listener for updating graphics. You should use $NI_SIGNAL_TIMER_MS for that. Beat timer depends on host tempo, so updates will go faster or slower depending on it...

Sure thing, but I'm also creating clocks for my sequencers in the on listener, that's why I'm using tempo-synced one. Also the only graphics that are being updated in the on listener are sequencer / modulation graphics, so I thought that it doesn't matter that these are synced. Is that thinking correct? :)
 
Top Bottom