Off-by-one error in Indexed Triggers

Tags: #<Tag:0x00007f9794a48b38>

In Cantabile 4.0 Build 4048 (latest), bindings from a route “Enabled” source to an “Indexed Trigger” invoke the wrong trigger number (one lower than specified in the binding). Here are steps to reproduce:

  1. Create a new song.

  2. Create a route from any input to any output, and give the route a name (by selecting it and pressing F2).

  3. Create two “On Specific Trigger” bindings with numbers #100 and #200, where trigger #100 hides the on-screen keyboard (View -> Onscreen Keyboard, Value=Off), and trigger #200 shows the on-screen keyboard (View -> Onscreen Keyboard, Value=On).

  4. Create two PC Keyboard bindings so that pressing “Y” invokes Trigger #200 and pressing “N” invokes Trigger #100. Verify that pressing “Y” now shows the onscreen keyboard and pressing “N” hides it again.

  5. Create a binding from the route you created in step 2 to an “Indexed Trigger Event” with 100 as the “Off Value” and 200 as the “On Value”.

  6. Disable and enable the route you created in step 2. Note that the onscreen keyboard does not show or hide as it should, because triggers #100 and #200 are not being invoked. (Cantabile is actually triggering #99 and #199.)

  7. Change the binding you created in step 5 to use 101 as the “Off Value” and 201 as the “On Value”. Observe that disabling and enabling the route you created in step 2 now shows and hides the onscreen keyboard. This is because Cantabile is invoking triggers #100 and #200.

Hi Hamlen,

I did the procedure you listed above here on Cantabile 4 V 4048 on a new song and it works fine. I’m not sure why your setup is producing the offset results. You could run some diagnostics to help determine what’s going on. In that case you would need to set some advanced diagnostics options temporarily to help track it. The log file would then show exactly what happened. If you already did this the log file would be of value to Brad for troubleshooting your issue. I hope you can find what caused it.


Hi Dave,

I’m using the 64-bit version of Cantabile 4.0 Build 4048 on Windows 11, in case it makes any difference. The behavior is completely consistent on my machine, and I’m starting with a completely blank song, so it’s surprising that the behavior differs between our two installations.

I created the log file you suggested and it’s pretty clear. Here are the relevant lines:

00303241        0   [05296:2]: Binding My Route - Enabled (in ) => Triggers - Indexed Trigger Event - invoking target with value 199
00312589     9348   [05296:2]: Starting route operation: disabling route
00312644       55   [05296:2]: Finished committing re-route: +10=63, 52ms
00312646        2   [05296:2]: Binding My Route - Enabled (in ) => Triggers - Indexed Trigger Event - source triggered with value 0 for time (now)
00312646        0   [05296:2]:   - invoking immediately
00312646        0   [05296:2]: Binding My Route - Enabled (in ) => Triggers - Indexed Trigger Event - invoking target with value 99
00314363     1717   [05296:2]: Starting route operation: enabling route
00314419       56   [05296:2]: Finished committing re-route: +10=73, 54ms
00314422        3   [05296:2]: Binding My Route - Enabled (in ) => Triggers - Indexed Trigger Event - source triggered with value 1 for time (now)
00314423        1   [05296:2]:   - invoking immediately
00314423        0   [05296:2]: Binding My Route - Enabled (in ) => Triggers - Indexed Trigger Event - invoking target with value 199

As you can see, it’s invoking triggers #99 and #199 instead of #100 and #200. I have the full log file in case @Brad wants it.

Hi Hamlen,

Yes, I think Brad should look at it since we know it behaves differently in our 2 machines. You can e-mail him or PM him and attach the relevant files.


In settings it is possible to choose whether Program Change starts at zero or one, I don’t know if this can affect triggers. Just a thought.

That was my first thought too but I couldn’t imagine that it could be so simple. You can choose “one based” or “zero based” in options.

TorstenH nailed it. Changing the option to zero-based Program Change numbers fixes it on my machine.
I think this is still a bug, right? Seems the code for processing Trigger indexes must sometimes (but not always) call the code for processing Program Change numbers, conflating the two.

1 Like

Hi @Hamlen

Yes, this is a side effect of the zero vs one based program numbers. Trigger numbers are always zero based and shouldn’t be using the one-based setting… but they are. Oops.

Mostly it’s a display issue when editing the binding value. If you set the binding to 101 and 201 (when using one based program numbers) the correct value will be stored in the binding and that’ll work around the issue for now.

I’ll look into making a fix that’ll display it correctly in an upcoming build.


Thank you, @Brad!

Yes, it’s easy to work around now that I understand what’s happening. Appreciate the fix! :slight_smile:

By the way, this is one of 3 bugs I encountered this past weekend while upgrading to 4.0, and that I reported on the forum. This one can also be worked around (seems to be an unintended side-effect of “Reset All”), but this one is tougher to avoid (error in SysEx evaluation).


Thanks Kevin… they’re on my list to investigate before the next build.