Skip to content

Warp and Tempo Sync ​

Drop a drum loop recorded at 90 BPM (beats per minute) into a project running at 120 BPM and it will not line up — it plays at its own speed while your grid moves faster. Warping is the feature that makes an audio clip bend to the project's tempo so everything stays locked together.

One-line definition

Warp = changing an audio clip's speed or duration (by time-stretching or by varispeed) so it follows the project tempo instead of its own original tempo.

The tempo map: the project's grid ​

Before warping makes sense, you need the thing a clip warps to: the project's tempo map.

The tempo map is a list of tempo segments. Each segment says "from this point on the timeline, the tempo is this many BPM". A song can have one constant tempo, or many segments — a slow intro, a faster verse, a ritardando at the end. A segment can also ramp: glide smoothly from one BPM to another instead of jumping.

Alongside the tempo map are time signatures (4/4, 3/4, 6/8…), which set how many beats make a bar. Together the tempo map and the time signatures define the musical grid — the bars-and-beats ruler that every clip position from Clips and Tracks is measured against.

The grid is the thing a clip warps to, so it helps to see how tempo turns musical positions into seconds. Drag the tempo below and watch the bars compress or spread against a fixed seconds ruler — that is exactly the mapping a clip has to follow.

TIMING · TEMPO GRIDIDLE
Tempo and the grid — how BPM maps musical time to seconds

The seconds axis on top never moves; the bar-and-beat grid below is computed from the tempo. Drag BPM and the grid slides — a faster tempo packs more bars into the same six seconds, a slower one spreads them out. The readout turns the tempo into the durations the engine actually counts in: one bar, one beat, and one tick at 480 ticks per quarter note, the resolution libsonare writes into a MIDI file by default (project positions themselves are fractional quarter notes). Change the beats per bar to reshape where the accented downbeats land. Press play for a metronome at the chosen tempo — the grid you see is the click you hear.

Tempo
120 BPM
Beats / bar

The warp modes ​

Every audio clip has a warp mode that decides how (or whether) it follows the grid.

off vs repitch vs tempo-sync vs time-stretch

  • off — no stretching. The clip plays at its native speed and pitch, ignoring the project tempo. Use this for one-shots and sound effects that should not bend.
  • repitch — like changing the speed of a tape. The clip is sped up or slowed down to fit its warp map, and the pitch moves with it: faster means higher, slower means lower. Simple, CPU-cheap, and musically useful when you want that vintage varispeed character.
  • tempo-sync — follow the tempo while keeping the original pitch. The clip is time-stretched so its timing matches the grid, but a phase vocoder — an algorithm that stretches audio in the frequency domain — holds the pitch constant. This is what you want for vocals, melodic loops, and anything where the notes must stay in tune.
  • time-stretch — also keeps the original pitch, but produces the stretch on the audio thread. It reads the same anchor map as repitch and synthesizes the output by overlap-adding source segments at a fixed rate, so changing the map moves the timing and leaves the pitch where it was. Use it when the user is dragging a tempo or an anchor and the result has to be audible immediately.
PARAM SWEEP · TIME STRETCHIDLE
Time stretch — changing length, not pitch

Time stretching changes duration while keeping pitch approximately unchanged. A rate below 1 slows and lengthens the clip; a rate above 1 speeds and shortens it. The waveform shows the duration change. Each render is peak-normalized for headroom, which does not equalize perceived loudness. Listen for changes in timing and transient clarity.

Preview
Switching during playback restarts the selected audio from the beginning.Processed
Rate
1 ×
ModeTiming follows the warp map?Pitch stays the same?Typical use
offNoYesOne-shots, SFX
repitchYesNo (moves with speed)Tape/varispeed feel, drums
tempo-syncYesYesVocals, melodic loops
time-stretchYesYesLive tempo/anchor editing

To hear the table, play one clip through each mode at the same rate. repitch is the only mode whose pitch moves; off keeps its own speed; tempo-sync and time-stretch land on the same length with the pitch held.

PARAM SWEEP · WARPIDLE
Warp modes — following a tempo change

The same melodic clip follows a warp map at the chosen rate; below 1 is slower. repitch plays it like tape: at 0.8× it lasts 1.25 times as long and the measured pitch drops by about four semitones. tempo-sync and time-stretch give the same length with the pitch held: tempo-sync bakes the stretch ahead with a phase vocoder, time-stretch overlap-adds slices on the audio thread, and the two are close by ear. off ignores the map and plays at its own speed inside the clip. The marker is the median measured pitch of each render, and each render is peak-normalized.

Preview
Switching during playback restarts the selected audio from the beginning.Processed
Warp mode
Rate
0.8 ×

time-stretch vs tempo-sync ​

Both preserve pitch, so the choice between them is not about the sound — it is about when the stretching happens and what that costs a host.

tempo-sync bakes the stretched audio on the control thread, ahead of playback. That gives the phase vocoder the whole clip to work with, but every new anchor set means a re-bake — and a tempo-sync clip with no warp map at all is a compile() error rather than a silent fallback. time-stretch does the work block by block on the audio thread, so a new anchor set takes effect from the very next block with nothing to re-bake.

Paying for that immediacy means living inside a preallocated voice budget. The pool holds 8 voices by default, and each handles up to two channels. Set the capacity from the control thread with setWarpVoiceCapacity(voices) and read it with warpVoiceCapacity(); Python uses set_warp_voice_capacity() and warp_voice_capacity(), while C uses sonare_engine_set_warp_voice_capacity() and sonare_engine_warp_voice_capacity(). The accepted range is 0..64; values above 64 are rejected. Capacity 0 disables time-stretch, so those clips use repitch and do not increment warpStretchOverflowCount(). Changing the capacity on a prepared engine rebuilds the voice pool immediately and restarts the WSOLA state (waveform-similarity overlap-add: each frame is placed where its waveform best continues the previous one) of clips using a voice. See Realtime Engine for the assignment and fallback rules.

Watch the fallback, it is silent

The fallback makes no sound of its own — the clip simply starts behaving like repitch. When a clip cannot get a voice because all configured voices are active, warpStretchOverflowCount() counts the fallback blocks. Capacity 0 is an intentional disabled mode and never increments the counter. A source wider than two channels also uses repitch because it cannot fit the stretcher state, but that is not a voice-capacity overflow. Poll the counter from the control thread while developing; see Realtime Engine for the binding names.

What time-stretch does, and what it costs ​

tempo-sync stretches in the frequency domain: the phase vocoder cuts the clip into short overlapping spectra (STFT frames), re-spaces them in time while advancing each frequency bin's phase to match, and adds them back together. time-stretch stays in the time domain. It cuts the source into overlapping frames of 1024 samples, lays them down at a fixed hop of 512 samples (overlap-add: the overlapping ends are summed) so the output always advances by the same amount, and chooses where in the source each frame is cut from according to the warp map. Playing faster means the cut points advance through the source faster than the output does, so frames get skipped; slower means they get repeated. Because each frame is a verbatim slice of the source, the waveform's own period — its pitch — is untouched.

Laying arbitrary slices end to end would produce clicks and cancellation wherever two overlapping frames disagree in phase. That is the WSOLA step: before cutting a frame, the stretcher searches a window of ±512 samples around the mapped position for the offset whose start best continues the frame it just emitted, so consecutive frames overlap in phase and the sum stays clean. The search is a correlation, coarse first and then refined, and it is the whole per-frame cost: a bounded number of multiply-adds on the audio thread, with no transform and no allocation.

That design sets what it costs and what it sounds like.

  • A configurable voice budget. The stretcher's state is preallocated so the audio thread never allocates; the capacity and overflow rules are in the voice budget above.
  • Transients survive; dense textures can double. A slice is the source itself, so a drum hit keeps its attack shape. The exception is a large stretch: slowing down far enough repeats a slice that contains an attack, which sounds as a doubled hit (a flam), and speeding up far enough can skip one. On sustained, polyphonic material — a pad, a choir — a large stretch repeats or skips slices that no single offset can align for every note at once, and the result is a faint doubling or flutter rather than the phase vocoder's smear.
  • A roughly 10 ms grain. Position is honored to within the search radius: each frame lands within about 10 ms of where the map points, which is invisible for tempo following and only matters when lining up sample-exact edits.

Reach for time-stretch when the map is going to change while audio runs — a tempo the user is dragging, an anchor being nudged, a groove being auditioned — because the next block simply reads the new map. On the raw engine it is also the pitch-preserving mode that works with paged clips, where tempo-sync bakes and therefore cannot. Reach for tempo-sync when the map is settled and the material is sustained or harmonic, where the vocoder's whole-clip view gives the cleaner stretch.

Warp anchors: pinning the audio to the grid ​

A tempo change tells the clip how fast to play, but real performances are not perfectly even — a drummer pushes and pulls, a phrase rushes slightly. Warp anchors let you pin specific points inside the audio to specific points on the timeline.

Each anchor ties a position in the source audio to a position on the project timeline. The downbeat of the loop snaps to bar 1, the snare hit snaps to beat 2, and the stretching between anchors is computed automatically. With enough anchors you can straighten a loose performance onto the grid, or deliberately bend a tight one to groove.

Anchors pin, the spans between them stretch
stretchedstretchedstretchedcompressedcompressedsource audio, as recordedbeat gridthe project timeline
  • stretched
  • compressed
  • warp anchor
An anchor fixes one point in the source to one point on the timeline. Whatever audio sits between two anchors is fitted to whatever distance the grid gives it, so a rushed phrase gets stretched and a dragging one gets compressed — inside the same clip, with no edit to the source file.

A clip bends to the grid exactly as far as the map you supplied says. Converting seconds, samples, and PPQ shows how to compute an anchor from a musical position.

Your app owns the anchors

A clip follows its warp map, not the tempo map directly. The map's anchors are absolute pairs of sample positions that your app computes from the tempo and supplies; the engine reads them and never invents them. Editing the tempo map afterwards does not rewrite them, so a host that lets the user change tempo has to recompute the anchors and push a new warp map. setWarpMap refuses a map with fewer than two anchors. A repitch or time-stretch clip with no warp map attached plays at its native rate, and a tempo-sync clip with none fails to compile.

tempo-sync is real time-stretching, with limits

Because tempo-sync changes duration while preserving pitch, it uses a phase vocoder under the hood — the same family of algorithm described in Phase Vocoder Stretch. Small stretches are transparent; very large ones can smear transients or add a "phasey" quality. On stereo and multichannel clips every channel follows one shared phase evolution, so the stereo image does not drift between left and right. repitch has none of these artifacts but moves the pitch, and it reads the source with linear interpolation and no anti-aliasing filter, so a large speed-up can alias (fold high frequencies back as false lower tones) on bright material. The two modes trade different costs.

tempo-sync stretches each span between two anchors as a separate pass and joins the spans with a short blend, so every anchor restarts the vocoder's phase. On sustained, tonal material a map with many closely spaced anchors can make those joins audible; place anchors on attacks where you can. (Audio whose source sample rate differs from the project rate is converted once at compile with a band-limited resampler, so that conversion does not alias.)

How playback and bounce stay consistent ​

A subtle but important guarantee: a clip warps the same way whether you are auditioning it live or rendering the final file. The tempo map, the warp mode, and the anchors are part of the project's edit model, so real-time playback and the offline bounce produce matching timing and pitch. What you hear while editing is what you get on render.

How libsonare implements this

Warp lives on the Project editing model. A clip's mode is set with setClipWarpMode(clipId, mode) where the mode is one of 'off' | 'repitch' | 'tempo-sync' | 'time-stretch'. Warp anchors are first-class warp maps: setWarpMap({ id, name?, anchors }), with each anchor as { warpSample, sourceSample }, and a clip references one via setClipWarpRef(clipId, warpRefId) (removeWarpMap clears it). The grid comes from setTempoSegments (each { startPpq, bpm, endBpm? }, where endBpm drives a ramp) and setTimeSignatures (each { startPpq, numerator, denominator }). For tempo-sync, the stretch is performed by StreamingPhaseVocoder so the pitch is preserved, and the same path is used in both realtime playback and offline bounce() — repitch, by contrast, resamples and lets pitch track speed, and time-stretch reads the same map as repitch but overlap-adds source segments on the audio thread, with warpStretchOverflowCount() reporting the blocks that fell back to resampling. All of warp mode, anchors, tempo, and time signatures round-trip through project JSON via toJson() / Project.fromJson(json).

Related: Project Editing, Phase Vocoder Stretch, Project Bounce