---
title: "Why My AI Songs Kept Losing Their Endings: Running YuE2 on a 12 GB GPU"
description: "Twelve Mandarin songs came out exactly 240.000 seconds long with the outros missing. The cause wasn't the lyrics — it was a hard cap, a VRAM ceiling, and a planner that ignores BPM."
pubDate: 2026-10-20
category: ai
tags: ["yue2", "comfyui", "ai-music", "local-ai", "gpu", "vram", "mandarin"]
ogImage: "/og/yue2-on-a-12gb-gpu-songs-that-lost-their-endings.jpg"
banner: "/banners/yue2-on-a-12gb-gpu-songs-that-lost-their-endings.jpg"
draft: false
---
YuE2 is a 3B open-weights music model that sings Mandarin from a style prompt
and a lyric sheet. I ran it locally on a 12 GB RTX 3060 through ComfyUI —
twelve original songs in one serial queue — and the first batch came back with
**every single file exactly 240.000 seconds long**. That number is not a
coincidence, and it is not good news.
## Every track was exactly 240.000 seconds
Twelve files, twelve identical durations to the millisecond:
```
01_A1_..._糖画小仙.mp3 duration=240.000000
02_A2_..._溪边捉云记.mp3 duration=240.000000
03_A3_..._寒露拾桂子.mp3 duration=240.000000
...
```
I had asked for `max_duration=240` because the style guide I was working from
said a 12-section structure would land around three and a half minutes. It
landed on 240 seconds exactly — which means the model never decided to stop.
It ran out of room.
## A round number is a confession
YuE2 plans a song with an ABC planner: it reads the lyric sheet, decides how
many bars each section gets, and emits a symbolic sketch. The music generator
then sings that sketch and **ends when the sketch ends**. A finished song
therefore comes out *shorter* than the cap you set — it stops on its own.
Here is the tell, and it costs one shell command:
```bash
d=$(ffprobe -v error -show_entries format=duration -of csv=p=0 take.mp3)
python -c "print(abs(float('$d') - 240.0) <= 0.005)" # True = hard cut
```
If the duration equals `max_duration` to within a few milliseconds, the render
was truncated. The lyric sheet wanted more time than the cap allowed, and the
last thing being sung at the cutoff point got sliced off mid-phrase.
## Confirming the outros were actually missing
Duration alone tells you *that* it was cut, not *what* was lost. So I pulled
the tail of each take and transcribed it with a local speech model (SenseVoice,
CPU — it stays running while the GPU renders):
```bash
ffmpeg -v error -y -i take.mp3 -ss 232 -t 20 -ar 16000 -ac 1 -c:a pcm_s16le t.wav
curl -s -X POST http://127.0.0.1:20129/v1/audio/transcriptions \
-F "file=@t.wav" -F "language=zh"
```
The results were unambiguous:
| Song | Sung up to | What was missing |
|---|---|---|
| 糖画小仙 | final chorus, last line | the entire `[Outro]` |
| 溪边捉云记 | `[Bridge]` at 215 s | solo + final chorus + outro |
| 寒露拾桂子 | `[Bridge]` at 215 s | final chorus + outro |
| 纸鸢与旧木偶 | final chorus, line 5 | rest of chorus + outro |
So: not a mixing artefact, not a fade-out. The songs were structurally
incomplete.
## Three fixes that did nothing, and one that half-worked
### 1. Raising the BPM — no effect
The obvious reasoning: faster tempo, more syllables per second, more lyrics
fit in the same window. I tested this across the batch: the 117 BPM guofeng
tracks and the 140 BPM DJ remixes had **effectively identical throughput**.
YuE2's duration comes from the ABC planner's bar count, not from the BPM token
in your style prompt. The BPM tag shapes the *feel*; it does not buy you room.
Stop tuning it for duration.
### 2. Re-running with the same seed — silently does nothing
ComfyUI was started with `--cache-classic`, so resubmitting a byte-identical
prompt — same style, same lyrics, same seed — hits the node cache. The job
"completes" in about ten seconds and hands you back the same truncated audio
with a `_00002` filename.
It looks like a fast, successful retry. It is a cache hit on a known-bad take.
### 3. Rolling new seeds — works about 3 times out of 4
The planner's bar allocation depends on the seed, so a different seed really can
produce a shorter, complete song from the same lyrics. This rescued three of my
four clipped tracks:
| Song | Original | New seed | Result |
|---|---|---|---|
| 溪边捉云记 | clipped at 281 s | 20002 | 279.7 s, complete |
| 外婆的绿搪瓷碗 | clipped at 300 s | 20007 | 251.9 s, complete |
| 北溟有小鱼 | clipped at 300 s | 20005, 30005 | clipped again |
## The budget is characters per second — and it isn't constant
I tried to turn this into arithmetic: count the Chinese characters, divide by a
throughput figure, get the duration. The measurements refused to cooperate.
| Song | Characters | Cap | Actual | Fits? |
|---|---|---|---|---|
| 糖画小仙 | 556 | 272 s | 258.7 s | ✅ |
| 纸鸢与旧木偶 | 572 | 300 s | 279.9 s | ✅ |
| 溪边捉云记 | 578 | 281 s | **281.0 s** | ❌ cut |
| 小楼檐下听雨眠 | 580 | 300 s | 256.3 s | ✅ |
| 画卷小锦鲤 | 634 | 300 s | 285.9 s | ✅ |
| 三楼窗台的蒲公英 | 693 | 329 s | 247.4 s | ✅ |
| 弄堂口的竹蜻蜓 | 692 | 300 s | 250.0 s | ✅ |
Read the last two rows against the third. **693 characters finished in 247
seconds, while 578 characters overflowed 281 seconds.** A lyrics-to-duration
formula simply does not exist here — the observed throughput ranges from about
2.0 to 2.8 Chinese characters per second, depending on how the planner chops the
sections.
So characters are useful for a *budget warning*, not for setting a timer.
## The limit that actually binds: VRAM
Once you accept "give it enough room and let it close naturally", the cap wants
to be large. On a 12 GB card it cannot be.
Measured peak VRAM while rendering, sampled every five seconds with
`nvidia-smi` during the run:
| `max_duration` | Peak VRAM |
|---|---|
| 240 s | 10.3 GB |
| 272 s | 11.65 GB |
| 278 s | 11.07 GB |
| 281 s | 12.06 GB |
| 300 s | 12.03 GB |
The card has 12.288 GB. **300 seconds is the practical ceiling** — past that
you are one desktop browser tab away from an out-of-memory abort in the middle
of a six-minute render.
## The fix
Three changes, all cheap:
1. **Set the cap to 300 s and let the planner close the song itself.** Do not
chase a target length; give it headroom and it ends where the music ends.
2. **Archive clipped takes instead of mixing them in.** A take whose duration
equals the cap goes to `未自然结尾\` ("did not end naturally"). The output
folder then contains only complete songs, and re-renders never destroy a
usable take.
3. **Cap the seed roulette at three attempts.** Different seeds help, but a song
whose lyrics genuinely need more than the VRAM ceiling allows will fail
forever. Three strikes and you trim the lyric sheet — don't burn an hour of
GPU pretending the next seed will be the one.
## The song that never fit
One of the twelve never made it. **北溟有小鱼 — 593 characters — failed five
times:**
| Attempt | Parameters | Result |
|---|---|---|
| 1 | seed 10005, cap 287 s | hit 287 s |
| 2 | seed 10005, cap 300 s | hit 300 s |
| 3 | seed 20005, cap 300 s | hit 300 s |
| 4 | seed 30005, cap 300 s | hit 300 s |
| 5 | seed 40005, both instrumental sections removed | hit 300 s |
I even deleted the `[Interlude]` and `[Instrumental Solo]` markers — 12 sections
down to 10, not one sung word changed — and it still overflowed. Its planner
wants more time than a 12 GB card can give. The honest fix is to cut about 40
characters of lyric and re-render, and I would rather tell you that than ship a
twelfth track with a bitten-off ending.
## A trap I hit before any of this
Worth its own warning: **YuE2 will sing the Chinese production notes in your
lyric sheet.**
If you paste lyrics that look like this — which is how most lyric sheets are
written —
```
[Intro]
(重鼓点起,急促琵琶轮指,唢呐破空,合成器铺底)
嘿!哈!山海震!
```
…then that parenthetical line becomes a lyric. The model does not know it is a
stage direction. Strip them before submitting:
```python
clean = "\n".join(l for l in raw.split("\n")
if not re.match(r"^\s*[((].*[))]\s*$", l))
```
Arrangement and dynamics belong in the style prompt. The lyric box gets section
tags and sung words, nothing else.
## What I'd do differently
- **Pre-flight the lyric sheet, not the render.** Count characters first. Over
roughly 600 characters on a 12 GB card, expect trouble; over 550, expect to
trim something.
- **Never set `max_duration` to a target length.** Set it generously above the
estimate and let the planner finish. The cap is a safety rail, not a dial.
- **Treat "duration == cap" as a hard failure in the pipeline**, not something
you notice three days later when a chorus sounds wrong.
- **Re-run with a new seed before touching the lyrics** — it is free and works
most of the time. Then stop at three.
## Eleven tracks
These are the ones that closed naturally, sung by a child voice, in Mandarin,
on a 12 GB card that cost less than a year of cloud music credits. Each player
below has synced lyrics — the timing was recovered by transcription and aligned
against the written lyrics, since the model emits no timestamps.
### 1. 糖画小仙 — Sugar-Painting Fairy
*Guofeng electronic* · 556 characters · 259 s