Guide
Tempo and key matching
Adapt selected loops to the session's global tempo and key with :match, and the optional live-stretch engine that performs the time and pitch processing.
In Mora, choosing source recordings and fitting them to the performance are separate steps. This guide shows how :match adapts selected loops to global tempo and key targets, how the tempo and key interpretations work, and what the optional live-stretch engine supports.
Global performance targets are separate from library selection. bpm and key set the targets, loops chooses the sources, and :match on a player adapts a chosen source to the targets:
(bpm 140)
(key "Fm")
(d1! (stack
(>> (part :drums 0
:match :tempo)
(cut 1)
(orbit 0))
(>> (stem :type :bass
:n "<0 1 1>"
:match t)
(cut 2)
(orbit 1))
(>> (part :melody 0
:filter '(:synth)
:match t)
(cut 3)
(orbit 2))))Selection need not be clamped to the target BPM or key. Matching retains the chosen source and uses the optional live engine to adjust its time and pitch, so there is no need to restrict discovery to sources already at the target. Exact :key and :bpm filters in loops intentionally narrow the pool; range, mode, genre, and local tag filters answer different questions.
The example above mixes part and stem players, so it stays an ordinary stack. When every layer is a stem, stem-stack makes each one a named lane with its own :match. Changing one lane’s selection and evaluating again hands off that lane while the others keep playing:
(d1! (stem-stack :fade-beats 4
:curve :equal-power
(>> (stem :type :drums
:n 0
:match :tempo)
(cut 1)
(orbit 0))
(>> (stem :type :melody
:id :lead
:n 2
:match t)
(cut 2)
(orbit 1))))The drum lane follows the tempo target only; the melody lane, named :lead, follows both targets.
| Value | Effect |
|---|---|
:match t | Follows the applicable global tempo and key targets. |
:match :tempo | Changes timing only, without asking a source such as a drum loop to claim a tonal key. |
:match :key | Follows the key target only. |
:match defaults to nil, so matching is never automatic. Source metadata and known mode conflicts are checked rather than guessed.
The tempo interpretation accounts for half- and double-time relationships. At a global 140 BPM, a source labeled 75 BPM can follow a 70 BPM half-time reading instead of being stretched as though it should literally become a 140 BPM recording. This is a source-to-session interpretation, not a change to the source’s stored BPM. An eight-bar source read this way spans sixteen master bars.
The normal, half-, or double-time interpretation is chosen separately from source eligibility. The reading is fixed at construction, so it does not flip with every subsequent tempo edit; evaluating the form again reads the master tempo again. :tempo-scale overrides the default :auto with an explicit 1/2, 1, or 2, and pinning half or double time needs :match t or :match :tempo.
Matching does not depend on family grouping, on how a source was selected, or on the recording’s own length: each family member or flat recording keeps its own length and is matched from its own metadata. Source timing, the default for stem slices, keeps both the matching and this frozen reading: one slice lasts the recording’s effective length under that reading divided by :slices. See slice timing.
Pitch shifting changes tonic, not mode. A minor recording is not turned into a major one by moving it several semitones.
- Pitched matching with
:match tor:match :keyneeds an explicit globalkeyand a known source tonic.:match :temponeeds no source key. - A source whose mode is unknown is admitted when its tonic is known.
- Known major/minor incompatibility is refused.
- Absent or insufficient source key information is not silently relabeled.
Use mode filtering with :mode in loops and the source metadata, or request tempo-only matching where appropriate. An explicit :mode is stricter than matching: it excludes pitched sources whose mode is unknown, while unpitched material stays eligible.
loop-stretch exposes independent tempo and pitch processing for a chosen sample pattern. It is not ordinary varispeed or SuperDirt psrate compensation, and it does not require pre-rendering every possible target before playing.
The managed reader uses an optional Signalsmith-based native SuperCollider plugin and bounded prepared resources. Its admitted rate and pitch ranges, channel formats, startup alignment, and source-generation identity are part of the preparation contract.
The engine admits playback rates from half to double speed and pitch shifts of up to two octaves in either direction. Unsupported combinations are errors, not silent clamps or a lower-quality fallback. Formats, rates, and latency are bounded.
The engine’s third-party license terms are listed in licensing and assets.
For an eligible registered bass loop, this slows score and read time and compensates pitch:
(d1! (prepare-audio
(>> (part :bass 0)
(hurry 1/2)
(loop-stretch :pitch 0)
(cut 2))
:cycles 16))The preparation window must match the selected source. hurry changes score timing and sample read speed together; in this form, the pitch is compensated.
:match is the higher-level, ongoing global-target workflow. A pitch pattern applies to newly triggered voices; it does not retune an ordinary sample that has already been dispatched.
PHALAR stem pairing constructs players from ranked compatibility results and can request the same :match behavior on them. paired constructs a part in flat mode and a stem in family mode; suggested-stem always constructs a stem, so its result can serve as a stem-stack lane.
Source references
Repository-relative paths in the Mora source where these mechanisms are implemented.
-
src/mini/loop-match.lisp -
src/mini/loop-target-policy.lisp -
src/mini/sample-loop.lisp -
src/mini/stem.lisp -
src/mini/stem-stack.lisp -
native/mora-stretch/