Separate stems in a module
Split a mix into vocals, drums, bass and other with the host separation task, then play the aligned stems through your Processor.
Stem separation turns one recording into aligned parts you can mute, solo, remix or resample. It is part of AI tasks in your module. For what to expect from separated stems, read Split a song into stems in the Help Center.
1. Declare the task and the stem buffer
The host separates and prepares the stems; your Processor plays them. Declare the capability and one in-place data resource sized for the longest clip:
implementation: {
kind: "processor",
source: "src/dsp/processor.ts",
resources: [
{
id: "stems",
kind: "data",
elementType: "f32",
maxElements: 2 + 8 * SEPARATION_MAX_FRAMES,
loading: "preload",
storage: "in-place",
},
],
},
ui: {
entry: "./ui/index.tsx",
hostCapabilities: { optional: ["audio.separation/v1"] },
},
SEPARATION_MAX_FRAMES comes from @maruaudio/audio-contracts (180 seconds at
44.1 kHz). The buffer layout is [sourceRate, frames, vocalsL, vocalsR, drumsL, drumsR, …]: four stereo stems, aligned sample for sample.
The shared @maruaudio/dsp/processors/stem-player leaf plays that buffer on
the sample clock with per-stem gain, mute, solo and pan. StemSep's Processor
delegates to it entirely.
2. Drive it from the face
const separation = face.separation
if (!separation) return <p>Stem separation is not available here.</p>
const { phase, source, results, progress } = separation.state
<Button onClick={separation.chooseSource}>Choose a song</Button>
<Button disabled={!source || phase === "separating"} onClick={separation.separate}>
Separate
</Button>
| Member | Purpose |
|---|---|
methods, setMethod(id) | The separation methods the host offers, each with execution (local, server or commercial), size, availability and a reason when unavailable |
chooseSource(), chooseExample(id) | Pick a file or a host-supplied example |
separate(), cancel() | Run or stop separation |
state.results, selectResult(id) | Each run's stems with peaks and timing; selecting one loads it into the Processor |
exportStem(id), exportAll() | Host-encoded WAV exports |
configureKey?(), forgetKey?() | A person's own key for a commercial provider |
trackRhythm?(source), state.rhythm | Beats and downbeats of the mix or the drum stem (see Beats and tempo) |
clearModelCache() | Remove downloaded model files |
Always show the chosen method's execution. A local method keeps the audio on
the person's device; server and commercial methods upload it.
Current implementations
| Method | Execution | Model |
|---|---|---|
| On this device | local | Spleeter 4-stem |
| Maru server | server | SCNet (evaluation) |
| Commercial provider | commercial | The provider's service, with the person's own key |
Show method labels and descriptions from methods rather than naming models in
your face.
From an app or server
Apps submit separate_audio_stems with a prepared source and the stems they
want. See
Add generative audio to your app.
Limits and availability
- Up to 180 seconds and 128 MiB per source; long clips are processed in 30-second windows.
- Separation runs on website module pages. Studio and native plug-ins do not offer it yet; a module there can still play stems it already has.
Ask your agent
Read the Maru docs page "Separate stems in a module", then read StemSep's source with `maru module files stemsep` and `maru module read stemsep src/index.ts`. Build a module that separates a song and lets me loop and pitch-shift only the vocal stem.