import numpy as np
import torch
from transformers import ClapModel, ClapProcessor
clap_id = "laion/clap-htsat-unfused"
clap = ClapModel.from_pretrained(clap_id)
clap_proc = ClapProcessor.from_pretrained(clap_id)
def clap_score(prompt, audio, sr):
"Cosine similarity between the prompt and the audio in CLAP space (prompt adherence)."
if sr != 48000: # CLAP expects 48 kHz
import librosa
audio = librosa.resample(np.asarray(audio, dtype="float32"), orig_sr=sr, target_sr=48000)
inp = clap_proc(text=[prompt], audio=[audio], sampling_rate=48000, return_tensors="pt", padding=True)
with torch.no_grad():
emb = clap(**inp)
a = torch.nn.functional.normalize(emb.audio_embeds, dim=-1)
t = torch.nn.functional.normalize(emb.text_embeds, dim=-1)
return float((a * t).sum())
# Sanity check on random noise (should score low)
print("noise CLAP score:", clap_score("a solo piano melody", np.random.randn(48000) * 0.01, 48000))Text-to-Audio
1. What is Text-to-Audio?
Text-to-Audio (TTA) generates general audio - music, sound effects, ambiences - from a natural-language prompt. It is distinct from Text-to-Speech (which generates spoken words); the two are usually separate models.
Input. A text prompt (“lo-fi hip hop with a mellow piano”, “rain on a tin roof, distant thunder”), optionally with a duration, a melody to condition on, or a seed.
Output. A mono or stereo waveform, typically 16 kHz (MusicGen) up to 44.1 kHz stereo (Stable Audio Open).
Neighbouring tasks:
| Task | What it does | Typical tool |
|---|---|---|
| Text-to-Speech | Generate spoken words | SpeechT5, Bark |
| Audio captioning | The inverse: describe a sound | Qwen2-Audio, CLAP |
| Music continuation | Extend an audio clip | MusicGen (melody) |
| Audio-to-audio | Transform existing audio | Demucs, enhancement |
2. Real-World Use Cases
Text-to-audio covers music and general sound generation, and it is deployed almost entirely as a content production tool - so the binding constraints are about rights and controllability far more than about model quality.
| Use case | Domain | Consumes / produces | Dominant constraint |
|---|---|---|---|
| Background music for video and ads | Marketing, social media (Suno, Udio, YouTube Create) | Text prompt + duration -> music track | Rights clearance and training-data provenance; cost per clip |
| Game audio and adaptive soundtracks | Gaming | Prompt or game state -> loopable music, sound effects | Loopability and seamless transitions; runtime latency if generated live |
| Foley and sound design for post | Film, TV (iZotope-class tooling) | Description (+ video) -> effect aligned to picture | Temporal alignment to picture; 48 kHz delivery fidelity |
| Stock music and sonic branding | Advertising, stock libraries | Prompt -> catalogue of variations | Diversity, and a clean licence chain per track |
| Melody-conditioned drafting | Music production | Prompt + reference melody -> arrangement or stems | Faithfulness to the conditioning melody; separable stems |
| Synthetic data for audio ML | ML engineering | Prompt -> labelled training audio | Label fidelity and acoustic diversity; cost per hour |
What the FAD score hides. The blocker for shipping a text-to-audio feature is usually not audio quality - it is provenance: what the model was trained on, whether the output is licensable, and whether a customer can defend it. Check the training-data and licence terms of a checkpoint before its benchmark numbers. Beyond that: generation is slow and offline (many seconds of GPU time per second of audio), so these models sit in batch pipelines, not in real-time paths. Delivery format bites - most open checkpoints emit 32 kHz mono while broadcast and game engines want 44.1/48 kHz stereo, so an upsampling and mastering stage is part of the system, not an afterthought. And prompt adherence decays with duration: coherence typically holds for tens of seconds, after which structure wanders, loops repeat, and the mix goes muddy - which is why production systems generate short conditioned segments and arrange them, rather than asking for a three-minute song in one shot.
3. How Modern Text-to-Audio Works
Two dominant families, converging on quality:
- Autoregressive codec LM (AudioGen, MusicGen, 2023). Encode audio into discrete EnCodec tokens, then predict them with a transformer LM conditioned on T5 text embeddings. Simple, streamable, strong on music; Meta’s MusicGen is the open reference.
- Latent diffusion (AudioLDM, AudioLDM 2, Tango, 2023). Run a diffusion model in a compressed VAE latent, conditioned on CLAP and/or T5 text features, then decode + vocode to waveform. Strong on general sound effects.
- Latent diffusion transformers / flow (Stable Audio, Stable Audio Open, 2024). A DiT with timing conditioning generates long, high-fidelity 44.1 kHz stereo in one pass. State of the art for open SFX and loops.
- 2025-2026. Faster sampling (consistency / flow-matching), longer coherent structure, and strong closed systems (Suno, Udio) alongside open MusicGen / Stable Audio Open / AudioLDM 2.
4. Evaluation Metrics
Generation has no single reference output, so metrics compare distributions and prompt adherence.
- FAD (Frechet Audio Distance). Distance between the feature distributions (VGGish / PANN embeddings) of generated vs real audio. Lower = more realistic. The primary TTA metric.
- KL divergence. Between PANN class-probability distributions of generated vs reference - measures semantic match.
- CLAP score. Cosine similarity between the CLAP text embedding of the prompt and the audio embedding of the output - measures how well the audio matches the prompt (higher is better).
- IS (Inception Score) and subjective MOS for quality/diversity.
The cell below computes a CLAP score with transformers - the one metric you can run on a single clip.
5. The Model Landscape (mid-2026)
| Model | Params | License | Type | Library | Best for |
|---|---|---|---|---|---|
| facebook/musicgen-small | 300M | CC-BY-NC 4.0 | music (codec LM) | transformers | music from text, melody conditioning |
| facebook/musicgen-stereo-small | 300M | CC-BY-NC 4.0 | stereo music | transformers | stereo music |
| cvssp/audioldm-s-full-v2 | 350M | CC-BY-NC | sound + music (LDM) | diffusers | general sound effects |
| cvssp/audioldm2 | 350M | CC-BY-NC | sound + music (LDM) | diffusers | successor (see note) |
| stabilityai/stable-audio-open-1.0 | 1.1B | Stability Community | 44.1 kHz stereo (DiT) | diffusers | high-fidelity SFX and loops |
| facebook/audiogen-medium | 1.5B | CC-BY-NC 4.0 | sound effects | audiocraft (external) | dense sound scenes |
There is no single crowd leaderboard; papers report FAD/KL/CLAP on AudioCaps (sound) and MusicCaps (music). MusicGen loads through transformers; AudioLDM and Stable Audio Open load through diffusers (the standard Hugging Face diffusion library - a general-purpose lib, so it fits the repo rule). AudioGen needs the external audiocraft runtime.
We run AudioLDM v1 (AudioLDMPipeline) below: it conditions on the CLAP text encoder directly, so it stays robust across transformers versions. The newer AudioLDM 2 uses an internal GPT-2 language-model prompt path that currently breaks against transformers 5.x inside diffusers - use it only with an older, matched diffusers/transformers pair.
6. Setup
Package roles:
transformers(>=5.13) +torch- MusicGen and CLAPdiffusers- AudioLDM (and Stable Audio Open)soundfile- write WAV;librosa- resample for CLAPpyecharts- the benchmark chart
MusicGen and CLAP are already covered by the repo deps; diffusers is the one extra general-purpose library used here.
import ctypes
import ctypes.util
import gc
import time
import urllib.request
from pathlib import Path
import torch
from dotenv import find_dotenv, load_dotenv
# Knowledge/.env sets HF_TOKEN - authenticated HF Hub requests get higher rate limits
load_dotenv(find_dotenv(usecwd=True))
device = "cuda:0" if torch.cuda.is_available() else "cpu"
if device != "cpu":
print(torch.cuda.get_device_name(0))
print("device:", device)
def vram(tag=""):
"Report current GPU memory (allocated / reserved). No-op on CPU."
if torch.cuda.is_available():
alloc = torch.cuda.memory_allocated() / 1e9
reserved = torch.cuda.memory_reserved() / 1e9
print(f"VRAM {tag:16s} {alloc:5.2f} GB allocated / {reserved:5.2f} GB reserved")
def free_memory():
"GC then release cached CPU/GPU memory. Call right after `del model`.\n\n `del` drops the Python reference; this reclaims the RAM and hands the\n freed VRAM back to the CUDA allocator so usage stays flat across cells.\n "
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
# glibc keeps freed CPU allocations in its arenas instead of returning them
# to the OS, so RSS compounds across model sections (cpu-offloaded weights
# live in system RAM). malloc_trim(0) hands the freed arenas back. See
# dl-visualization-and-memory.instructions.md - not optional on a 12 GB box.
try:
ctypes.CDLL(ctypes.util.find_library("c") or "libc.so.6").malloc_trim(0)
except Exception:
pass
# All downloads (samples, HF cache) go to DL_tasks/datasets/ (gitignored)
DATA_DIR = Path("../../datasets")
DATA_DIR.mkdir(exist_ok=True)
import soundfile as sf
PROMPT = "a warm lo-fi hip hop beat with a mellow piano and soft vinyl crackle"
OUT_DIR = DATA_DIR / "tta_out"
OUT_DIR.mkdir(exist_ok=True)
def save_wav(name, audio, sr):
path = OUT_DIR / name
sf.write(path, np.asarray(audio).squeeze(), sr)
print(f"{name}: {np.asarray(audio).squeeze().shape[-1] / sr:.1f} s @ {sr} Hz -> {path}")
return path7. MusicGen (transformers)
The text-to-audio pipeline wraps MusicGen end to end. max_new_tokens sets the length (~50 tokens/second of audio); the output sampling rate comes from the model config (32 kHz). Use facebook/musicgen-melody to additionally condition on a hummed melody.
from transformers import pipeline
musicgen = pipeline("text-to-audio", model="facebook/musicgen-small", device=device)
t0 = time.perf_counter()
out = musicgen(PROMPT, forward_params={"do_sample": True, "max_new_tokens": 256}) # ~5 s of audio
print(f"{time.perf_counter() - t0:.1f}s")
save_wav("musicgen.wav", out["audio"], out["sampling_rate"])
del musicgen
free_memory()
vram("after musicgen")8. AudioLDM (diffusers)
AudioLDM is a latent-diffusion model good at general sound effects as well as music. It loads through diffusers and conditions on a CLAP text encoder; num_inference_steps trades speed for quality and audio_length_in_s sets duration. Runs on CPU (slowly) or GPU with torch_dtype=float16, and outputs 16 kHz.
from diffusers import AudioLDMPipeline
dtype = torch.float16 if device != "cpu" else torch.float32
ldm = AudioLDMPipeline.from_pretrained("cvssp/audioldm-s-full-v2", torch_dtype=dtype,
cache_dir=str(DATA_DIR / "hf_cache")).to(device)
t0 = time.perf_counter()
audio = ldm("rain on a tin roof with distant thunder", num_inference_steps=50,
audio_length_in_s=5.0).audios[0]
print(f"{time.perf_counter() - t0:.1f}s")
save_wav("audioldm.wav", audio, 16000) # AudioLDM outputs 16 kHz
del ldm
free_memory()
vram("after audioldm")9. Head-to-head Benchmark
Same prompt through MusicGen and AudioLDM; report generation RTF and CLAP score (prompt adherence). CLAP is an automatic proxy - for real evaluation use FAD/KL over AudioCaps/MusicCaps. Free each model before loading the next.
# ECharts (pyecharts) is the repo standard for all charts - it renders interactive
# and embeds straight into the Quarto docs via .render_notebook().
from pyecharts import options as opts
from pyecharts.charts import Bar
def bar_chart(title, categories, series, y_name=""):
"Grouped bar chart. `series` is a dict {name: [values aligned to categories]}."
chart = Bar(init_opts=opts.InitOpts(width="720px", height="420px"))
chart.add_xaxis([str(c) for c in categories])
for name, vals in series.items():
chart.add_yaxis(name, [round(float(v), 4) for v in vals])
chart.set_global_opts(
title_opts=opts.TitleOpts(title=title),
yaxis_opts=opts.AxisOpts(name=y_name),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=20)),
tooltip_opts=opts.TooltipOpts(trigger="axis"),
legend_opts=opts.LegendOpts(pos_top="8%"),
)
return chart.render_notebook()from transformers import pipeline
from diffusers import AudioLDMPipeline
results = {}
# MusicGen
mg = pipeline("text-to-audio", model="facebook/musicgen-small", device=device)
t0 = time.perf_counter()
out = mg(PROMPT, forward_params={"do_sample": True, "max_new_tokens": 256})
gen_s = time.perf_counter() - t0
audio, sr = np.asarray(out["audio"]).squeeze(), out["sampling_rate"]
results["musicgen"] = {"rtf": gen_s / (len(audio) / sr), "clap": clap_score(PROMPT, audio, sr)}
del mg
free_memory()
# AudioLDM
dtype = torch.float16 if device != "cpu" else torch.float32
ldm = AudioLDMPipeline.from_pretrained("cvssp/audioldm-s-full-v2", torch_dtype=dtype,
cache_dir=str(DATA_DIR / "hf_cache")).to(device)
t0 = time.perf_counter()
audio = np.asarray(ldm(PROMPT, num_inference_steps=50, audio_length_in_s=5.0).audios[0])
gen_s = time.perf_counter() - t0
results["audioldm"] = {"rtf": gen_s / (len(audio) / 16000), "clap": clap_score(PROMPT, audio, 16000)}
del ldm
free_memory()
for name, r in results.items():
print(f"{name:12s} RTF {r['rtf']:6.2f} CLAP {r['clap']:.3f}")
vram("after benchmark")names = list(results)
bar_chart(
"Text-to-Audio: prompt adherence (CLAP, higher better) and RTF",
names,
{"CLAP score": [results[n]["clap"] for n in names],
"RTF": [results[n]["rtf"] for n in names]},
y_name="score",
)10. Common Frameworks
This task is split down the middle by library rather than by model family: the codec language models (MusicGen, AudioGen) live in transformers and audiocraft, and the latent diffusion models (AudioLDM, Stable Audio Open) live in diffusers. Both are general-purpose Hugging Face libraries, so both are allowed here - but the split is real, and it decides which knobs you get. A diffusion model gives you schedulers and step counts; a codec LM gives you sampling temperature and continuation.
| Framework | Layer | What it gives you | License | Reach for it when |
|---|---|---|---|---|
| transformers | modelling | MusicGen (mono and stereo) with melody conditioning, through the standard generate API | Apache 2.0 | Music from a text prompt, or continuing an existing clip. Note the CC-BY-NC weights |
| diffusers | modelling | AudioLDMPipeline, AudioLDM2Pipeline, StableAudioPipeline - latent diffusion over a mel or waveform VAE |
Apache 2.0 | Sound effects and high-fidelity stereo. Where you want the step-count and scheduler trade shown in section 9 |
| audiocraft | modelling | Meta’s own runtime: AudioGen, MusicGen training and fine-tuning, EnCodec | MIT code, CC-BY-NC weights | You need AudioGen for dense sound scenes, or you are fine-tuning MusicGen on your own catalogue |
| stable-audio-tools | modelling | The training stack behind Stable Audio Open - DiT, autoencoder, conditioning | MIT (weights: Stability Community License) | Training or fine-tuning a 44.1 kHz stereo generator, which diffusers alone will not do |
| torchaudio + soundfile + pedalboard | data | Resampling, loudness normalisation to a broadcast target, and the effects chain that makes generated audio sit in a mix | BSD-2 / BSD-3 / GPL-3.0 (pedalboard) | Always for the first two. Generated audio arrives at inconsistent loudness and clips on export more often than it should |
torch.compile + xformers / SDPA |
inference runtime | Fused attention and a compiled UNet/DiT - the cheap 1.5-2x on the same card | BSD-3 | Generation is the product and every second of wall clock is user-visible. Cheaper than any model change |
| BentoML / Ray Serve | serving | Queue-backed async jobs, which is the right shape when one request takes 30 s and the client polls | Apache 2.0 | You are serving generation to users. A synchronous HTTP handler will time out |
| ComfyUI | orchestration | A node graph over the same diffusers weights: prompt, generate, post-process, loop - without writing the glue |
GPL-3.0 | Iterating on sound design by hand, or handing the controls to someone who does not write Python |
| audioldm_eval + a CLAP score | evaluation | FAD, KL and inception score against AudioCaps/MusicCaps, plus CLAP text-audio similarity for “does it match the prompt” | MIT / Apache 2.0 | Comparing checkpoints. FAD alone rewards fidelity and says nothing about whether the prompt was followed - report both |
The 2026 default stack is diffusers for sound effects and transformers for music, with pedalboard or ffmpeg doing loudness normalisation on the way out and a CLAP score gating what you keep. Reach past that only for a specific reason: audiocraft for AudioGen or fine-tuning, stable-audio-tools for 44.1 kHz stereo training.
The common wrong turn is licensing. MusicGen and AudioGen weights are CC-BY-NC: fine for this notebook, not fine in a product, and the code license being MIT does not change that. Stable Audio Open’s Stability Community License has its own revenue threshold. Check the weight license separately from the library license, every time.
11. Going Further
- Higher fidelity.
stabilityai/stable-audio-open-1.0(diffusersStableAudioPipeline, gated) generates 44.1 kHz stereo up to ~47 s - the best open SFX/loop quality available. - Melody conditioning.
facebook/musicgen-melodycontinues or re-harmonizes a reference melody, which turns the model from a slot machine into an instrument. - Longer / structured music. MusicGen supports sliding-window continuation, but coherence over minutes is still the open problem; commercial Suno / Udio lead on song structure.
- Prompting is the biggest free win. Genre, instrumentation, tempo, recording style and mood in one sentence beats a bare noun phrase by more than a model upgrade does. Keep a prompt log next to your outputs.
- Related notebooks.
00_Text_to_Speech(generated audio that has a reference transcript to be faithful to),04_Audio_Classification(CLAP again, used to score rather than to generate),Computer_Vision/04_Text_to_Image(the same latent-diffusion machinery, one modality over).