0

The x0xb0x MIDI sync timing issue
Moderators: altitude, adafruit_support_bill, adafruit, phono, hamburgers

Please be positive and constructive with your questions and comments.

The x0xb0x MIDI sync timing issue

by burns_audio on Tue Aug 06, 2019 3:48 pm

Hi,

After looking at the x0xb0x source code I believe the "timing fix" in most firmwares is incorrect. I didn't check MarOS but I understand a lot of the sync code was rewritten there, so perhaps it's fixed there.

Solutions that "bump" the MIDI clock count on receipt of MIDI start are just masking the issue, and when you do that, you will find on some devices the x0xb0x ends up "ahead".

The actual issue is that the MIDI clock message is handled directly in the MIDI serial interrupt, and MIDI start is not. MIDI start is put onto a queue which is read off by the general processing thread at a later time.

The MIDI interrupt has the following code:
Code: Select all | TOGGLE FULL SIZE
SIGNAL(SIG_USART0_RECV) {
  char c = UDR0;

  if (c == MIDI_CLOCK) {

    // raise dinsync clk immediately, and also sched. to drop clock
    // (MIDISYNC -> DINSYNC conversion);
    if (sync != DIN_SYNC) {
      sbi(DINSYNC_PORT, DINSYNC_CLK); // rising edge on note start
      dinsync_clock_timeout = 5;      // in 5ms drop the edge, is this enough?
    }

    if (!playing)
      return;
    midisync_clocked++;
    return;
  }

  // putstring("0x"); putnum_uh(c); putstring("   ");
  midi_q[tail_idx++] = c; // place at end of q
  tail_idx %= MIDI_Q_SIZE;

  if (tail_idx == head_idx) {
    // i.e. there are too many msgs in the q
    // drop the oldest msg?
    head_idx++;
    head_idx %= MIDI_Q_SIZE;
  }
}


So, in a perfect world, this is the order of events:
(clock is stopped, playing=false)
MIDI start received
MIDI start message is added to the midi_q queue
the general thread runs, and works off the midi_q queue, sets playing to true.
MIDI clock received
playing = true
MIDI clock gets counted

So what happens if we receive a MIDI start message immediately followed by a MIDI clock?

(clock is stopped, playing is false)
MIDI start received
MIDI start message is added to the midi_q queue
MIDI clock received
playing is false
MIDI clock dropped
general thread runs, works midi_q off, sets playing to true.

So in this case the first MIDI clock was dropped.

So, what to do about it? The solution is to process MIDI start and clock in the same thread. I'm pretty sure the interrupt can handle processing MIDI start and MIDI clock since on the general case it'll just be an extra "if" statement, but I'm not yet sure exactly how much code needs to be moved into there.

A simpler solution may be to move MIDI clock OUT of the interrupt but that'll introduce some amount of jitter and lag to the response of the x0xb0x and generated DIN sync.

A quick fix is to just always increment the midi clock variable, and only reset it to 0 when receiving a MIDI stop. That way MIDI start does not reset the clock counter to 0. However I know some devices (like elektron) send MIDI clock even when stopped, so then restarting the sequencer would not reset the MIDI counter to 0.

So, in summary, the fix needs to process MIDI clock serially (not enqueue some messages and act on others in the interrupt).

Cheers,
Tom

burns_audio
 
Posts: 2
Joined: Tue Aug 06, 2019 3:33 pm

Re: The x0xb0x MIDI sync timing issue

by burns_audio on Wed Aug 07, 2019 8:46 am

Here's what I *think* is Sokkos 1.9.1, with the MIDI timing fixed properly. I'll have to check, it's hard to figure out exactly what source matches what version (or even which version is ideal!)
Attachments
x0xb0x.hex
(40.2 KiB) Downloaded 14 times

burns_audio
 
Posts: 2
Joined: Tue Aug 06, 2019 3:33 pm

Re: The x0xb0x MIDI sync timing issue

by r.odent on Sun Sep 29, 2019 7:20 am

Very good find! I tried your firmware, and while it does seem to play a pattern in perfect sync, the light and buttons seem to have stopped working.

Another approach to getting these events in the same order, while still dealing with them quicker than the regular MIDI queue is to create a new real-time message queue. The real time message queue is then exhausted in the non-interrupt code entirely before dealing with the regular MIDI queue, so that real-time messages are dealt with as quickly as possible, while not staying for too long in the interrupt handler.
r.odent
 
Posts: 9
Joined: Sun Jul 01, 2012 8:44 pm

Please be positive and constructive with your questions and comments.