You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a sync device that was very tight on version 2.1.0. I'm playing around with the new 2.3.0 update and noticed that my device is off quite a bit. I downgraded to 2.2.1 and found the same issue.
UPDATE: Sorry, i accidentally loaded 2.3.0 instead of 2.2.1 in the test below. 2.2.1 shows the same results as with 2.1.0. The issue is introduced with 2.3.0. I will investigate further.
Here's a screenshot from Ableton. The first track contains my device running v2.1.0 firing 16th triggers to an external synth. The second track is v2.2.1 (see UPDATE above) and the other tracks are all v2.3.0 with different setPhaseLockQuartersCount and setExtIntervalBuffer settings. All these test are done with the same clock and hardware. I'm syncing to an external clock running at 24PPQN via hardware interrupt.
These test were done with the straight clock output, e.g. SetOnPPQN and setOnOutputPPQN.
Coming from 2.1.0, I'm currently not using ATOMIC. I expect that to impact performance, but my issue seems to be a consistent offset with version 2.2.1 and 2.3.0. I expect any performance issues regarding not using ATOMIC will be random jitter and performance issues?
This is certain related to this PR:
Code review, shuffle multitrack support and refactor on external sync schema by @midilab in #60
The off phase could be triger by the phase counting variable maybe, wich is used to a better lock phase on modular lower pulse sync like 2ppqn or 4ppqn, the setPhaseLockQuartersCount is used to adjust this behaviour depeding on the resolution of external clock (lower or higher)
At main clock function:
...
if (clock_mode == EXTERNAL_CLOCK) {
// Tick Phase-lockif (labs(int_clock_tick - ext_clock_tick) > 1) {
// only update tick at a full quarter or phase_lock_quarters * a quarter// how many quarters to count until we phase-lock?if ((ext_clock_tick * mod_clock_ref) % (output_ppqn*phase_lock_quarters) == 0) {
...
This also now solves long-standing problem with BPM being a little too slow. Should now be perfectly accurate!
Problem was needing to cast interval from uint32_t to int32_t so that the correct negative interval is passed to add_repeating_timer_us (because "the repeat delay in microseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks.")
@doctea can you tell me anything related on v2.3.0 vs olders based on the tests you have been running?
I will try to do some test to try and understand it :). If I remember correctly then my own experience with moving to 2.3 was that it made external clock less reliable - I think the big change was the introduction of the PLL, whereas previously it was more directly synced to the external clock messages? But I'll have to check.
I think there were API changes too which makes it not so simple to do direct A/B comparison with the same test sketch code. So perhaps it would be useful to be able to try different sync approaches while using the same test sketches.
I wonder too if we need a testing harness so that we can run repeatable tests with different kinds of simulated clock and jitter, so that we can prove improvements and regressions..
@doctea
Can you test it too? The setPhaseLockQuartersCount() does nothing in this branch, im almost sure that this new "feature" was the one in charge for the unstability.
I used MIDI-OX as the clock source on my PC. (I have previously discovered that my FLStudio would not give me anything like sensible and sane clock output, giving a mad flurry of clock messages and maybe even missing some or sending extra entirely, which caused me much consternation, so I am eschewing that when testing now!).
Using my Compulidian project (RP2040-based, USB MIDI connection, midihelpers as my uClock 'wrapper').
I found that:-
The revert-ext-clock-alg branch did not work properly for me -- pressing play gives a brief initial burst and then nothing (silence/no clock) until a second or two later when clock seems to continue.
The main branch did not have this problem -- seemed to work correctly for me!
The feature-strict-external-develop branch (using setStrictExternalMode(true)) -- also seemed to work correctly. I also found this was better at tracking tempo changes (e.g., starting at 120bpm and then changing to 60bpm or 90bpm, or going from 90bpm to 120bpm) -- the clock synced with the beat at the new speed very quickly, whereas with the main version it took a couple of seconds to audibly slow down before syncing to the new beat.
(The only thing I'm not quite sure about is whether my feature-strict-external-develop version always started on the downbeat correctly after a stop->reset->start -- it felt like perhaps the first step did not fire or had a little stutter, but without more testing I can't be sure exactly, and I think its more likely to be a bug I introduced in my project code a couple of weeks ago).
Activity
[-]Unstable device with 2.2.1 and 2.3.0 update[/-][+]Unstable device 2.3.0 update[/+]midilab commentedon Apr 23, 2026
Hi @vanzuiden ,
Thanks for reporting back.
This is certain related to this PR:
Code review, shuffle multitrack support and refactor on external sync schema by @midilab in #60
The off phase could be triger by the phase counting variable maybe, wich is used to a better lock phase on modular lower pulse sync like 2ppqn or 4ppqn, the setPhaseLockQuartersCount is used to adjust this behaviour depeding on the resolution of external clock (lower or higher)
At main clock function:
... if (clock_mode == EXTERNAL_CLOCK) { // Tick Phase-lock if (labs(int_clock_tick - ext_clock_tick) > 1) { // only update tick at a full quarter or phase_lock_quarters * a quarter // how many quarters to count until we phase-lock? if ((ext_clock_tick * mod_clock_ref) % (output_ppqn*phase_lock_quarters) == 0) { ...or/and at external sync call function:
... case STARTING: clock_state = SYNCING; start_sync_counter = 4; break; case SYNCING: if (--start_sync_counter == 0) clock_state = STARTED; ...looks like there are two 2.3.0 that syncs correctly on your screenshot? what values did you use on each test?
vanzuiden commentedon Apr 23, 2026
Hi @midilab, thank you for the reply. None of the 2.3.0 tests line up, you can compare all the hits to the first track in the screenshot.
Unfortunately, I didn't write down the values. I can retest this for you with the values mentioned.
midilab commentedon Apr 23, 2026
Since none of those tests works, no need to re-test.
I will try to make some changes that could possible solve and sent back to you via the develop branch if thats ok for you to test it.
I ping you here when changes take place.
midilab commentedon Apr 23, 2026
@doctea can you tell me anything related on v2.3.0 vs olders based on the tests you have been running?
vanzuiden commentedon Apr 23, 2026
@midilab Sure thing, i'll keep my out on this thread!
vanzuiden commentedon Apr 23, 2026
This also now solves long-standing problem with BPM being a little too slow. Should now be perfectly accurate!
Problem was needing to cast interval from uint32_t to int32_t so that the correct negative interval is passed to
add_repeating_timer_us(because "the repeat delay in microseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks.")Originally posted by @doctea in #64 (comment)
This might be related as i'm running a RP2040
doctea commentedon Apr 23, 2026
I will try to do some test to try and understand it :). If I remember correctly then my own experience with moving to 2.3 was that it made external clock less reliable - I think the big change was the introduction of the PLL, whereas previously it was more directly synced to the external clock messages? But I'll have to check.
I think there were API changes too which makes it not so simple to do direct A/B comparison with the same test sketch code. So perhaps it would be useful to be able to try different sync approaches while using the same test sketches.
I wonder too if we need a testing harness so that we can run repeatable tests with different kinds of simulated clock and jitter, so that we can prove improvements and regressions..
midilab commentedon Apr 23, 2026
@doctea
Im beting that the sync phase counter is the issue, but i will have to investigate it, probrably releasing on some branch a version without to test.
midilab commentedon May 10, 2026
hi @vanzuiden
I've create a new branch and revert to the old external sync algorithm so we can test to see if the unstability is gone.
https://github.com/midilab/uClock/tree/test/revert-ext-clock-alg
Please let me know!
@doctea
Can you test it too? The setPhaseLockQuartersCount() does nothing in this branch, im almost sure that this new "feature" was the one in charge for the unstability.
vanzuiden commentedon May 10, 2026
@midilab Great, I will give it a go when i'm home next week. I'll report back, thank you!
doctea commentedon May 14, 2026
@midilab I just did some quick testing with your test/revert-ext-clock-alg branch, the current
mainbranch, and my feature-strict-external-develop.I used MIDI-OX as the clock source on my PC. (I have previously discovered that my FLStudio would not give me anything like sensible and sane clock output, giving a mad flurry of clock messages and maybe even missing some or sending extra entirely, which caused me much consternation, so I am eschewing that when testing now!).
Using my Compulidian project (RP2040-based, USB MIDI connection, midihelpers as my uClock 'wrapper').
I found that:-
revert-ext-clock-algbranch did not work properly for me -- pressing play gives a brief initial burst and then nothing (silence/no clock) until a second or two later when clock seems to continue.mainbranch did not have this problem -- seemed to work correctly for me!feature-strict-external-developbranch (usingsetStrictExternalMode(true)) -- also seemed to work correctly. I also found this was better at tracking tempo changes (e.g., starting at 120bpm and then changing to 60bpm or 90bpm, or going from 90bpm to 120bpm) -- the clock synced with the beat at the new speed very quickly, whereas with themainversion it took a couple of seconds to audibly slow down before syncing to the new beat.(The only thing I'm not quite sure about is whether my
feature-strict-external-developversion always started on the downbeat correctly after a stop->reset->start -- it felt like perhaps the first step did not fire or had a little stutter, but without more testing I can't be sure exactly, and I think its more likely to be a bug I introduced in my project code a couple of weeks ago).Hope this helps!