Guide

Stems and sample libraries

Import a user-owned library, select an eligible pool with loops, compose from it with part and stem by type, variant, family, slice, and tag, and layer stems as replaceable lanes with stem-stack.

Mora can compose from a library of recorded loops by role, such as drums, bass, or melody, rather than by filename. This guide covers importing a user-owned library, selecting an eligible pool, playing variants, families, slices, and tagged subsets from it, and layering replaceable stem lanes.

Importing a Splice library

sync-splice imports Splice content you have already downloaded, together with its available metadata, into Mora’s library. It is designed to be repeatable and incremental.

Common Lisp
(sync-splice)

What the importer keeps

The importer keeps confirmed metadata distinct from guesses. It preserves tags beyond a few predefined genres, maps useful role labels, and carries source identity into the sample catalog.

One-shots can be imported and embedded, but requesting a stem category does not turn them into BPM-, key-, or bar-qualified loops.

Restoring the library at startup

Mora’s normal startup integration can restore your usual saved library, so the catalog does not need to be rebuilt by hand on every launch. Restoring it requires the SuperDirtMora companion’s sample-loading path and sufficient configured memory.

Select a pool, then compose from it

loops describes the eligible pool of source recordings. part and stem construct ordinary temporal patterns that read from it: part selects a typed variant, and stem is a keyword-oriented version for richer variant and slice schedules. stem-stack layers stems as named, replaceable lanes. parts and stem-tags inspect what is available; you do not need to call them before each phrase.

A broad selection:

Common Lisp
(loops :bpm-range '(130 150)
       :mode :minor)

A narrower selection that also names genres:

Common Lisp
(loops :bpm-range '(130 150)
       :mode :minor
       :genre '(:drill :trap)
       :sync nil)

What a selection means

A selection like this names a range, not a hidden exact-BPM or exact-key clamp. It describes a source-tempo palette of pitched material known to be in a minor mode, not one exact tonic. Unpitched percussion remains eligible, and the source-tempo range logic recognizes appropriate half-time and double-time readings.

  • :mode keeps minor or major selection explicit.
  • :genre accepts multiple styles. A plain list is an OR: a source matching any requested genre is eligible.
  • An exact :key or :bpm filter intentionally narrows the pool. Leave a restriction out when you do not want it.

Known metadata and active filters determine eligibility. The importer does not fill in a missing metadata field by guessing.

Selection is not pitch or time processing. Global tempo and key matching is requested on the player, not folded into discovery by filtering everything to one original tempo and key. See tempo and key matching.

Genre expressions

Quoted :and, :or, and :not operators combine genres and may nest. This selects drill or trap, excluding witch house:

Common Lisp
(loops :mode :minor
       :genre '(:and (:or :drill :trap)
                     (:not :witch-house)))

:not takes exactly one expression, and a plain list inside it is still an OR. Names match explicit genre metadata and the corresponding Splice tags, ignoring case and treating spaces as hyphens. Genres are never guessed from tempo or filename, a negative-only query can include untagged sources, and :genre nil clears the filter.

Each selection is complete

loops publishes a complete selection context. Omitted filters are unrestricted; they are not inherited from the preceding query. That includes grouping: repeat :group :family on each call to stay grouped. A bare (loops) reports the current selection and does not reset it.

Requested pool and active playback

Existing part and stem readers follow the new pool at subsequent scheduled onsets. Audio already dispatched is not canceled solely because the library selection changed.

Requested source metadata publishes immediately, so parts describes the requested pool, while playing voices keep their frozen view until the replacement is prepared. If preparation fails, the old score keeps playing with one nonmodal diagnostic; correct the query and run it again. A query that would leave a playing type without eligible sources is refused.

Variants and slices

Variant selection and slicing are independent patterns. The first argument after the type selects variants and may be an integer or mini notation. The optional following string selects slice steps. With no slice pattern, the complete source loop plays over its native length.

Common Lisp
(d1! (part :bass 0))

(d1! (part :bass "<0 1 1>"))

(d1! (part :bass 0 "0 ~ 2 3 4 ~ 6 7"))

In a variant string, <0 1 1> chooses variant 0, then 1, then 1 in successive cycles. A slice pattern rearranges slices; slice numbers are not treated as pitch values.

Slice strings use mini notation, timed by default in ordinary Mora cycles for part and by the source recording for stem (see slice timing). The default source division has eight slices, but slice strings are not required to contain eight tokens. Nested alternation and subdivision are supported, so a rhythm need not be flattened into eight manually counted values, and nested alternations and rests do not retune the sample.

Layering parts

Common Lisp
(d1! (stack
       (>> (part :drums 0 "0 1 2 3 4 4 6 7")
           (cut 1))
       (>> (part :bass "<0 1 1>" "0 ~ 2 3 4 ~ 6 7")
           (cut 2))
       (>> (part :melody 0)
           (cut 3))))

These examples need matching drums, bass, and melody members in your own collection.

The keyword form

stem makes the two axes explicit: :n selects which loop, and :p selects its slices. In family mode, :v also selects a recording within the family. A single unindexed :p string supplies a common slice pattern:

Common Lisp
(d1! (stack
       (>> (part :drums 0)
           (cut 1))
       (>> (part :bass 0)
           (cut 2))
       (>> (stem :type :melody
                 :n "<0 1 1>"
                 :p "0 ~ 2 3 4 ~ 6 7")
           (cut 3))))

No sample filenames need to appear in the performance form. This needs enough eligible variants for each requested type.

Repeated :p entries with numeric positions address occurrences in the variant progression, not globally named sample IDs or :v positions:

Common Lisp
(d1! (stem :type :melody
           :n "<0 1 1>"
           :p 0 "0 ~ 2 3 4 ~ 6 7"
           :p 1 "0 ~ 4 2 3 ~ 6 7"
           :p 2 "2 ~ 0 3 4 ~ 6 7"))

<0 1 1> uses variant 0 for one cycle and variant 1 for the next two occurrences before repeating. Here :p 2 addresses position 2 of the progression, not variant 2: it supplies a second slice strategy for the repeated variant 1, so the two variant-1 occurrences can have different slice patterns.

Slice timing

FormDefault :timingOne cycle of slice notation lasts
stem:sourceOne source slice: the recording’s effective length divided by :slices.
part:cycleOne ordinary Mora cycle.

Both accept either value explicitly, and :slices defaults to 8 regardless of how many tokens the slice string has. The effective length includes the normal, half-time, or double-time reading chosen under matching. With eight slices, the first form below traverses each recording over its own length, even when lanes have different lengths; the second plays one slice per Mora cycle:

Common Lisp
(d1! (stem :type :melody
           :n 0
           :p "<0 1 2 3 4 5 6 7>"))

(d1! (stem :type :melody
           :n 0
           :p "<0 1 2 3 4 5 6 7>"
           :timing :cycle))

With cycle timing, a short recording can finish a slice before the next trigger: the spacing changes, not the speed or pitch. :n and :v progressions always use ordinary Mora cycles.

Replaceable lanes with stem-stack

stem-stack layers stems as named lanes. When the form is evaluated again, unchanged lanes keep playing and each changed lane fades to its replacement on the running audio.

Common Lisp
(d1! (stem-stack :fade-beats 4
                 :curve :equal-power
                 (>> (stem :type :drums
                           :n 0
                           :match :tempo)
                     (cut 1)
                     (orbit 0))
                 (>> (stem :type :bass
                           :n 0
                           :match t)
                     (cut 2)
                     (orbit 1))
                 (>> (stem :type :melody
                           :n 0
                           :match t)
                     (cut 3)
                     (orbit 2))))

Change the melody lane’s :n 0 to :n 1 and evaluate the form again: only that lane hands off, and the drum and bass lanes keep playing. This needs a second eligible melody for the change to be audible.

Lane options and identity

Options come before all lanes: :fade-beats is a positive, finite number of musical beats, not seconds (default 4, one bar in 4/4), and :curve is :equal-power (default) or :linear.

Each lane contains exactly one stem, optionally wrapped in transforms, effects, gain, cut, and orbit. A part or s lane is refused; layer those with ordinary stack, as in layering parts.

A lane is identified by its stem type when that type is unique; when a type repeats, each lane of that type needs a distinct :id on its stem, such as :id :lead. Identity is never :n, :v, an orbit, or a cut group, so reordering lanes keeps it.

How a lane is handed off

  • A lane keeps playing when its literal recipe and selected recording are unchanged; a loops change that alters its recording hands it off even if its text is unchanged.
  • The fade acts on running audio, not only on newly triggered events. It starts shortly after evaluation, on a quarter-cycle boundary rather than the next loop boundary, and incoming lanes join at their current source phase; if preparation misses that point, the edit is refused and the previous lanes keep playing.
  • Removing a lane fades it out. During a fade, only the newest pending edit is kept.
  • A failing lane is silenced on its own with a nonmodal notice while the others continue. Hush and stopping the channel behave as usual.

Mora does not detect equivalent rebuilt expressions, so wrapping a stored stem in a new >> on each evaluation makes a new lane; keep complete routed lanes in variables instead. One stem-stack forms the whole audio assignment: outer transforms may wrap it, but other parallel audio cannot sit outside its lanes.

Families and recordings

The default :group :flat keeps every recording’s original number. :group :family is opt-in and groups related recordings of one musical idea into a family.

Common Lisp
(loops :group :family
       :mode :minor
       :genre '(:drill :trap))

(parts :melody)

In family mode, :n selects the family and :v selects its member recording (default 0). parts lists family and member coordinates, original filenames, and each member’s bars, BPM, and key; full and chopped recordings in one family can differ in length. The two selectors are independent progressions:

Common Lisp
(d1! (stem :type :melody
           :n "<0 1 1>"
           :v "<0 1>"
           :match t))
  • stem accepts a nonnegative integer or numeric mini notation for :v; part accepts one nonnegative integer. Flat mode accepts only :v 0.
  • A changing :v changes the recording only in a family with several members.
  • Numbers wrap within established slot counts. A removed recording leaves a silent, unavailable slot rather than renumbering the others.

Grouping is conservative inference from names, such as trailing treatment words (alternate, dry or wet, fill, chops) within the same pack, type, BPM, and key; meaningful numbers and instrument words stay significant, and ambiguous names stay separate. It is not audio clustering, no audio is deleted, and member 0 is the first member found, not necessarily the dry original.

Family numbering is a separate, persisted view. Returning to flat grouping restores the original numbers:

Common Lisp
(loops :group :flat
       :mode :minor
       :genre '(:drill :trap))

Tags at selection time

Tags remain useful after the initial genre filter. The following restricts the melody candidates to those matching the requested tag:

Common Lisp
(part :melody 0
      :filter '(:synth))

Use the library’s tag inventory rather than assuming that every provider uses the same spelling for an instrument or adjacent genre:

Common Lisp
(stem-tags :melody)

(d1! (part :melody "<0 1 1>"
           :filter '(:synth :wet)))

By default, stem-tags counts tags across the complete registered library; :scope :selection limits the counts to the selected pool:

Common Lisp
(stem-tags :melody
           :scope :selection)

Tag filters follow these rules:

  • A flat :filter list is an AND: it requires every requested tag. The :genre list in loops, by contrast, accepts any requested genre.
  • Keyword and string forms normalize case, spaces, underscores, and hyphens.
  • nil removes the local tag restriction.
  • Filtered variant numbering belongs to that subset and retains established logical identities. It does not reorder the global pool by search relevance. In family mode, families and members are numbered inside the filtered pool.

A tag is a source attribute. Learned compatibility between stems is a separate tool; see PHALAR stem pairing.

Your library and its limits

Available recordings depend on your library. Sample import is explicit, repeatable, and subject to configured limits. Missing metadata, an empty filtered pool, and a sound bank absent from SuperDirt are different conditions.

Mora does not include licensed Splice audio. See licensing and assets.

Source references

Repository-relative paths in the Mora source where these mechanisms are implemented.

  • src/stems/import.lisp
  • src/samples-import/
  • src/mini/loop-library.lisp
  • src/mini/loop-tag-filter.lisp
  • src/mini/loop-families.lisp
  • src/mini/loop-session.lisp
  • src/mini/stem.lisp
  • src/mini/stem-stack.lisp
  • src/mini/stem-stack-runtime.lisp

Documentation

Search the documentation

Results update as you type. Use the up and down arrow keys to choose a result and Enter to open it.