Skip to content

Commit eb44ede

Browse files
author
Michal Warda
committed
Gate separation by source presence
1 parent 8c2090a commit eb44ede

16 files changed

Lines changed: 836 additions & 116 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ curl -f http://localhost:8000/v1/separate \
7272
-o separation.zip
7373
```
7474

75+
The optional `targets` form field accepts `music`, `voice`, or
76+
`music,voice` (default). A single target runs only one generation stage and the
77+
ZIP contains that target plus its SFX residual. The durable pipeline sets this
78+
field from its Audio Flamingo source-scene presence preflight:
79+
80+
```bash
81+
curl -f http://localhost:8000/v1/separate \
82+
-F audio=@mixture.wav \
83+
-F targets=voice \
84+
-o voice-and-residual.zip
85+
```
86+
7587
Generation, prompt, batching, and TF32 policy are configured with the
7688
`SAM_AUDIO_*` environment variables demonstrated in
7789
[`deploy/start-sam-audio-api.sh`](deploy/start-sam-audio-api.sh). A supplied

docs/pipeline/ARCHITECTURE.md

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,47 @@ rule are persisted under `adaptive_routing`.
1818

1919
1. The control API appends an upload job to a persistent dataset and returns S3 upload URLs. API requests finish
2020
after durable acceptance; they do not stay open while inference is queued.
21-
2. An ingestion worker normalizes each source with FFmpeg and creates 30-second
22-
chunks with 5-second overlap. The final short chunk is retained.
21+
2. An ingestion worker probes the source before chunking. Exactly two-channel
22+
stereo is required; mono and multichannel sources are durably marked
23+
`non_stereo_input` and skipped without model work. Accepted sources are
24+
normalized with FFmpeg into 30-second chunks with 5-second overlap. The
25+
final short chunk is retained.
2326
3. A sound gate measures peak and RMS level. Empty or extremely quiet chunks are
2427
marked `skipped` without consuming GPU time.
25-
4. Each audible chunk is sent to the single SAM Audio queue. One GPU worker
26-
receives one message at a time and calls the local `sam-audio-small-tv` API.
27-
5. SAM Audio separates `music soundtrack` from the original chunk, then
28-
`human voices` from the music residual. Candidate expansion and Judge/CLAP
28+
4. Audio Flamingo analyzes the complete source before SAM work is scheduled and
29+
returns `has_music` and `has_voices`. A malformed response fails open to both
30+
targets; a valid negative prevents that separation stage from running.
31+
5. Each audible chunk is sent to the single SAM Audio queue with zero, one, or
32+
two requested targets. Zero targets retains the normalized stereo chunk as a
33+
bit-identical SFX pass-through without calling SAM. One target runs one SAM
34+
stage. Two targets use the normal cascade.
35+
6. SAM Audio normally separates `music soundtrack` from the original chunk,
36+
then `human voices` from the music residual. Candidate expansion and Judge/CLAP
2937
evidence determine `success`, `uncertain`, or `failure` independently for
3038
each stage. A failed complete cascade triggers the bounded reverse-order
3139
attempt described above; it never recursively separates the SFX residual.
32-
6. Music, voice, and final residual (SFX) WAVs are retained as the original mono
40+
These judges measure candidate quality, not whether the requested sound was
41+
present; source-scene preflight owns presence decisions.
42+
7. Music, voice, and final residual (SFX) WAVs are retained as the original mono
3343
model outputs. A CPU post-process also creates a stereo companion for every
3444
audible stem by matching it against the original stereo chunk in 32
35-
log-frequency bands. Per-band left/right correlation and loudness are
36-
smoothed with a bidirectional EMA (`alpha=0.03`) in time and a three-bin
37-
frequency kernel before equal-power panning. Only a final peak limiter is
38-
allowed to alter the reconstructed level.
39-
7. Raw and stereo-mapped WAVs, the 120-point pan/loudness trajectory, inference
45+
log-frequency bands. Per-band left/right correlation controls panning, while
46+
loudness transfer is broadband so it cannot re-EQ the stem. Both are smoothed
47+
with a bidirectional EMA (`alpha=0.03`) before equal-power panning. An SFX-only
48+
pass-through bypasses mapping and keeps the original stereo PCM exactly.
49+
8. Raw and stereo-mapped WAVs, the 120-point pan/loudness trajectory, inference
4050
metadata, timings, and review assertions are stored in S3 and indexed in
4151
DynamoDB. A stereo mapping failure never discards or delays the raw result.
42-
8. Description and transcription tasks share one Audio Flamingo queue so that a
52+
9. Description and transcription tasks share one Audio Flamingo queue so that a
4353
single loaded model processes one item at a time. It creates one source-scene
4454
analysis per audible source, one description per music stem, and one
4555
speaker-labelled transcript per voice stem. Tasks exist in DynamoDB before
4656
SQS delivery and are reconciled after worker/message loss.
47-
9. A browser dashboard shows job/queue progress and exact remaining review
57+
10. A browser dashboard shows job/queue progress and exact remaining review
4858
counts. Its reviewer mode pulls the next `uncertain` or `failure` assertion,
4959
starts audio automatically, displays the prompt/assertion, and accepts
5060
one-key decisions.
51-
10. The dataset explorer reports source count, duration, input/chunk/stem storage,
61+
11. The dataset explorer reports source count, duration, input/chunk/stem storage,
5262
processing counts, stem mix, and review backlog. Each source displays the
5363
original recording first, then only the music/voice/SFX stems that passed the
5464
output sound gate, grouped by chunk, followed by its processing route and

docs/pipeline/OPERATIONS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ than keep an inference request open.
5858

5959
## Stereo mapping
6060

61+
The pipeline accepts exactly two-channel stereo sources. Mono and multichannel
62+
uploads remain visible in the dataset but finish with `skip_reason` set to
63+
`non_stereo_input`; they create no chunks, stems, or model tasks.
64+
65+
For accepted sources, Audio Flamingo scene analysis runs before SAM. Its
66+
`has_music` and `has_voices` booleans select zero, one, or both SAM targets. A
67+
malformed scene response deliberately falls back to both targets. Judge/CLAP
68+
scores remain separation-quality evidence and are not used as presence scores.
69+
6170
New audible stems automatically keep both versions:
6271

6372
- `{stem}.wav` is the untouched mono model output used by review and existing
@@ -69,6 +78,10 @@ New audible stems automatically keep both versions:
6978
In **Split data**, select a source and use **Raw / Stereo mapped** above the
7079
players. The original player never changes. The maps below the players show the
7180
smoothed trajectory; left is below the center line and right is above it.
81+
Version 2 applies frequency-specific panning but only broadband gain, preventing
82+
the mapper from making stems bass-heavy. A source with neither music nor voices
83+
is stored as a single SFX stem; Raw and Stereo mapped are PCM-identical and the
84+
map is labelled `identity · no EQ`.
7285

7386
Backfill a completed job after deploying the mapper. The command is idempotent
7487
and skips stems that already have a mapped object unless `--force` is supplied:

docs/pipeline/PROGRESS.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ was an example batch size, not a workflow limit.
3232
original-plus-stems source explorer.
3333
- [x] Add frequency-aware stereo/loudness reconstruction while preserving every
3434
raw mono stem, plus a dashboard playback toggle and smoothed trajectory plots.
35+
- [x] Require stereo input, gate SAM targets with source-scene presence, support
36+
single-stage inference, and use identity pass-through for pure SFX sources.
3537
- [x] Add persistent datasets, successive upload jobs, and reconciliation of
3638
uploaded/stale work from DynamoDB back into SQS.
3739
- [x] Provision AWS, deploy services, and run a multi-file end-to-end test.
@@ -60,17 +62,20 @@ was an example batch size, not a workflow limit.
6062
191,551,793 bytes.
6163
- Dataset `991945c1f082446a9ff66d482838f964`, job
6264
`3f0233f2442f4044a0f2967c68cb5a89`, completed all 100 sources and chunks with
63-
zero failed chunks. It stored 284 audible stems (94 music, 91 voice, 99 SFX)
65+
zero failed chunks. It initially stored 284 audible stems (94 music, 91 voice,
66+
99 SFX)
6467
and omitted 16 below-gate outputs (6 music, 9 voice, 1 SFX). The selected
6568
route was music-first for 63 sounds and voice-first for 37.
66-
- The completed random dataset contains 624.4 MiB of indexed originals,
67-
normalized chunks, and raw stems plus 543,877,704 bytes of mapped stereo
68-
companions. All 284 audible stems were backfilled without replacing their
69-
raw keys. Automatic verification placed 161 stems in
70-
success, 54 in uncertain, and 69 in failure, leaving 123 review items.
69+
- After the presence repair, the completed random dataset contains 282 stems
70+
and 540,037,650 bytes of mapped stereo companions. All 282 companions were
71+
force-migrated to stereo v2 (or the identity path) without replacing their
72+
raw keys. Automatic verification places 159 stems in success, 54 in
73+
uncertain, and 69 in failure, leaving 123 review items.
7174
- Browser verification confirms the review screen shows exact remaining,
7275
failure, and uncertain counts; the dataset screen shows 100 sounds and its
73-
storage breakdown; and gated stems are absent from the stacked track view.
76+
storage breakdown; gated stems are absent from the stacked track view; the
77+
repaired writing source shows `sfx only` plus `identity · no EQ`; and the mono
78+
canary shows the explicit stereo-input skip reason.
7479
- Live reference job `ae31001eec754ee6850e3727134de84e` processed the supplied
7580
30-second `chunk_ours/og.wav` through the normal production handler and
7681
created raw plus stereo music, voice, and SFX objects. The rendered dashboard
@@ -81,6 +86,17 @@ was an example batch size, not a workflow limit.
8186
(right), matching the attached chunk-zero analysis. All three 30-second stems
8287
map in about 0.58 seconds locally; the fresh remote job mapped its three stems
8388
in about 1.02 seconds.
89+
- Source `099_writing_mHf7COLGcD0_170.wav` exposed both failure modes: its scene
90+
annotation correctly said no music and no voices, but the old cascade still
91+
created three `success` stems, and stereo v1 reduced music high-frequency
92+
energy from 8.7% to 0.8%. Stereo v2 retains 7.8% on the same raw stem. The live
93+
source was repaired from its stored scene evidence and now has one SFX-only
94+
stem; raw, mapped, and original normalized PCM are sample-identical. The
95+
dataset now indexes 282 stems (93 music, 90 voice, 99 SFX).
96+
- Live mono-filter job `5ec26b1bb28b420a9245e21d475d26be` completed with
97+
`non_stereo_input`, one input channel, zero chunks, zero stems, and zero model
98+
tasks. A live `targets=voice` API canary produced only voice and residual
99+
artifacts in one 12.8-second SAM stage.
84100
- Multi-file job `bd85486f9d314c258956cac9b81a730f` completed: one 30-second
85101
audible source produced exactly one chunk and three stems; one silent source
86102
was sound-gated without GPU work; all three Audio Flamingo tasks completed.
@@ -90,7 +106,7 @@ was an example batch size, not a workflow limit.
90106
- Recovery job `1d07979ee96a45a98cf220b102fd7006` was uploaded without the
91107
completion callback. Reconciliation found the S3 object, enqueued ingestion,
92108
and completed it as sound-gated. All three queues were empty afterward.
93-
- Local and remote validation: 28 pipeline tests pass; the existing 12 SAM
109+
- Local and remote validation: 36 pipeline tests pass; the existing 12 SAM
94110
batching tests also pass.
95111
- Docker build execution remains unverified because neither the local Mac nor
96112
the current H100 host has a Docker daemon. Both Dockerfiles remain model-free.

pipeline/src/sam_audio_pipeline/api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ def _dataset_snapshot(aws: PipelineAWS, dataset_id: str) -> dict[str, Any]:
173173
"summary": {
174174
"jobs": len(jobs),
175175
"sources": len(all_sources),
176+
"non_stereo_sources": sum(
177+
source.get("skip_reason") == "non_stereo_input"
178+
for source in all_sources
179+
),
176180
"duration_seconds": sum(
177181
float(source.get("duration_seconds") or 0.0) for source in all_sources
178182
),

pipeline/src/sam_audio_pipeline/audio.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import hashlib
6+
import json
67
import math
78
import subprocess
89
import wave
@@ -63,6 +64,28 @@ def probe_duration(path: Path) -> float:
6364
return duration
6465

6566

67+
def probe_channels(path: Path) -> int:
68+
result = _run(
69+
[
70+
"ffprobe",
71+
"-v",
72+
"error",
73+
"-select_streams",
74+
"a:0",
75+
"-show_entries",
76+
"stream=channels",
77+
"-of",
78+
"json",
79+
str(path),
80+
]
81+
)
82+
streams = json.loads(result.stdout).get("streams", [])
83+
channels = int(streams[0].get("channels") or 0) if streams else 0
84+
if channels <= 0:
85+
raise ValueError(f"Audio channel count is unavailable for {path}")
86+
return channels
87+
88+
6689
def gate_wav(
6790
path: Path,
6891
*,

0 commit comments

Comments
 (0)