Edit: I'm not actually looking for a solution, because short of syncing the hardware clocks (which I can't do) there isn't one. What I'm looking for is an explanation of where the magic -8.4ms I seem to have identified is coming from.
I have a hardware music sequencer (Squarp Pyramid), a laptop, a digital MIDI-triggered drum synthesizer (Roland TR-8), and an audio mixing console with built-in USB audio (Soundcraft Signature 12 MTK). Using my laptop with DAW software (Reaper), I record a sequence of drum beats from the TR-8, triggered over hardware MIDI from the Pyramid:
What I find is that the beat-markers in Reaper drift over time compared to the onsets of beats in the recorded audio - between 10ms and 20ms per minute of audio, depending on the BPM. This is expected, of course, but I'd like to reduce it as much as possible.
I don't have access to a reliable way of keeping the clocks in sync. The solution I'm working with right now is to use a slightly modified BPM on Reaper to compensate for the drift. For example, for 125BPM on the Pyramid, I set reaper to 125.02BPM. For 117BPM on the Pyramid, I set Reaper to 117.0382BPM. This reduces the drift to fractions of ms per minute of recorded audio, which I can live with - but I have to work out the drift by hand, and it's different for each BPM.
My method for determining the drift amount is as follows:
- Record 10 minutes of audio with beat pulses at a particular BPM.
- Align the onset of the 0th recorded audio beat with t=0 in Reaper.
- Find the nearest Reaper beat marker to t=10mins, and measure the distance between it and the onset of the corresponding audio beat. This distance represents the clock drift over 10 minutes.
For 125BPM, the drift over 10 minutes is -95ms, giving me -9.5ms of drift per minute. \$\frac{60000}{59990.5}*125 = 125.02\$, so I set Reaper to 125.02BPM and the drift over 10 minutes becomes small enough to not be a problem any more. Unfortunately this has to be re-calculated for every BPM I set on the Pyramid. I figured out I can streamline this process by writing a C++ app that uses the RtMidi library to sample incoming MIDI clock pulses from the Pyramid’s USB output, calculating a rolling offset error to estimate drift from an expected BPM.
For 125BPM, the USB samples exhibit a clock drift of -1.1ms per minute, and I have to add a magic number of -8.4ms of drift per minute to match what I'm seeing when I line things up by hand in Reaper. I've tried this across multiple BPMs and the -8.4ms seems consistent (+/- 0.2ms). This method gives me reliable predictions for a BPM to use in Reaper to get the beat marks to line up with the audio, but the -8.4ms magic number is troubling.
Where is this magic -8.4ms coming from? I figure there are three clocks involved:
- Pyramid (P)
- My laptop (L)
- The USB audio module in the mixing console (M)
I suppose that the tweaked BPM in Reaper needs to be based on driftPM (clock drift between Pyramid and mixer's USB audio). My USB capture method is giving me driftPL. The magic -8.4ms must then be driftLM. So driftPM = driftPL + driftLM.
Does this seem correct? It appears to work in practise but I feel like I’m stabbing in the dark. Is there an easier approach?