The symptom nobody trusts
You set a filter sweep to one bar. You loop four bars and it breathes correctly. You stop, you move the cursor back, you play again, and the sweep is now opening where it used to close. Nothing about the patch changed. Nobody touched the rate.
This is one of those bugs that people report as a feeling rather than a fact, because the modulation is obviously still in time. It is going round once per bar, exactly as instructed. So you assume you imagined it, or that you nudged something, and you move on. beatsbasteln sent me this exact observation about Laura in August, and the tell in his message was that he only noticed it when he restarted playback. That is the fingerprint of the whole class of bug.
The mistake is treating tempo sync as a question about speed. It is two questions. How fast should this go, and where in its cycle should it be right now. Almost every implementation answers the first and quietly guesses at the second.
Rate is not position
An LFO is a phasor and a shape. The phasor is a number that walks from 0 to 1 over and over, and the shape turns that number into a value you can modulate with. Making it tempo synced looks like a two line job. Ask the host for the tempo, work out how many seconds one bar takes, turn that into a frequency, and add a little bit of that frequency to the phasor on every audio block.
That is correct arithmetic and it produces the right speed forever. What it does not produce is the right place. The phasor only agrees with the bar line if it happened to be at zero when the bar started, and it only stays agreed if nothing ever interrupts the count. In a session that is playing straight through from bar one, both of those are true, which is why this survives so long: the first take is right, and every take after a restart is wrong by a different amount.
Everything a producer does to a transport breaks the assumption. Stopping and starting again leaves the phasor wherever it was when you hit stop. Looping a bar means the playhead jumps backwards while the phasor keeps counting forwards. Clicking into bar 33 to work on the drop means the phasor arrives having counted nothing at all. Bouncing from bar 9 gives you a render that does not match what you heard when you played from bar 1. The modulation is in time with itself and out of time with your arrangement.
The fix is to stop counting. The host already knows where you are, to the sample, and it will tell you as a position in quarter notes since the start of the song. If one cycle of the LFO is worth some number of quarter notes, then the phase is just the fractional part of the position divided by that number. Read it fresh every block and there is nothing left to drift. A loop wrap moves the position backwards, so the modulation moves backwards with it. Scrubbing into bar 33 puts the modulation exactly where bar 33 has it. Bouncing gives you what you heard.
I did this for the four LFOs and for the fractal path traversal, and the test that reproduces the report is now boring in the best way: play a bar, stop, rewind, play the same bar again, and the phase comes back identical to six decimal places. It used to come back about a tenth of a cycle out, which on a one bar filter sweep is an audible difference in where the sweep is pointing.
Ask for the length in beats, not the frequency
There is a smaller trap sitting inside the fix, and it caught me first. If your sync code already converts a division into a frequency, it is tempting to lock the phase using that frequency: divide it back out through the tempo and you have your cycle length again.
Do not. Those two numbers are only inverses of each other at one tempo. The moment the tempo changes, and a tempo automation lane changes it constantly, the frequency you derived a moment ago describes a bar length that no longer exists, and the phase you compute from it lands somewhere the grid has never been. Carry the cycle length as a number of beats, all the way through, and let the beat count meet the song position directly. The tempo never enters the calculation at all, which is exactly right: a bar is a bar regardless of how fast it goes by.
Slow modulation needs bigger numbers
He sent one line of unsolicited engineering advice with the report: do not use single precision for phasors that run very slowly, because the amount you add each time can fall below what the number can represent. He was right to warn me, and the arithmetic is worth doing out loud because it decides how slow your instrument is allowed to go.
Take a four bar cycle at 60 beats per minute. That is sixteen seconds. At 48 kHz with a 256 sample buffer, one buffer is 5.3 milliseconds, so each block should advance the phasor by about 0.0000033 of a cycle. A single precision float can only resolve steps of about 0.00000012 near the value 1.0, so that increment is roughly twenty seven times the smallest step available. It works. It also means you are twenty seven doublings of slowness away from the phasor refusing to move at all, and long before it stops entirely the motion turns into visible stairs, because most of the increments get rounded to a value the number can actually hold.
There is a test in Laura now that adds up one cycle worth of those increments twice, once in double precision and once in single, and asserts that the double closes the cycle exactly and the float does not. It is an odd looking test, an assertion that something fails, but it records why the choice was made where anyone changing it will trip over the reason.
The practical upshot for anyone building this: phasors are double precision, always, and it costs nothing. The shape lookup can be single precision, because a position between 0 and 1 has plenty of resolution there. It is the accumulation that needs the headroom, not the reading.
The other half: knowing when not to lock
Locking everything to the bar would be a worse instrument. An LFO has three different jobs and only one of them wants the grid.
A synced LFO in free run is the grid case. It is part of the arrangement, it should be in the same place on every pass, and the bar owns it. A note retriggered LFO is the opposite: the whole point is that it starts when the key goes down, so a per note wobble is per note and not per bar. And a free rate LFO in hertz has no grid to land on, so it keeps counting, which is also what keeps the visual moving when the transport is parked and what makes the plugin behave sensibly in standalone with no host clock at all.
So the rule I settled on is that a synced modulator reads the song position when the host gives one and the transport is rolling, and counts for itself otherwise. Note retrigger loses to the bar while a synced modulator is locked, which sounds like a loss until you remember that the drift it used to introduce was the exact thing being fixed.
One more thing worth checking if you are chasing this in your own code: a lot of implementations do something on the cycle wrap, like latching a new random value for a stepped or smoothed random shape. If you switch from counting to reading a position, a comparison against the previous phase still tells you a wrap happened, and it has the pleasant side effect of firing correctly after a playhead jump, which counting never did.
What Laura gives you to point at things
Four LFOs, each with six shapes: sine, triangle, saw, square, stepped random, and a smooth random drift that wanders between targets instead of jumping to them. Rate is either free from 0.01 to 30 Hz or a division from a thirty second note out to four bars. Each one has a phase offset, a fade in up to five seconds, a one shot mode that stops after a single pass, and a switch between retriggering per note and running free.
Any of the four can modulate another one's rate or depth, which is where the more interesting motion comes from: a slow LFO widening a fast one is a different sound from either of them alone. And any of the four can stop being an LFO altogether and become a multi segment envelope you draw by hand, if a shape is what you want rather than a cycle.
The fractal path traversal has its own sync, and in this release it gained two bar and four bar orbits, because one bar turned out to be faster than the set's structure changes when you are moving through it. If you want the longer version of what that path is doing, there is a piece on playing the edge of the Mandelbrot set, and the release it landed in is written up in the 1.6.1 notes.
Keep reading
- How to program a tape stop instead of drawing one · Every glitch fill you have printed was drawn by hand, once, for one drop, at one tempo. The alternative is to write time as a pattern and let the fill happen on the bar, every bar, without you being there.
- How to build a riser that never ends · Every riser you have printed ends somewhere. It runs out of top, or it loops and you hear the seam. A longer sweep will not fix it. What does is an illusion that has been sitting in the psychoacoustics literature since 1964.
- Playing the edge of the Mandelbrot set · Most of a fractal is quiet. The sound lives at the edge, and until now Laura mostly sampled past it. The new update pins the oscillator to the boundary, then adds the kind of modulation that knows how to walk along it.
Hear it for yourself. The demo is free and unrestricted for 30 minutes per session.