Skip to content

How to Add Round Robins

Set up round-robin sample cycling so that repeated notes play different samples, avoiding the machine-gun effect.

Prerequisites

  • A working IVLS product with at least one flow and Stl.PlayEvent
  • Stl.RoundRobins already included via Ivls.STL (it is part of the standard library bundle)
  • Your sample groups organized with multiple round-robin layers per note
  • Familiarity with round robin concepts

Steps

1. Confirm Stl.RoundRobins is in IVLS_NODES

Stl.RoundRobins ships inside Ivls.STL. If your product includes that bundle, the node is already available. Otherwise, add it explicitly:

define IVLS_NODES := ..., Stl.RoundRobins, ...

2. Register Stl.RoundRobins in your flow

Place Stl.RoundRobins in the flow before Stl.PlayEvent. The node runs on cb NotePass, so it sets Voice[self].rr before the event is triggered.

node MyProduct.Flows:
    cb Flows:
        define FLOWS += my.sound_flow

        ivls.register_node(my.sound_flow, Stl.RoundRobins)
        ivls.register_node(my.sound_flow, Stl.PlayEvent)
end node

3. Register RR dimension fields

In your product's cb FirstLoad, tell the round-robin system which voice fields form the RR index. Each field is a dimension -- the system combines them into a composite index to track independent RR counters per combination.

The most common case is note-only RR (each MIDI note has its own counter):

node MyProduct.Setup:
    cb FirstLoad:
        ivls.RoundRobins.register_field(Voice.field.note, 128)
end node

For multi-articulation products, register multiple fields to get independent RR cycling per articulation per note:

cb FirstLoad:
    ivls.RoundRobins.register_field(Voice.field.my_artic, NUM_ARTICS)
    ivls.RoundRobins.register_field(Voice.field.note, 128)

4. Implement the get_max_rr hook

Override ivls.RoundRobins.hooks.get_max_rr() to return the number of available round robins for the given voice. The system uses this to index into its pre-shuffled seed table.

node MyProduct.Product:
    cb Functions:
        function ivls.RoundRobins.hooks.get_max_rr(vo) -> rr_max override
            { Return the number of RR samples for this voice's group }
            rr_max := my.num_rr_for_group(Voice[vo].my_group)
        end function
end node

If all your groups have the same RR count, this can be a simple constant:

function ivls.RoundRobins.hooks.get_max_rr(vo) -> rr_max override
    rr_max := 4
end function

5. Use Voice[self].rr in EventGroups

After Stl.RoundRobins runs, Voice[self].rr contains the selected round-robin index (zero-based). Use it in your cb EventGroups to allow the correct sample group:

node MyProduct.PlayEvent from Stl.PlayEvent:
    cb EventGroups:
        declare rr := Voice[event_vo].rr
        declare grp := my.get_group_for_rr(Voice[event_vo].note, rr)

        event.groups[grp] := TRUE
end node

Complete example

Here is a minimal product setup with 4-way round robins indexed by note:

node MyProduct.RRSetup:
    cb FirstLoad:
        ivls.RoundRobins.register_field(Voice.field.note, 128)

    cb Functions:
        function ivls.RoundRobins.hooks.get_max_rr(vo) -> rr_max override
            rr_max := 4
        end function
end node

node MyProduct.Flows:
    cb Flows:
        define FLOWS += my.sound_flow

        ivls.register_node(my.sound_flow, Stl.RoundRobins)
        ivls.register_node(my.sound_flow, MyProduct.PlayEvent)
end node

node MyProduct.PlayEvent from Stl.PlayEvent:
    cb EventGroups:
        { Select the correct group based on note and RR position }
        declare rr := Voice[event_vo].rr
        declare base_group := Voice[event_vo].note * 4
        event.groups[base_group + rr] := TRUE
end node

Verify

  1. Play the same note repeatedly -- each hit should trigger a different sample (check via Kontakt's group monitor or message())
  2. Verify that RR counters reset on transport start (DAW stop/play)
  3. If using multiple dimensions, confirm that different articulations cycle independently

Further reading

  • Guide: Round Robins for the conceptual architecture and seed table details
  • Guide: Voice Fields for understanding Voice.field.* accessors