Official repository of AURA, a chat music editor model
Python
0
8 commits
updated Sep 15, 2026
Quoc-Huy Trinh¹⁴ · Minh-Van Nguyen²⁴ · Debesh Jha³
¹ Aalto University · ² Technical University of Denmark · ³ University of South Dakota · ⁴ OpenRB Lab
Chat with a song, ask for edits in natural language, get edited audio back.
Architecture: a Qwen2.5-Omni-7B thinker (LoRA-tuned) listens to the music
and the request, replies conversationally, and emits a typed edit-token block
[EDIT_<KIND>][EDIT_0..7] (7 kinds: ADD / REMOVE / REPLACE / EXTRACT /
REBALANCE / EFFECT / MOOD). The 9 hidden states at those tokens condition a
dual-stream fusion MusicGen bridge (frozen MusicGen-medium decoder; a
music-free source stream fused per layer via bi-dual cross-attention + gated
FiLM; edit tokens as cross-attention K/V with LoRA) that renders the edited
audio through EnCodec. Localized edits are code-anchored outside the requested
segment and seam-crossfaded.
conda env create -f src/environment.yml # creates env "music-edit"
conda activate music-edit
Notes:
weights/ (set HF_HUB_CACHE=$PWD/weights).ckpts/edit_agent/ (not in git).torch.backends.cudnn) — no action needed.The training data is built in stages (all under data/edit_dataset/):
# a) Slakh stem-edit pairs (exact mixes; add/remove/isolate/rebalance/swap)
python src/edit_agent/slakh_edits.py \
--slakh-root data/edit_dataset/slakh_raw/slakh2100_flac_redux/train \
--max-tracks 600 --pairs-per-chunk 2 --traj-per-track 1
# (--ops isolate to restrict ops; --force-split test for held-out builds)
# b) Conversations for the pairs (needs an OpenAI-compatible LLM endpoint,
# see src/data_utils/llm_client.py — default http://localhost:9003)
python src/edit_agent/synth_dialogues_slakh.py --workers 16
# c) Bridge training caches: manifest -> hidden states -> EnCodec codes
python src/edit_agent/precompute_bridge.py --phase manifest
python src/edit_agent/precompute_bridge.py --phase hidden # GPU; SFT thinker
python src/edit_agent/precompute_encodec.py # GPU (needs hidden first)
# d) Optional data-quality score used by --silence-filter
python src/edit_agent/filter_silence.py
Held-out evaluation sets:
# Instruct-MusicGen-comparable benchmark from the Slakh TEST split
python src/edit_agent/build_impg_benchmark.py # -> results/impg_bench/
Stage 1 — thinker SFT (edit-token emission + music chat; see
train_sft.py): produces ckpts/edit_agent/sft/final.
Stage 2 — bridge (MusicGen fusion adapter on cached hidden states):
torchrun --nproc_per_node=2 src/edit_agent/train_musicgen_bridge.py \
--arch fusion --lora-r 64 --lora-alpha 128 \
--batch 8 --accum 2 --steps 40000 --lr 5e-5 --silence-filter
# checkpoints -> ckpts/edit_agent/musicgen_fusion/step_*
# IMPORTANT: select checkpoints with the audio probe, never with CE:
python src/edit_agent/probe_musicgen.py \
--adapter ckpts/edit_agent/musicgen_fusion/step_30000 --arch fusion
Stage 3 — joint (live thinker + bridge; loss = λ·CE_musicgen + (1−λ)·CE_LM, the LM term is Qwen's built-in loss and keeps chat/QA ability):
python src/edit_agent/train_joint.py --arch fusion \
--mg-init ckpts/edit_agent/musicgen_fusion/step_30000 \
--steps 4000 --lam 0.5
# -> ckpts/edit_agent/joint/final/{qwen,musicgen}
Verification: verify_sft.py (edit-token emission), eval_musicgen.py
(60-clip benchmark + FAD), run_impg_bench.py + score_impg_bench.py
(FAD/CLAP/KL/SSIM/P-Demucs/SI-SDR vs published baselines).
API_GPU=1 API_PORT=9004 bash src/scripts/serve_musicgen_api.sh
Endpoints (docs/API.md has full schemas + clients; Swagger at /docs):
POST /chat {text, audio(base64)} → {text}POST /edit {text, audio(base64), segment?, guidance?, seed?} →
{text, audio(base64 wav 32 kHz), ...}The Gradio app chats with the thinker, chunks an uploaded song, and sends edit requests to a render worker. Point it at the production API:
# terminal 1 — the model API (render worker)
API_GPU=1 API_PORT=9004 bash src/scripts/serve_musicgen_api.sh
# terminal 2 — the Gradio UI
WORKER_URL=http://127.0.0.1:9004 \
SFT_ADAPTER=ckpts/edit_agent/joint_fusion_r64/final/qwen \
WEBAPP_PORT=7862 CUDA_VISIBLE_DEVICES=0 \
conda run -n music-edit python src/edit_agent/webapp.py
Open http://localhost:7862: upload a song, pick a chunk, chat
("make the chorus more energetic", "remove the drums from 4 to 8 seconds") and
listen to rendered edits inline. The API accepts the webapp's legacy prompt
field, so no further glue is needed.
@misc{trinh2026auraunifiedmultimodalframework,
title={AURA: Unified Multimodal Framework for Conversational Music Editing},
author={Quoc-Huy Trinh and Minh-Van Nguyen and Debesh Jha},
year={2026},
eprint={2609.14344},
archivePrefix={arXiv},
primaryClass={cs.SD},
url={https://arxiv.org/abs/2609.14344},
}
8 commits
Python
91.7%
HTML
5.4%
Shell
2.8%
Official repository of AURA, a chat music editor model
Python
0
8 commits
updated Sep 15, 2026
Quoc-Huy Trinh¹⁴ · Minh-Van Nguyen²⁴ · Debesh Jha³
¹ Aalto University · ² Technical University of Denmark · ³ University of South Dakota · ⁴ OpenRB Lab
Chat with a song, ask for edits in natural language, get edited audio back.
Architecture: a Qwen2.5-Omni-7B thinker (LoRA-tuned) listens to the music
and the request, replies conversationally, and emits a typed edit-token block
[EDIT_<KIND>][EDIT_0..7] (7 kinds: ADD / REMOVE / REPLACE / EXTRACT /
REBALANCE / EFFECT / MOOD). The 9 hidden states at those tokens condition a
dual-stream fusion MusicGen bridge (frozen MusicGen-medium decoder; a
music-free source stream fused per layer via bi-dual cross-attention + gated
FiLM; edit tokens as cross-attention K/V with LoRA) that renders the edited
audio through EnCodec. Localized edits are code-anchored outside the requested
segment and seam-crossfaded.
conda env create -f src/environment.yml # creates env "music-edit"
conda activate music-edit
Notes:
weights/ (set HF_HUB_CACHE=$PWD/weights).ckpts/edit_agent/ (not in git).torch.backends.cudnn) — no action needed.The training data is built in stages (all under data/edit_dataset/):
# a) Slakh stem-edit pairs (exact mixes; add/remove/isolate/rebalance/swap)
python src/edit_agent/slakh_edits.py \
--slakh-root data/edit_dataset/slakh_raw/slakh2100_flac_redux/train \
--max-tracks 600 --pairs-per-chunk 2 --traj-per-track 1
# (--ops isolate to restrict ops; --force-split test for held-out builds)
# b) Conversations for the pairs (needs an OpenAI-compatible LLM endpoint,
# see src/data_utils/llm_client.py — default http://localhost:9003)
python src/edit_agent/synth_dialogues_slakh.py --workers 16
# c) Bridge training caches: manifest -> hidden states -> EnCodec codes
python src/edit_agent/precompute_bridge.py --phase manifest
python src/edit_agent/precompute_bridge.py --phase hidden # GPU; SFT thinker
python src/edit_agent/precompute_encodec.py # GPU (needs hidden first)
# d) Optional data-quality score used by --silence-filter
python src/edit_agent/filter_silence.py
Held-out evaluation sets:
# Instruct-MusicGen-comparable benchmark from the Slakh TEST split
python src/edit_agent/build_impg_benchmark.py # -> results/impg_bench/
Stage 1 — thinker SFT (edit-token emission + music chat; see
train_sft.py): produces ckpts/edit_agent/sft/final.
Stage 2 — bridge (MusicGen fusion adapter on cached hidden states):
torchrun --nproc_per_node=2 src/edit_agent/train_musicgen_bridge.py \
--arch fusion --lora-r 64 --lora-alpha 128 \
--batch 8 --accum 2 --steps 40000 --lr 5e-5 --silence-filter
# checkpoints -> ckpts/edit_agent/musicgen_fusion/step_*
# IMPORTANT: select checkpoints with the audio probe, never with CE:
python src/edit_agent/probe_musicgen.py \
--adapter ckpts/edit_agent/musicgen_fusion/step_30000 --arch fusion
Stage 3 — joint (live thinker + bridge; loss = λ·CE_musicgen + (1−λ)·CE_LM, the LM term is Qwen's built-in loss and keeps chat/QA ability):
python src/edit_agent/train_joint.py --arch fusion \
--mg-init ckpts/edit_agent/musicgen_fusion/step_30000 \
--steps 4000 --lam 0.5
# -> ckpts/edit_agent/joint/final/{qwen,musicgen}
Verification: verify_sft.py (edit-token emission), eval_musicgen.py
(60-clip benchmark + FAD), run_impg_bench.py + score_impg_bench.py
(FAD/CLAP/KL/SSIM/P-Demucs/SI-SDR vs published baselines).
API_GPU=1 API_PORT=9004 bash src/scripts/serve_musicgen_api.sh
Endpoints (docs/API.md has full schemas + clients; Swagger at /docs):
POST /chat {text, audio(base64)} → {text}POST /edit {text, audio(base64), segment?, guidance?, seed?} →
{text, audio(base64 wav 32 kHz), ...}The Gradio app chats with the thinker, chunks an uploaded song, and sends edit requests to a render worker. Point it at the production API:
# terminal 1 — the model API (render worker)
API_GPU=1 API_PORT=9004 bash src/scripts/serve_musicgen_api.sh
# terminal 2 — the Gradio UI
WORKER_URL=http://127.0.0.1:9004 \
SFT_ADAPTER=ckpts/edit_agent/joint_fusion_r64/final/qwen \
WEBAPP_PORT=7862 CUDA_VISIBLE_DEVICES=0 \
conda run -n music-edit python src/edit_agent/webapp.py
Open http://localhost:7862: upload a song, pick a chunk, chat
("make the chorus more energetic", "remove the drums from 4 to 8 seconds") and
listen to rendered edits inline. The API accepts the webapp's legacy prompt
field, so no further glue is needed.
@misc{trinh2026auraunifiedmultimodalframework,
title={AURA: Unified Multimodal Framework for Conversational Music Editing},
author={Quoc-Huy Trinh and Minh-Van Nguyen and Debesh Jha},
year={2026},
eprint={2609.14344},
archivePrefix={arXiv},
primaryClass={cs.SD},
url={https://arxiv.org/abs/2609.14344},
}
8 commits
Python
91.7%
HTML
5.4%
Shell
2.8%