Tempo Convert Tempo, Preserve Duration

The Convert Tempo, Preserve Duration tool re-targets a MIDI file (or a part of it) from one musical tempo to another without changing how long it takes to play back in real time. A 90 BPM vocal recording dropped into a 180 BPM project will line up bar-for-bar with the project grid and sound exactly the same speed it always did.

💡 Find it in: Tools → Tempo Tools → Convert Tempo, Preserve Duration…, or right-click a track / channel / selection and pick the same entry from the context menu.

Convert Tempo, Preserve Duration dialog

The dialog with live preview, scope chips and tempo-mode picker.


Why this exists

Two existing operations almost solve the problem but neither is enough on its own:

Combining the two by hand is fiddly and easy to get wrong. Convert Tempo, Preserve Duration does both in one atomic, undoable operation and shows the projected duration delta before you commit.


The math - one formula, one example

scale = target_bpm / source_bpm new_tick = round(old_tick * scale) new_duration = round(old_duration * scale) tempo_meta := target_bpm

Worked example, 90 BPM → 180 BPM (scale = 2.0):

QuantityBeforeAfter
Note start tick480960
Note length (ticks)240480
Tempo meta90 BPM180 BPM
Real-time start0.333 s0.333 s
Real-time length0.167 s0.167 s

Real playback duration is preserved; the file now lives in the host project's 180 BPM grid. Round-trip 90 → 180 → 90 may introduce ≤ 1 tick of drift per event from rounding; the dialog shows a toast when this happens, and the optional Snap to grid checkbox cleans it up.


The dialog - field by field

FieldWhat it does
Source BPM Auto-detected from the tempo at tick 0; the label shows (detected). Override it manually for files whose first tempo event lies elsewhere.
Target BPM Where you want to end up. Defaults to the current project tempo.
Scope Whole project, Selected tracks, Selected channels or Selected events. A chip strip below the combo lists the affected tracks / channels and lets you add or remove entries on the fly - the right-click entry points pre-fill this strip from your panel selection.
Tempo handling Replace tempo map with target tempo (default), Scale existing tempo map, or Keep tempo map, scale events only. The default does what most people mean by "convert tempo": rewrite the tempo meta to the target BPM and scale every event tick by target/source so the real-time playback length stays identical. See the modes section for the other two.
Include checkboxes Notes, controllers, pitch bend, program changes, aftertouch, lyrics & text events, markers and time signatures - all on by default except time signatures, which are intentionally off (scaling them moves the bar lines relative to the notes and is rarely what you want).
Quantize after conversion Optional. Runs the existing quantizer with the project grid right after the conversion so the result lands cleanly on beats.
Live preview pane Recomputes on every field change (100 ms debounce) without modifying the file. Shows the projected new duration with a Δ indicator, the scale factor, the affected event count and the number of source tempo events.

Tempo handling modes

ModeBehaviourUse when
Replace tempo map with target tempo
(default - "just convert the BPM")
All TempoChange events are removed and a single tempo at target_bpm is inserted at tick 0. Event ticks scale by target/source so note lengths grow / shrink to match. The piece sounds exactly the same speed as before but now lives at the new BPM. The common case: re-target a clip from one tempo to another. 90 BPM vocal → 180 BPM project, etc.
Scale existing tempo map Tempo events keep their tick-relative position and their BPM values are inverse-scaled so the real-time tempo curve is preserved at the new musical-tick rate. Pieces with a written-in ritardando / accelerando that you want to retime without flattening the tempo changes.
Keep tempo map, scale events only The tempo map is left exactly as it was. Only the selected event ticks are rescaled. You want a half-time / double-time correction inside a project that already runs at the correct musical tempo.

Channel-scoped conversions cannot use Replace mode. Rewriting the project tempo from a single channel's local conversion would silently retime every other channel, so the dialog disables Replace when scope is Selected channels and forces Events only (or, eventually, Scale tempo map).


Entry points

All four entry points open the same dialog and route through the same onConvertTempoPreserveDuration(ScopeHint) slot. The dialog itself remains the source of truth - you can change scope, chips, modes and include-flags before clicking OK.


Safety guarantees


Warnings the dialog raises

SituationWhat you'll see
Source ≡ Target BPMOK button disabled. Footer: "Source and target tempo are identical - nothing to do."
Scope returns 0 eventsOK disabled. "No events in scope."
> 1 tempo event in source + Replace modeYellow warning row: "Source contains N tempo changes - they will be collapsed to a single tempo at the target BPM."
Time signatures unchecked but selection contains themInline warning: bar lines will visually drift relative to the notes after conversion.
Round-trip driftAfter-action toast: "N events scaled. Round-trip back to source BPM may differ by ≤ 1 tick per event."