yue2-studio / scripts /test-lyric-structure.py
RalphThings's picture
Upload folder using huggingface_hub
10a3361 verified
Raw History Blame Contribute Delete
4.21 kB
import importlib.util, sys
spec = importlib.util.spec_from_file_location("tl", "/home/claude/yue2-studio/scripts/transcribe-lyrics.py")
tl = importlib.util.module_from_spec(spec); spec.loader.exec_module(tl)
fails = []
def check(label, ok):
print((" ok " if ok else " FAIL ") + label)
if not ok: fails.append(label)
def words(text, t0, word_gap=0.30, dur=0.28):
"""Build word-level chunks the way the pipeline returns them."""
out, t = [], t0
for w in text.split():
out.append({"text": " " + w, "timestamp": (round(t, 2), round(t + dur, 2))})
t += dur + word_gap
return out, t
# --- A realistic word-timed song -------------------------------------------
chunks = []
t = 0.0
V1, t = words("The kettle ticks itself to sleep. Your coat still holding yesterday.", t); chunks += V1
t += 1.9 # instrumental turnaround
C1, t = words("Say it slow, I have got all night. Every word can take its time.", t); chunks += C1
t += 2.1
V2, t = words("Rain has found the kitchen window. Running lines it cannot keep.", t); chunks += V2
t += 1.8
C2, t = words("Say it slow, I have got all night. Every word can take it's time.", t); chunks += C2
stanzas = tl.force_structure(tl.group(tl.words_from({"chunks": chunks})))
out = tl.render(stanzas)
print("\n" + out + "\n")
check("four sections found, not one", len(stanzas) == 4)
check("both section kinds present", "[Verse]" in out and "[Chorus]" in out)
check("the repeated hook is tagged chorus twice", out.count("[Chorus]") == 2)
check("the two verses are tagged verse", out.count("[Verse]") == 2)
check("a near miss still counts as a repeat", "it's time" in out.lower() or "its time" in out.lower())
check("lines do not end in full stops", not any(l.rstrip().endswith(".") for l in out.split("\n")))
check("lines start capitalised", all(l[0].isupper() or l.startswith("[") for l in out.split("\n") if l.strip()))
check("no line runs longer than the cap", all(len(l.split()) <= tl.MAX_WORDS_PER_LINE for l in out.split("\n") if not l.startswith("[")))
# The length cap must never fire before the sentence split, or a phrase gets
# chopped at an arbitrary word and the sentence break lands inside the piece.
check("sentences break before the length cap does", "Say it slow, I have got all night" in out)
check("no orphaned sentence fragment", "\nNight" not in out)
check("both chorus passes read as whole lines", out.count("Say it slow, I have got all night") == 2)
# --- What the OLD code actually received: contiguous sentence chunks --------
# Whisper's sentence timestamps butt up against each other, which is precisely
# why the previous version produced a single [Verse].
contiguous = [
{"text": " The kettle ticks itself to sleep.", "timestamp": (0.0, 4.0)},
{"text": " Your coat still holding yesterday.", "timestamp": (4.0, 8.0)},
{"text": " Say it slow, I have got all night.", "timestamp": (8.0, 12.0)},
{"text": " Every word can take its time.", "timestamp": (12.0, 16.0)},
{"text": " Rain has found the kitchen window.", "timestamp": (16.0, 20.0)},
{"text": " Running lines it cannot keep.", "timestamp": (20.0, 24.0)},
{"text": " Say it slow, I have got all night.", "timestamp": (24.0, 28.0)},
{"text": " Every word can take its time.", "timestamp": (28.0, 32.0)},
{"text": " You are humming something older.", "timestamp": (32.0, 36.0)},
{"text": " Than the both of us asleep.", "timestamp": (36.0, 40.0)},
]
old_style = tl.group(tl.words_from({"chunks": contiguous}))
check("contiguous input really does collapse to one block", len(old_style) == 1)
forced = tl.force_structure(old_style)
check("the fallback rescues it into sections", len(forced) > 1)
rendered = tl.render(forced)
print(rendered + "\n")
check("fallback output still carries tags", rendered.startswith("["))
# --- Degenerate inputs ------------------------------------------------------
check("empty input renders empty", tl.render([]) == "")
check("a short song is left alone by the fallback", len(tl.force_structure([["one", "two"]])) == 1)
check("blank lines are dropped", tl.render([["", " ", "real line"]]).count("\n") == 1)
sys.exit(1 if fails else 0)