[Ardour-Dev] how to precisely align the audio output of a Jack MIDI client in Ardour?

Robin Gareus robin at gareus.org
Fri Nov 10 07:36:07 PST 2017

On 11/10/2017 06:26 AM, Markus Grabner wrote:
> Am Freitag, 10. November 2017, 05:56:00 CET schrieb Robin Gareus:
>> Hi Markus,
>> It looks like you're sending and receiving data in the same jack client.
>>   your client -> Ardour -> your client.
> No, it's actually
> Ardour (MIDI track) -> my client -> Ardour (audio track),

Same thing.
I'm sorry I should not have used arrows. They're easily confused with
port connections. Jack runs clients in a given order. Perhaps this is
better represented vertically and without arrows:

cycle 1:
  1.1: midipulse
  1.2: ardour

cycle 2:
  2.1 midipulse
  2.2: ardour

cycle 3:
  3.1: midipulse
  3.2: ardour


Let assume there is a midi-note at time 0 and Ardour starts rolling at
time 0 in cycle 1, with a jack period of N samples.

cycle 1: midipulse runs first. it's midi-input is empty and hence
there's no output. Then Ardour runs and records N samples of silence and
plays a MIDI note at time t = 0.

cycle 2: midipulse runs. sees the MIDI-event (in this cycle at sample 0)
and produces audio: a spike at 0 in this cycle.  Ardour runs and records
the produced audio. The spike ends up an N  on the global time-line.

The same would be true if the graph was reversed:

cycle 1:
  1.1: ardour
  1.2: midipulse

cycle 2:
  2.1: ardour
  2.2: midipulse

C 1.1 Ardour records N samples of silence, sends a MIDI-event
C 1.2 midipulse processes the midi-event and produces audio.
C 2.1 Ardour records the generated audio. 1 cycle late.

As for Ardour's alignment modes.

"Align With Capture Time" is only to be used for internal Ardour
bounces.  Ardour 5 aligns the Clock with the Input. The clock represents
the time at which something is recorded; hence "capture time".  If you
record an Ardour track onto another Ardour Track, they run in the same
cycle. External port-latencies are not relevant.

"Align With Existing Material" assumes Ardour plays back, the signal
goes outside Ardour, someone plays along, Ardour records that and aligns
it with the material that was played.

Ardour playing takes at least one cycle plus the worst-case latency of
all port-connection to the outside: "Worst playback latency".
Recording in the next cycle is late by the capture latency of the port
that you record.  So Ardour 5 moves the recorded signal back (towards 0)
by "worst playback latency" + "port capture latency".

I guess when you did your test, you still had other tracks or busses,
probably ardour's master-bus connected to physical output and
"worst-playback latency" was not zero.

Your simple midipulse jack client ignores any port-latency. Check with
`jack_lsp -l`.  In case there are hardware connections, midi_pulse:out
does have a non-zero playback latency. "How long will it be until the
signal reaches the output";  that's  the (m-1)*n+o in your original
post. The simple client ignores this and produces audio assuming it's
zero. But even if that simple client would properly takes the
port-latencies into account, the result would still be one cycle off.

I am not aware of a jack-mechanism to that sets port-latencies to
include the 1 cycle latency of the split graph.

Also jack does not automatically compensate for latency (it just passes
numbers around to allow clients to do so).

in case of
  no-input -> Hydrogen (MIDI in H2) -> LinuxSampler -> Ardour -> output

it can work if you use jack-transport. The corresponding graph above can
be executed in a single cycle, and all clients (H2, Ardour) share a
common transport.

but the circular graph
   X -> Hydrogen -> LinuxSampler -> Ardour (MIDI from A) -> X
will again be broken in two and a 1 cycle latency will occur.

c'est la vie.  It's arguably a bug or design mistake that jack does not
set port-latencies when it splits the graph to include the split [1] or
vice-versa that it doesn't delay signal "in the wire" for internal
connections [2].

[1] set the capture latency of the midipulse audio-port in the first
example to include the split or [2] delay the midi signal by 1 cycle in
the second example above. Either would yield the same result.


More information about the Ardour-Dev mailing list