Ardour external MIDI Clock

Support & discussion regarding DAWs and MIDI sequencers.

Moderators: MattKingUSA, khz

Post Reply
User avatar
khz
Established Member
Posts: 1648
Joined: Thu Apr 17, 2008 6:29 am
Location: German
Has thanked: 42 times
Been thanked: 92 times

Ardour external MIDI Clock

Post by khz »

When I read the article community.ardour - midi sync I remembered my several failed attempts to synchronize Ardour to an external MIDI clock.
On MTC, JACK-transport and such Ardour receives and transmits without problems. Ardour reacts to "start/stop" commands.
But not on external MIDI Clock(, without "start/stop/song position" commands).
I use Jack2 and a2jmidid.

Has anyone made this successful and if so: how?
An understandable HOWTO would make sense.

http://manual.ardour.org/preferences-an ... alog/midi/
http://manual.ardour.org/preferences-an ... transport/
http://manual.ardour.org/preferences-an ... es-dialog/
http://manual.ardour.org/synchronizatio ... nd-slaves/
http://manual.ardour.org/setting-up-you ... g-up-midi/
http://manual.ardour.org/setting-up-you ... -on-linux/

EDIT:
The external MIDI clock (MMC, MTC,...) arrives at Ardour (Midi Tracer).
For "External-MIDI-Clock >> Ardour": MIDI connections is only the MIDI clock connected to ardour. But Ardour internal Clock (tempo) is not responding.
. . . FZ - Does humor belongs in Music?
. . GNU/LINUX@AUDIO ~ /Wiki $ Howto.Info && GNU/Linux Debian installing >> Linux Audio Workstation LAW
  • I don't care about the freedom of speech because I have nothing to say.
j_e_f_f_g
Established Member
Posts: 2032
Joined: Fri Aug 10, 2012 10:48 pm
Been thanked: 358 times

Re: Ardour external MIDI Clock

Post by j_e_f_f_g »

Unlike with MIDI Time Code (MTC -- essentially SMPTE over a midi cable), when you sync something using MIDI clock, then the master clock completely takes over the duties of setting tempo, and starting/stopping play. That's the way this extremely basic, very low overhead sync was designed. (It was made for the very slow 31Khz midi ports, so it's deliberately minimalistic.)

You can't start/stop play from the slave (ie Ardour). Only the master can start play by sending the slave a MIDI Start (or MIDI Continue) message. Only the master can stop play by sending the slave a MIDI Stop message.

You also can't set the tempo from the slave. Only the master can control tempo by sending the slave a continuous stream of MIDI Clock messages at a speed relative to the master's tempo.

So with MIDI Clock (which requires use of Start/Stop messages too), you must set tempo, and start/stop play on your master device. If you want to do that from Ardour's window, then make Ardour be the master. (Let Ardour use its own internal clock, as normal. And tell Ardour to send out MIDI Clock, Start, and Stop messages.)

Then make all other devices slaves. Tell them not to use their internal clocks. Tell them to sync to (ie receive) MIDI Clock.

Oh yeah, Song Position Pointer (SPP) message is also used with midi clock sync. It allows the master to tell a slave to start play from some point other than the very beginning of "a track". But many slaves don't implement it, and it's notorious for introducing "out of sync" conditions anyway (because its resolution is so coarse). So for reliability, you typically need to always start play with all devices cue'ed to the track's beginning.

I won't get into the gory details of SPP (unless someone really needs it). But midi's "my thing". MIDI spec, MIDI File Format, MIDI Sync, Sample Dump Standard, etc -- I'm into it all. So if you've a midi-related question, I'm your guy.

Ironically, I just finished adding MIDI clock support to my BackupBand program today.

Author of BackupBand at https://sourceforge.net/projects/backupband/files/
My fans show their support by mentioning my name in their signature.

tramp
Established Member
Posts: 2347
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 466 times

Re: Ardour external MIDI Clock

Post by tramp »

Hi Jeff

I'm interested in your knowledge.
Since long time I've implemented MIDI clock support in my app, and it works nice so far, when use software MIDI clock masters like Qtractor or Ardour, or anything else.
Just, when external hardware is in use, it seems to ain't work properly. ( received bug reports)
Problem for me is I ain't own a device with MIDI clock.
Maybe you could see a culprit in my source?

here is how I calculate MIDI clock signals to BPM:

Code: Select all

unsigned int MidiClockToBpm::rounded(float f) {
    if (f >= 0x1.0p23) return (unsigned int) f;
    return (unsigned int) (f + 0.49999997f);
}

bool MidiClockToBpm::time_to_bpm(double time, unsigned int* bpm_) {
    ret = false;
    // if time drift to far, reset bpm detection.
    if ((time-time1)> (1.05*time_diff) || (time-time1)*1.05 < (time_diff)) { 
        bpm = 0;
        collect = 0;
        collect_ = 0;
    } else {
        bpm_new = ((1000000000. / (time-time1) / 24) * 60);
        bpm += bpm_new;
        collect++;
        
        if (collect >= (bpm_new*bpm_new*0.0002)+1) {
          bpm = (bpm/collect);
          if (collect_>=2) {
            (*bpm_) = rounded(min(360.,max(24.,bpm))); 
            collect_ = 0;
            ret = true;
          }
          collect_++;
          collect = 1;
        }
    }
    time_diff = time-time1;
    time1 = time;
    return ret;
}
and here is how I handle the jack callback:

Code: Select all

else if ((in_event.buffer[0] ) > 0xf0) {   // midi clock
            if ((in_event.buffer[0] ) == 0xf8) {   // midi beat clock
                clock_gettime(CLOCK_MONOTONIC, &ts1);
                gx_jack::GxJack& jack = *static_cast<gx_jack::GxJack*>(arg);
                static unsigned int sr = jack.get_jack_sr();
                time0 = (ts1.tv_sec*1000000000.0)+(ts1.tv_nsec)+
                        (1000000000.0/(double)(sr/(double)in_event.time));
                if (mp.time_to_bpm(time0, &bpm_)) {
                    set_bpm_val(bpm_);
                    val_chg();
                }
            } else if ((in_event.buffer[0] ) == 0xfa) {   // midi clock start
                set_ctr_val(23, 127);
                val_chg();
            } else if ((in_event.buffer[0] ) == 0xfb) {   // midi clock continue
               //  set_ctr_val(23, 127);
            } else if ((in_event.buffer[0] ) == 0xfc) {   // midi clock stop
                set_ctr_val(23, 0);
                val_chg();
            } else if ((in_event.buffer[0] ) == 0xf2) {   // midi clock position
              // not implemented 
              //  set_ctr_val(24,(in_event.buffer[2]<<7) | in_event.buffer[1]);
            }
        }
On the road again.
j_e_f_f_g
Established Member
Posts: 2032
Joined: Fri Aug 10, 2012 10:48 pm
Been thanked: 358 times

Re: Ardour external MIDI Clock

Post by j_e_f_f_g »

Just a heads-up, tramp. I haven't forgotten you. I think I know where your problem is. The comment below jumped out at me:

Code: Select all

// if time drift too far, reset bpm detection.
MIDI clocks are delivered at a rate tied to the tempo. Plus, a clock is not referenced to an absolute time. It is referenced only to the clock before it. Therefore 2 things are true:

1) If the time between midi clocks starts to increase or decrease, you can't know whether that's due to some latency/drift, or a legitimate change in tempo (ie, the user moving a tempo knob). You can't assume it's the former. Trying to compensate for supposed drift, especially using floating point math with rounding, is almost sure to knock you out of sync. The key to MIDI sync is to follow any fluctuations as accurately and quickly as you can. Don't try to "compensate" for any change in clock rate. Follow it verbatim.

2) Even if there was a way to determine whether a change in rate is legitimate or not (there isn't), without those clocks being referenced to absolute time, you don't have enough info from the master to be able to definitively compensate.

To test my theory that your problem is your "drift compensation", put a printf as so:

Code: Select all

if ((time-time1)> (1.05*time_diff) || (time-time1)*1.05 < (time_diff)) {
  printf("I'm compensating for drift...\r\n");
        bpm = 0;
        collect = 0;
        collect_ = 0;
}
Run your test with ardour. I'll bet you never see that printf. The software connection is probably stable enough that you never execute that code.

Now give that version to your bug report guy, and tell him to look for that message. I'll bet he sees it... right at the moment things start to go wrong.

I need to finish the next version of BackupBand this weekend. But after, I'll give you a very simple command-line app (with lots of commented source) that does midi sync. I test with hardware clocks from 2 Roland products, 1 Yamaha, and 1 Casio.

Now about your situation. I have this old Yamaha rx7 drum box. It's obsolete to me. I'm never going to use it again, and it's in my roomful of crap I'm going to toss (along with a dozen keyboards, some other drumboxes, mixers, speakers, computers -- this room is literally an electronics junkyard). It outputs midi clock/start/stop (from a MIDI Din jack) and has a big tempo knob. It also syncs to midi clock via its midi in. If you live in the USA, email me your mailing address, and I'll send it out.

Author of BackupBand at https://sourceforge.net/projects/backupband/files/
My fans show their support by mentioning my name in their signature.

tramp
Established Member
Posts: 2347
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 466 times

Re: Ardour external MIDI Clock

Post by tramp »

j_e_f_f_g wrote:I need to finish the next version of BackupBand this weekend. But after, I'll give you a very simple command-line app (with lots of commented source) that does midi sync. I test with hardware clocks from 2 Roland products, 1 Yamaha, and 1 Casio.
That will be very welcome.
j_e_f_f_g wrote:To test my theory that your problem is your "drift compensation", put a printf as so:
I'll do, but, if I remember right, this function is called as soon ardour change the BPM.
j_e_f_f_g wrote: If you live in the USA, email me your mailing address, and I'll send it out.
Many thanks jeff, I'm living in Germany, so it will be a bit to far.
On the road again.
Post Reply