# Question about using on_listener



## GiuseppeS+OS (Jun 14, 2021)

Hello,
I'm working on a script that requires deepening my knowledge of the on_listener callback a little bit.
I have created a custom LFO based on a drawable table which is assigned to the filter cutoff , so far I had no problem in synching it to different time signatures and 'resetting' it every time that a new note is played (it will be a monophonic synth kind of instrument).
However, my plan is to set multiple versions of said LFO, each one assigned to a different parameter (pitch, distortion etc), and I want them to be independently settable: for example, I might want to synch the cutoff LFO to a quarter note and the distortion LFO to 16th. 
Is that possible? As long I understand, you can set just one timer in the on_listener callback, so I couldn't set separate timers for each LFO, according to the idea I've just described.
Is this correct?
Best,
Giuseppe


----------



## EvgenyEmelyanov (Jun 14, 2021)

Hi Giuseppe. I think it's possible. Just create a counter that equals to, let's say, 32nd or 64th. And then use simple math. 

1/16 = 1/32 + 1/32. And 1/4 = 1/32 * 4. 

It means to get 16th you need to count two 32nd. And to get a quarter note you need to count four 32nd.


----------



## GiuseppeS+OS (Jun 14, 2021)

@EvgenyEmelyanov Hi Evgeny, thanks a lot for your reply 
That's what I thought in first instance, but here's my question: would that work if there are multiple LFO which need to run simultaneously (each on set to different parameters) at different timings?
I change the NI_SIGNAL_TIMER_MS of the first LFO by using change_listener_par in the ui_control callback associated to a slider, however, there'll be another slider controlling the frequency of a second LFO. Wouldn't this overwrite the NI_SIGNAL_TIMER_MS value?


----------



## Lindon (Jun 14, 2021)

yes just use an if statement in the on_listener that checks the modulo of what you want for each LFO, and execute your LFO code in its own if-modulo-check segment of code...


----------



## GiuseppeS+OS (Jun 14, 2021)

Lindon said:


> yes just use an if statement in the on_listener that checks the modulo of what you want for each LFO, and execute your LFO code in its own if-modulo-check segment of code...


thanks a lot, this is very helpful!


----------



## EvgenyEmelyanov (Jun 14, 2021)

Yeah, no need to use change_listener_par in this situation IMO. Set the listener parameter to the smallest step - let's say 1/128 or 1/64.

And then use if-statements to count these small steps for each listener iteration. Let's say you have two LFO modules. Count these little steps for each of them. Let's say, for the 1/16 step you should wait for four 1/64 iterations.

If you change the speed of your LFO then all you need to do is to change these parameters. How many on_listener little steps you should count before activating LFO.


----------



## GiuseppeS+OS (Jun 14, 2021)

EvgenyEmelyanov said:


> Yeah, no need to use change_listener_par in this situation IMO. Set the listener parameter to the smallest step - let's say 1/128 or 1/64.
> 
> And then use if-statements to count these small steps for each listener iteration. Let's say you have two LFO modules. Count these little steps for each of them. Let's say, for the 1/16 step you should wait for four 1/64 iterations.
> 
> If you change the speed of your LFO then all you need to do is to change these parameters. How many on_listener little steps you should count before activating LFO.


thanks Evgeny, now things are much clearer, I really appreciate


----------



## GiuseppeS+OS (Jun 17, 2021)

Hey guys,
just wanted to thank you again as your suggestions definitely set me on the right track.
I've started from a listener of 1/64 and from there I've managed to set the LFO on longer waves (1/32,1/16 etc). 

As the LFO waveform is read from the values of a table with a fixed number of steps, I've written a function which 'blocks' the table steps counter for a number of listener ticks according to the speed I wanted to achieve. To do this I 've 'resampled' the waveform defined in the table to a longer array, as jumping (for example) by two steps at each listener tick results in a wave that is half the original one. Also, in order to keep a good resolution of the resulting wave I've used linear interpolation, as the longer is the final wave (slower LFOs), the more it tends to resemble a step function with this technique. 
Btw, thanks again, now I have to figure out to properly sync the even LFO speeds , as setting the cursor to jump of 3 or 6 steps just doesn't work so far.
Thanks again!
G.


----------



## Lindon (Jun 17, 2021)

GiuseppeS+OS said:


> Hey guys,
> just wanted to thank you again as your suggestions definitely set me on the right track.
> I've started from a listener of 1/64 and from there I've managed to set the LFO on longer waves (1/32,1/16 etc).
> 
> ...


look up the modulo command... you need a counter in the on_listener and then apply the modulo command to that to get any repeat of on_listener calls that you want...


----------



## thesteelydane (Jun 17, 2021)

GiuseppeS+OS said:


> Hey guys,
> just wanted to thank you again as your suggestions definitely set me on the right track.
> I've started from a listener of 1/64 and from there I've managed to set the LFO on longer waves (1/32,1/16 etc).
> 
> ...


I'd be very curious to see how you did the linear interpolation, because I'm working on something similar.


----------



## GiuseppeS+OS (Jun 18, 2021)

thesteelydane said:


> I'd be very curious to see how you did the linear interpolation, because I'm working on something similar.


Hi, I'll try to summarise how I've proceeded so far.

As suggested by the posts above, if I set the listener to a given timer (64th in my case), I can set the LFO to slower speeds (longer waves) by ‘blocking’ the LFO table counter each 2 or more listener ‘ticks’ , by using a separate cursor for each of them , which I called $j and $tick.

For example, if I want to set the LFO to 32th, I need to block $j for two consecutive values of $tick( $j is 0 for $tick =0 and 1, $j= 1 for tick =2 and 3 etc). In other words, the $j counter runs at half speed of the listener.
This results in an LFO wave whose values are equal two by two, which gives the problem of a degraded resolution in the resulting 'halved' wave. Obviously this problem gets much worser for slower LFOs, where the resulting wave tends to be a step function, as you block the table counter for an higher number of ticks.​To solve this issue, rather than blocking the 32th wave value to the previous one, you can use linear interpolation. In this example, this is only applied to the odd values of $tick, as they're the only one that 'block' the table counter:

LFOwave = (table(previous tick)+table(current tick))/2, for tick=1,3,5 etc

This is a reasonable approximation, I've plotted the original wave and the 'upsampled' one in Octave and they've the same shape.

For slower speeds this approach needs to be changed: in case of setting the LFO on 16th, you have to 'block' the table counter each 4 listener ticks: this means that the listener 'leaves out' the sine wave values each 3 ticks, so I've calculated these 'jumped' values with the linear equation between each set of ticks which are not an integer multiple of 4 (use modulo). So, at tick 1,2 and 3, you can calculate the LFO with the linear equation calculated between ticks 0 and 4. The equation needs to be recalculated at each set of 4 ticks, as the angular coefficient changes (slop of the line).

This works well for a sine wave, I suspect it will give me some problems with a square set to long LFOs, as the interpolation might smooth out the the values around the middle of the wave, where it jumps from 1 to -1.


----------



## Lindon (Jun 18, 2021)

GiuseppeS+OS said:


> [snip]​
> This works well for a sine wave, I suspect it will give me some problems with a square set to long LFOs, as the interpolation might smooth out the the values around the middle of the wave, where it jumps from 1 to -1.


That might not work out so bad as you imagine, if you use an oscilloscope to look at a "classic" square wave from a hardware synth (e.g. a mini moog) you'll see its not "ideal" and has all sorts of artefacts (including rounding) at the transition points...


----------



## GiuseppeS+OS (Jun 18, 2021)

Lindon said:


> That might not work out so bad as you imagine, if you use an oscilloscope to look at a "classic" square wave from a hardware synth (e.g. a mini moog) you'll see its not "ideal" and has all sorts of artefacts (including rounding) at the transition points...


Yes, I did some tests by manually adding some 'noise' to that area (the whole point is making a drawable wave) and even on extremely low LFOs I can't hear a substantial difference. And I'm testing a sinewave tone, probably with more complex samples it wouldn't even worth bother with some corrections to achieve a perfect instant jump.


----------



## GiuseppeS+OS (Jan 19, 2022)

Hello everyone, 

sorry for resuscitating this thread, but I got back working on said script and I'm sort of stuck in figuring out the listener timing. To recap, I have a drawable LFO created as a bipolar table of 128 steps, the idea is to change the LFO wave shape in real time by "drawing" on the table. So, if I want to set the LFO to, say, 1/16, it means that a complete cycle (period) of 128 steps has to be run each 1/16 measure. So, I've set the listener as:

set_listener($NI_SIGNAL_TIMER_MS,$DURATION_SIXTEENTH/4/128).

Here, $DURATION_SIXTEENTH/4 represents the maximum rate I want for the LFO (1/64), 128 is the number of steps of the table. So, each LCB "tick" corresponds to a step further within the table (the index is reset at 128). The problem is that by using this division you obviously have decimal numbers, which are by default approximated to the previous integer, creating the effect of the LFO running faster than the actual host tempo. I've tried to compensate by calculating the difference in microseconds between the "real" timing and the approximated one, let's call it $d, then I've added a wait($d) at the end of each complete table iteration, in order to keep it on time. It doesn't work at all though, the LFO always ends up running faster than it should, so I guess probably there's a basic problem about how I need to set the listener tempo?


----------



## EvgenyEmelyanov (Jan 19, 2022)

Why did you set the listener to MS instead of BEAT? This is the first thing that came to my mind.


----------



## GiuseppeS+OS (Jan 19, 2022)

EvgenyEmelyanov said:


> Why did you set the listener to MS instead of BEAT? This is the first thing that came to my mind.


Hi Evgeny,
seems like with BEAT I can't set a resolution smaller than 1/16. If I set 32 or more in the argument Kontakt gives me an error ("Beat timer divisor not valid"). Am I missing something stupid?

EDIT: Also, in my understanding the listener can be fired at maximum 24/beat, which is much lower than needed in my case


----------



## EvgenyEmelyanov (Jan 19, 2022)

GiuseppeS+OS said:


> Hi Evgeny,
> seems like with BEAT I can't set a resolution smaller than 1/16. If I set 32 or more in the argument Kontakt gives me an error ("Beat timer divisor not valid"). Am I missing something stupid?
> 
> EDIT: Also, in my understanding the listener can be fired at maximum 24/beat, which is much lower than needed in my case



Please, check everything I write here because I didn't test it myself.

You can set the listener to 24. Correct me if I am wrong, but it means that you have 24 ticks in a single beat.

```
set_listener($NI_SIGNAL_TIMER_BEAT,24)
```

Then, you can create a variable called $divide, which will help you check a current song position. So, again, you count your song position in the listener section, divide it by $divide, and look at the result.


```
on init
    set_listener(NI_SIGNAL_TIMER_BEAT, 24)
    declare divide := 96
    declare song_pos
end on

on note
    song_pos := -1
end on

on listener
    if $NI_SIGNAL_TYPE = $NI_SIGNAL_TIMER_BEAT
        inc(song_pos)

        if song_pos mod divide = 0
            //something
        end if
    end if
end on
```

*I believe you will get the idea.*

And here are the values:

divide := 96 {A whole note}
divide := 48 {A 1/2 note}
divide := 24 {A 1/4 note}
...etc

divide := 32 {A 1/2 triplet note}
divide := 16 {A 1/4 triplet note}
divide := 8 {A 1/8 triplet note}
... etc


----------



## GiuseppeS+OS (Jan 19, 2022)

Thanks a lot Evgeny.
The procedure you outline here is clear and I've implemented it for some other time-related functions within the same script. 
However, in the case of a 128 steps table used as an LFO, you virtually need much more ticks per beat: if the LFO rate is set to 1/16, an entire cycle of 128 steps takes that time to be completed: if tempo is 120 bpm, that corresponds to 125000 microsec, so each step takes roughly 976.5 microsec (automatically rounded to 976). That's why I've used MS instead of BEAT and that's where I have the problem of the LFO running faster than it should (the wrong timing changes remarkably at different bpm values).

Curiously, I've tried to iterate through the same LFO table in the _on note_ callback , by simply using _wait_ commands to obtain that resolution (again, sixteenth/128): it works fine and the LFO is perfectly on time, so it's not a problem of decimal rounding, there must be something else I'm missing in the LCB.
Probably I'll keep the table cycling in _on note _before figuring out the actual issue in listener.


----------



## thesteelydane (Mar 16, 2022)

GiuseppeS+OS said:


> Thanks a lot Evgeny.
> The procedure you outline here is clear and I've implemented it for some other time-related functions within the same script.
> However, in the case of a 128 steps table used as an LFO, you virtually need much more ticks per beat: if the LFO rate is set to 1/16, an entire cycle of 128 steps takes that time to be completed: if tempo is 120 bpm, that corresponds to 125000 microsec, so each step takes roughly 976.5 microsec (automatically rounded to 976). That's why I've used MS instead of BEAT and that's where I have the problem of the LFO running faster than it should (the wrong timing changes remarkably at different bpm values).
> 
> ...


I have a solution for this problem. Please note this is not the complete code, it's just to show you one possible way of getting more ticks than 24 per quarter note. The beauty is it follows tempo changes really well, because it resets the number of "sub ticks" 24 times per quarter note. Hope it makes sense, it should give you some ideas.



```
on init
    set_listener($NI_SIGNAL_TIMER_BEAT, 24)
    set_listener($NI_SIGNAL_TIMER_MS,1000)
end on

on listener
    if NI_SIGNAL_TYPE = NI_SIGNAL_TIMER_BEAT
        change_listener_par($NI_SIGNAL_TIMER_MS,$DURATION_QUARTER/24/6)
            {do your math for resetting the LFO here and determine how many subticks a whole cycle needs}
    else
        {here you have 6 ticks per every 1/24 quaternote beat to do something with}
    end if
end on
```


----------



## EvilDragon (Mar 16, 2022)

...or you can just wait_ticks() inside that single beat listener tick.


----------



## polypx (Mar 16, 2022)

I think there's some confusion between MIDI ticks (ie. wait_ticks) and the listener TIMER_BEAT ticks. Probably because both are referred to as "ticks".


----------



## EvilDragon (Mar 16, 2022)

No they're the one and the same, the difference is in resolution - with listener you get 24 ticks per quarter note, whereas wait_ticks() has the usual 960 PPQ resolution. So, you can run your listener at 24 ticks per quarter, which gives you 1/64T, and then you can subdivide that one tick further by using the appropriate wait_ticks() duration.


----------



## polypx (Mar 16, 2022)

Well that's what I mean, they're not the same tick because they're different lengths of time. And even when you wait_ticks inside the listener, you wait MIDI ticks, not listener ticks.


----------



## EvilDragon (Mar 16, 2022)

Listener ticks _are_ kinda like coarse MIDI ticks - they are locked to host beat grid, so for all intents and purposes, doing this:


```
on init
    declare i
    set_listener(NI_SIGNAL_TIMER_BEAT, 1)
end on

on listener
    if NI_SIGNAL_TYPE = NI_SIGNAL_TIMER_BEAT
        for i := 0 to 3
            // stuff
            wait_ticks(240)
        end for
    end if
end on
```

should behave pretty much the same as:


```
on init
    set_listener(NI_SIGNAL_TIMER_BEAT, 4)
end on

on listener
    if NI_SIGNAL_TYPE = NI_SIGNAL_TIMER_BEAT
        // stuff
    end if
end on
```


----------



## GiuseppeS+OS (Mar 17, 2022)

Thank you everyone for your insights !


----------



## GiuseppeS+OS (Apr 5, 2022)

thesteelydane said:


> I have a solution for this problem. Please note this is not the complete code, it's just to show you one possible way of getting more ticks than 24 per quarter note. The beauty is it follows tempo changes really well, because it resets the number of "sub ticks" 24 times per quarter note. Hope it makes sense, it should give you some ideas.
> 
> 
> 
> ...


Hi, sorry for replying so late. This is what I tried in first instance, but the problem I'm encountering is due to the fact that the resolution I need is way higher. My drawable LFO is a 128 steps table, where I draw the shape of the LFO. If I want the LFO rate to be at 16th, then every step corresponds to $DURATION/(4*128). My first attempt was exactly to use change_listener_par() and set it to $DURATION_QUARTER/(16*128), as I want the maximum rate of my LFO to be 64th. Well, the result is that the LFO runs a bit faster than it should. Instead, if I just use wait() in the on_note CB, it works flawlessly.
I thought it had something to do with decimal rounding, but in this case the same issue should appear seamlessly with wait(), so I really have no idea.


----------

