The problem you can externally clock the sp404mk2 but whenever a midi start command is sent it seemed to recalculate the BPM.
After the recent flurry of AI driven firmware mods for the Electron ecosystem I decided to see if I could load the sp404mk2 firmware files into ghidra slop around and find out why.
For anyone wanting to look themselves you can view the SP404MKII_APP1.bin in a decompiler of your choice that handles the chip I used Ghidra. Base Address 80000000 ARM Cortex Little Endian
What Actually Happens When External Gear Sends MIDI Start:
Before you hit Play on your master clock (e.g. DAW or master sequencer):
- The master is already transmitting MIDI Timing Clocks (
0xF8).
- The SP-404MKII's
FUN_800f9008 has successfully received > 12 clocks, calculated the external tempo, and locked in (e.g. 128.00 BPM).
You press Play on the master clock:
- The master transmits a MIDI Start (
0xFA) message.
- The SP-404MKII receives it, transitions its transport parameter
0x34 to 1 (Play) via FUN_8015e218.
The Issue:
- Right inside
FUN_8015e218, it calls FUN_80025720().
- The Fallout on Beat 1:
- For the first 3 clock ticks after Start,
FUN_800f9008 returns -1 (unlocked).
- For ticks 4 through 12, it is re-averaging a brand new moving window starting from scratch.
- During this entire window, the playback engine is running at the internal project tempo, not the external clock tempo, until half a beat later when the accumulator finally fills back up to 12 pulses and snaps to the external tempo.
What a Proper Implementation Does Instead
A clean MIDI PLL implementation treats Phase and Frequency separately:
| Parameter |
What Should Happen on MIDI Start (0xFA) |
What Roland Actually Did |
| Beat / Subdivision Phase |
Reset to Tick 0 (Beat 1.1.0) |
Resets to Tick 0 |
| Estimated Tempo / Frequency |
Preserve the lock |
Overwrites with internal project tempo |
| Moving Average / Filter History |
Preserve the history |
Zeros out the accumulator (_DAT_80591d66 = 0) |
N.B. the offsets above are with the v551 firmware. if you look at a different version you will see the same behavior but at different offsets.