# On Listener and starting off the bar.



## Discotron (Aug 12, 2017)

Hi guys and girls,
I'm trying to make some kind of note sequencer and it is kind of working and looping around playing notes(if I press stop on my DAW and reset to start of bar) but there is a problem with starting off bar - When starting off beat it will only wait for the next bar if the DAW is two beats into the bar.

If I start the DAW for example before the second beat, the sequencer starts up right away and is out of time, if I start it after the 2nd beat the sequencer waits until it reaches the next bar and the starts which is great.

Weirdly if I change the *$NI_SIGNAL_TIMER_BEAT,4* to *$NI_SIGNAL_TIMER_BEAT,1* it works, but then the loop is slow. I thought $NI_SIGNAL_TIMER_BEAT,4 is the same as wait $DURATION_SIXTEENTH.

The script below is updating two variables (*$daw_is_playing* and *$bar_start*)before the sequencer is allowed to start.

Play is pressed on the DAW and *$daw_is_playing* is set to 1 and then *if ($bar_position mod $SIGNATURE_NUM = 0) sets $bar_start to 1* which then satisfies the if statement below and the loop starts
if ($daw_is_playing = 1 and $bar_start = 1)

loop around
play notes etc
increase $step etc
end if

Am I way off the mark here?

on init
set_listener($NI_SIGNAL_TRANSP_STOP,1)
set_listener($NI_SIGNAL_TRANSP_START,1)
set_listener($NI_SIGNAL_TIMER_BEAT,4)
end on

on listener
if ($NI_SIGNAL_TYPE .and. $NI_SIGNAL_TRANSP_STOP # 0)
$bar_start :=0
$daw_is_playing :=0
end if{this stops the sequencer}

if ($NI_SIGNAL_TYPE .and. $NI_SIGNAL_TRANSP_START # 0 )
$daw_is_playing :=1
$step := 0
end if{this sets up one of the needed variables to start the loop }


if ($NI_SIGNAL_TYPE .and. $NI_SIGNAL_TIMER_BEAT # 0)
$bar_position := $NI_SONG_POSITION/960

if ($bar_position mod $SIGNATURE_NUM = 0)
$bar_start :=1
end if{this sets the final variable needed to start the loop below}


if ($daw_is_playing = 1 and $bar_start = 1)

loop around
play notes etc
increase $step etc
end if

Any help or guidance about the above or the on listener call back would be very appreciated.
Thanks guys.


----------



## EvilDragon (Aug 12, 2017)

Why not run the listener at the highest rate (24 ticks per beat)? Then the delay will be at most 1/64T.


You don't need $daw_is_playing variable at all. Just use $NI_TRANSPORT_RUNNING instead, it's the same thing, and built into KSP.


----------



## Discotron (Aug 13, 2017)

Thanks.
Just tried it and still not working and the speed of the sequencer has become really fast. 
All I want it to do is not start my scripted sequencer until the pointer in the DAW reaches the start of the next bar. So, if I press stop on my DAW and it has stopped somewhere in-between a bar and then I press start again I just want my sequencer to wait until the DAW reaches the start of the next bar and then start my sequencer so it is is time. Is it not possible to do this in Kontakt? I thought the script below would make it wait until the start of a bar was reached.

if ($bar_position mod $SIGNATURE_NUM = 0)
$bar_start :=1
end if

If I'm using $NI_TRANSPORT_RUNNING can I now totally remove the start signal type check from the script - if ($NI_SIGNAL_TYPE .and. $NI_SIGNAL_TRANSP_START # 0 )
$step := 0
end if
Thanks


----------



## EvilDragon (Aug 13, 2017)

Ah, I get ya. I don't think you want Kontakt to wait for next bar. What would be better is to start from the current position in the bar, would it not? For this, I recommend checking out the arpeggiator part of Retro Machines II script, it contains some useful code for this exact purpose. Check it out (it's in tick_received() function at the beginning):



If you want to restart $step, you need to keep that check in. However I'm not sure why you're not simply doing if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TRANSP_START), works to the same effect and it's shorter to write. 


By the way, please use CODE tags when posting code.


----------



## Discotron (Aug 13, 2017)

Thanks very much for your help. Yes, starting at current position would be amazing if I could get that to work. I will go through the arp code and see what absolute disaster I can make of it


----------



## Lindon (Aug 14, 2017)

I've put this up here before but one more time, if you want your sequencer to start at bar start(assuming a 4/4 signature), and a way to see which bar you are in:

```
$bar_position := $NI_SONG_POSITION/960

if ($NI_SONG_POSITION-(3840*($NI_SONG_POSITION/3840))<4)
     {do your stuff here...}
end if
```


----------



## EvilDragon (Aug 14, 2017)

That of course assumes everybody lives and breathes in 4/4 all the time (and Retro Machines II script is much the same)


----------



## Discotron (Aug 14, 2017)

```
Code:
$bar_position := $NI_SONG_POSITION/960

if ($NI_SONG_POSITION-(3840*($NI_SONG_POSITION/3840))<4)
     {do your stuff here...}
end if
```

Thanks for this.
I'm still getting a delay which seems to vary in time when using this. If I stop my DAW and ensure it returns to the start of bar and press play, all is fine but if I start somewhere other than the beginning of a bar my sequencer waits until it reaches the start of the next bar(using above code snippet), but seems to be a bit early in timing.


----------



## Lindon (Aug 15, 2017)

its using the song position pointer sent to it by your DAW so its not late....and yes its SUPPOSED to wait for a bar start. That's the point.


----------



## Discotron (Aug 15, 2017)

Lindon said:


> its using the song position pointer sent to it by your DAW so its not late....and yes its SUPPOSED to wait for a bar start. That's the point.


yes, it is waiting as it is supposed to, but when it lands on the beginning of a bar and starts my sequencer it is starting out of time, but is not out of time if I stop my daw and reset to the beginning of the bar that way. It must be my sequencer code causing the issue. Thanks


----------



## Discotron (Aug 15, 2017)

```
on init
    set_ui_height_px(200)
    make_perfview
    set_listener($NI_SIGNAL_TRANSP_STOP,1)
    set_listener($NI_SIGNAL_TRANSP_START,1)
    set_listener($NI_SIGNAL_TIMER_BEAT,4)

    declare $bar_start :=1
    declare $tick_counter :=0
    declare $ticks_per_step :=1
    declare $bar_position
    declare $play_the_notes
    declare $step_counter :=0
    declare ui_knob $start_knob(0,31,1)
    declare ui_knob $end_knob(0,31,1)
    declare ui_table %note_table[32] (20,20,127)
 

    set_control_par(get_ui_id(%note_table),$CONTROL_PAR_WIDTH,500)
    set_control_par(get_ui_id(%note_table),$CONTROL_PAR_HEIGHT,100)

    move_control_px($end_knob, 125,140)
    move_control_px($start_knob, 25,140)
    move_control_px(%note_table, 1,26)

    make_persistent($start_knob)
    make_persistent($end_knob)
    make_persistent(%note_table)
end on

on note
    allow_group($ALL_GROUPS)
end on

on listener

  select ($NI_SIGNAL_TYPE)

    case $NI_SIGNAL_TRANSP_STOP
    $step_counter := $start_knob
    $tick_counter := 0
    $bar_start :=0
 


    case $NI_SIGNAL_TRANSP_START
    $step_counter := $start_knob
    $tick_counter := 0
    $bar_start :=0
 

    case $NI_SIGNAL_TIMER_BEAT
    $bar_position := $NI_SONG_POSITION/960

    if ($NI_SONG_POSITION-(3840*($NI_SONG_POSITION/3840))<4)
         $bar_start :=1
    end if

    if ($NI_TRANSPORT_RUNNING = 1  and $bar_start = 1 )

            
        if (%note_table[$step_counter] > 0) {only play a note if table value is above 0}               
            $play_the_notes := play_note(%note_table[$step_counter]+36,127,0,$DURATION_SIXTEENTH)
        end if

        inc($tick_counter)
         if ($tick_counter >= $ticks_per_step)
            inc($step_counter)
            if ($step_counter >= $end_knob)
                $step_counter := 0
            end if
            $tick_counter := 0
         end if

    end if {the end if for if ($NI_TRANSPORT_RUNNING = 1  and $bar_start = 1 )}
  end select

    end on
```

This is the code I'm using. It works if I press stop on my DAW and then press play, but if I start off beat somewhere it waits for the next bar as expected and then starts but sometimes the playing sequencer above feels a tad out of time, like it has been started a fraction too early or maybe late.

It is probably my code that is causing the problems. Am I on the correct lines with the code above?

Thank you.


----------



## EvilDragon (Aug 16, 2017)

Can't you do something with the Retro Machines code I posted above?

You basically just need to modify what happens when a step is reached in tick_received() function. Everything else is practically set up for you.

(Also, make sure that you reset $tick_counter to -1, not 0, for reasons.)


----------



## Discotron (Aug 16, 2017)

EvilDragon said:


> Can't you do something with the Retro Machines code I posted above?
> 
> You basically just need to modify what happens when a step is reached in tick_received() function. Everything else is practically set up for you.
> 
> (Also, make sure that you reset $tick_counter to -1, not 0, for reasons.)


Thanks.
I'm curious as to what is wrong with the code above as I'm trying to further my understanding of on listener. I can't understand why the above code does not work as it waits for bar as it should, but the sequencer starts a bit out of time.

Also, if I set_listener($NI_SIGNAL_TIMER_BEAT,24) the thing goes haywire and sounds like it is repeating notes really quickly. Should it always be set to set_listener($NI_SIGNAL_TIMER_BEAT,4) ?


----------



## EvilDragon (Aug 16, 2017)

You set the listener rate however you want, that's why you can decide which number of ticks to respond to _per beat_. That means your code needs to be adjusted in order to match the rate you want the notes to be played at. In other words, when you have listener running at 4 ticks per beat, that equals 1/16 notes, because you respond on every tick.

If you run it at 24 ticks per beat, in order to get 1/16th notes, you need to respond to *every 6th tick*, and you do that by counting ticks and then only playing notes on each n-th tick. Modulo operation is a godsend for stuff like this. Again, this has all already been handled in the Retro Machines script, so study it a bit.


----------



## Discotron (Aug 16, 2017)

Thanks for the explanation, it is starting to make sense why and how things are set in the on listener callback.


----------



## Discotron (Aug 11, 2018)

I've spent some time now pulling apart the NI Retro Machines script and trying things out, but I'm damned if I can get it to work properly(at least I think it is not working). It does start and run and plays a note(if you put a sample in the first group and stretch it over all key ranges), but I'm not convinced it is finding song start position and starting the sequencer from it. Am I on the right tracks with this or is it miles off?

I have also experimented with showing table position by having a table that is filled with a value using the step_counter and a variable that holds the previous step counter which gets filled with 0. Is this how you guys would make that kind of position indicator?

Thanks.


```
make_perfview
set_ui_height_px(400)
set_ui_width_px(640)
declare const NUM_OF_BEATS := 4 { i.e. 4 = 4/4 bar }
declare const TICKS_PER_BEAT := 24
declare const MAX_NUM_OF_STEPS := 32
declare ui_knob  beat_grid (1,12,1)
declare seq_running
declare step_counter
declare step_duration
declare sync_position
declare ticks_per_step := 6
declare tick_counter
declare Steps
declare $PLAY_SOUND
declare $prev_step_counter
set_listener($NI_SIGNAL_TRANSP_STOP,1)
set_listener($NI_SIGNAL_TRANSP_START,1)
set_listener(NI_SIGNAL_TIMER_BEAT,24)
make_persistent(beat_grid)
make_persistent(ticks_per_step)
read_persistent_var(Steps)

declare ui_table %notes[32] (10,10,61)
set_control_par(get_ui_id(%notes),$CONTROL_PAR_WIDTH,400)
set_control_par(get_ui_id(%notes),$CONTROL_PAR_HEIGHT,200)
make_persistent(%notes)
move_control_px(%notes,100,50)

declare ui_table %notes_marker[32] (10,10,30)
set_control_par(get_ui_id(%notes_marker),$CONTROL_PAR_WIDTH,400)
set_control_par(get_ui_id(%notes_marker),$CONTROL_PAR_HEIGHT,50)
move_control_px(%notes_marker,100,250)

ticks_per_step := 16
beat_grid := 1
Steps := 32
message("")
end on


function print_position {fill note position table with a value}
%notes_marker[step_counter] :=30
end function

function delete_position{fill previous note  position table with a 0 to delete value/turn off position}
%notes_marker[$prev_step_counter] :=0
end function
 
function tick_received()
    if sync_position = 0
     inc(tick_counter)
        if tick_counter >= ticks_per_step{a step has occurred}
           $prev_step_counter := $step_counter
            inc(step_counter)
            call delete_position  {delete previous position from position table by filling with 0}
            if step_counter >= Steps
               step_counter := 0
            end if
            tick_counter := 0
        end if
    end if

    if tick_counter = 0 { we have reached a step }
        call print_position{fill position table with value to indicate sequencer position}
        step_duration := DURATION_QUARTER / beat_grid
        $PLAY_SOUND := play_note(%notes[step_counter],127,1,step_duration)
    end if
end function

function song_position
    if  NI_SONG_POSITION = 0 {The arp script has this as < 0 but that did not seem to work here so I set it to = 0}
        sync_position := 0
        step_counter := 0
        tick_counter := -1
        else
            tick_counter := (NI_SONG_POSITION / 40) mod ticks_per_step
            step_counter := (NI_SONG_POSITION / (960 / beat_grid)) mod Steps
    end if
    message ("Tick counter = " & tick_counter & "step counter" & step_counter)
end function

on listener
select ($NI_SIGNAL_TYPE)
    case $NI_SIGNAL_TRANSP_STOP
    call song_position

    case $NI_SIGNAL_TRANSP_START
    call song_position
 
    case $NI_SIGNAL_TIMER_BEAT
    if ($NI_TRANSPORT_RUNNING = 1)
       call tick_received
    end if
end select
end on
```


----------



## EvilDragon (Aug 12, 2018)

There's a reason why the RMII script had NI_SONG_POSITION < 0, keep it that way.

You're not refreshing the rate of listener at all in that script, you need to add UI callback for this (and it's not supposed to be called beat_grid either ). Here's extracted RMII script which you might find easier to parse.


----------



## Discotron (Aug 12, 2018)

Thanks. When you say refreshing the rate of the listener, do you mean it needs the change_rate function etc.
I left that out to keep it all shorter to make it easier for me to read as I try and understand it all. In the script I think I set it to I think 16ths(if ticks per step is 6?) I'm probably wrong.

```
[CODE]function change_rate()
    select (SyncRate)
        case 0 { 1/4 }
            ticks_per_step := 24
            beat_grid := 1
        case 1 { 1/8 }
            ticks_per_step := 12
            beat_grid := 2
        case 2 { 1/8 T }
            ticks_per_step := 8
            beat_grid := 3
        case 3 { 1/16 }
            ticks_per_step := 6
            beat_grid := 4
        case 4 { 1/16 T }
            ticks_per_step := 4
            beat_grid := 6
        case 5 { 1/32 }
            ticks_per_step := 3
            beat_grid := 8
        case 6 { 1/32 T }
            ticks_per_step := 2
            beat_grid := 12
        case 7 { 1/64 T }
            ticks_per_step := 1
            beat_grid := 24
    end select
    set_knob_label(SyncRate,!tempo_names[SyncRate])
end function

on ui_control (SyncRate)
    call change_rate()
end on
```


----------



## EvilDragon (Aug 12, 2018)

Yes, you need that function there.


----------



## Discotron (Aug 12, 2018)

Thanks. One thing that has confused me is the song position being less than 0. Is it possible for the song position to go below 0?
I put a message (song position)on $NI_SIGNAL_TRANSP_START and it displayed a 0. I'm using Ableton Live and when I pressed play it started at the beginning of bar 1 and displayed a zero for song position.


```
if  NI_SONG_POSITION < 0
        sync_position := 0
        step_counter := 0
        tick_counter := -1
        else
            tick_counter := (NI_SONG_POSITION / 40) mod ticks_per_step
            step_counter := (NI_SONG_POSITION / (960 / beat_grid)) mod Steps
    end if
    
end function
```


----------



## EvilDragon (Aug 12, 2018)

Song position cannot be below 0, but I think there's a certain special case in some DAWs that Kontakt covers where NI_SONG_POSITION _could_ be below 0.


----------



## Levitanus (Aug 12, 2018)

Why is song position used? I have several projects, lasts more than half of hour, I thought, on this distance variable reaches int limit.
I use distance_bar_start, but it hurts sometimes


----------



## Levitanus (Aug 12, 2018)

EvilDragon said:


> certain special case


cubase pre-bar, for example.


----------



## EvilDragon (Aug 12, 2018)

Song position is used to, for example, start a pattern on a particular step depending on where within a bar you start playback in the DAW. The RMII arp script easily showcases that.



Levitanus said:


> cubase pre-bar, for example.



No, I think that's just a visual thing for the ruler and transport display, internally it would still count from 0 as song position pointer, I believe. It's like that in Reaper, for example.


----------



## Levitanus (Aug 12, 2018)

EvilDragon said:


> where within a bar


sorry, my English is not too good. What exactly do You mean? Where inside the bar transport has started, or which the bar is it?
Still, is there the problem with int limit?

P.S. As I see in the code, it calculates position inside the bar? What the problem with using DISTANCE_BAR_START? (exclude the cross-bar "loops", it can be solved)


EvilDragon said:


> It's like that in Reaper,


Reaper is clever)
My opinion is based on received multitrack and click-track mismatch. Maybe it was bad export, of course)


----------



## EvilDragon (Aug 12, 2018)

Try the arp script that I posted in a DAW. Make a pattern in it with different velocity values in the table, and let's say the rate stays at 1/16 and there are 16 steps. Start playing in the DAW from the beginning of the bar - the arp will also start from step 1. Start playing in the DAW from halfways in the bar, the arp will start from step 8.



Levitanus said:


> My opinion is based on received multitrack and click-track mismatch.



That has nothing to do with how Kontakt reports song position pointer, though!



Levitanus said:


> Still, is there the problem with int limit?



Not really, 2^31 ticks is a LOT of ticks (at 960 per quarter note). That's about 2237000 quarter notes, or if we stick to 4/4 time signature, 559250 bars of music!


Oh and BTW. $DISTANCE_BAR_START depends on note on events. $NI_SONG_POSITION doesn't, so it's better for usage in listener callback.


----------



## Discotron (Aug 12, 2018)

Weirdly, if I reset to the beginning of bar 1 in Live and start the Arp, the Arp starts on beat 2 and not beat 1.


----------



## GeneraStudios (Aug 12, 2018)

:O Does NI leave a lot of their scripts open to study?


----------



## Levitanus (Aug 12, 2018)

GeneraStudios said:


> open to study?


all of them
this was SublimeKSP source. So, it seems, Mario has written it)


----------



## EvilDragon (Aug 13, 2018)

No it's actually a script from Retro Machines II (which is open for studying, yes), I just rewrote it with simpler SublimeKSP syntax. 



Discotron said:


> Weirdly, if I reset to the beginning of bar 1 in Live and start the Arp, the Arp starts on beat 2 and not beat 1.



Check the value of NI_SONG_POSITION at that point?


----------

