Fix first word being cut at clip start ('Žena' problem)
Real-world failure: 'Ansambel Saša Avsenika - ŽENA ME TEPE'
- Refren starts with 'Žena me tepe' at 78.0s
- Scribe's segment boundary: word 'Žena' was end of previous segment (73.9-78.2s)
while new segment 'tepe, mi prazni žepe' started at 78.3s
- Claude picked clip start = 78.3s (segment boundary)
- Fade-in 0.4s on vocal start = inaudible 'Že-'
- User hears: '...na me tepe' (cut)
Three-part fix:
1. PROMPT: instruct Claude to start clip ~0.3s BEFORE first chorus word
(not exactly at it). Concrete example with timing math.
2. POST-LLM EXTENSION: scan corrected_segments for boundary cases:
- If clip start falls MID-segment → extend back to segment start - 0.2s
- If a previous segment ended within 0.5s of clip start → check if its
last word might actually be the first chorus word, extend back to it
- Uses word-level timestamps when available (Scribe provides these)
3. FADE-IN: was 0.4s when starting on vocal — too long, audibly cuts first
word. Reduced to 0.05s (just click prevention, not audible). Still 0.2s
for instrumental intros where fade is musically appropriate.
Now 'Žena' will be heard fully — clip starts at ~77.5-77.7s, word starts
at 78.0s, plenty of buffer.
This commit is contained in:
parent
1cc8e8be35
commit
a5097c5acc
@ -742,9 +742,12 @@ def detect_audio_fade(clip_range, transcript, video_duration=None):
|
|||||||
if video_duration is not None:
|
if video_duration is not None:
|
||||||
extended_end = min(extended_end, video_duration)
|
extended_end = min(extended_end, video_duration)
|
||||||
|
|
||||||
fade_in = 0.4 if starts_in_vocal else 0.2
|
# Fade-in: če clip začne MED vokalom, fade-in mora biti zelo kratek
|
||||||
|
# da ne odreže prve besede. Pri vokalnem začetku samo 0.05s "smooth click prevention",
|
||||||
|
# ne pravi audible fade. Pri instrumentalnem intro lahko 0.2-0.3s.
|
||||||
|
fade_in = 0.05 if starts_in_vocal else 0.2
|
||||||
# Krajši fade out (0.5s) ker zdaj clip konča po koncu vokala
|
# Krajši fade out (0.5s) ker zdaj clip konča po koncu vokala
|
||||||
fade_out = 0.5 if ends_in_vocal else 0.3
|
fade_out = 0.3 if ends_in_vocal else 0.4
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"fade_in": fade_in,
|
"fade_in": fade_in,
|
||||||
@ -838,7 +841,8 @@ PROSIM:
|
|||||||
## ⚠️ ABSOLUTNO PRAVILO: clip se ZAČNE na PRVI BESEDI prvega refrena
|
## ⚠️ ABSOLUTNO PRAVILO: clip se ZAČNE na PRVI BESEDI prvega refrena
|
||||||
- **NE** vključuj kateri koli verz, pre-chorus, build-up, ali intro
|
- **NE** vključuj kateri koli verz, pre-chorus, build-up, ali intro
|
||||||
- **NE** začni "tik pred" refrenom
|
- **NE** začni "tik pred" refrenom
|
||||||
- **Začetek = točno tam, kjer prva vrstica refrena prvič začne**
|
- **Začetek = ~0.3s PRED prvo besedo refrena** (npr. če prva beseda refrena "Žena" začne pri 78.0s, izberi start = 77.7s)
|
||||||
|
- **Razlog**: 0.3s buffer da ne odrežeš prve besede zaradi audio fade-in efekta
|
||||||
|
|
||||||
## Identifikacija refrena (univerzalno čez jezike):
|
## Identifikacija refrena (univerzalno čez jezike):
|
||||||
- Najdi del, ki se v pesmi **ponavlja vsaj 2-krat** (običajno 3-4x)
|
- Najdi del, ki se v pesmi **ponavlja vsaj 2-krat** (običajno 3-4x)
|
||||||
@ -1350,6 +1354,57 @@ def main():
|
|||||||
duration # ne čez celoten audio
|
duration # ne čez celoten audio
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ── EXTEND clip START nazaj če Claude začne sredi besede/segmenta ──
|
||||||
|
# Refren se pogosto začne na isti besedi kot v transkriptu, ampak Scribe
|
||||||
|
# lahko zazna mejo med segmenti **PO** prvi besedi refrena (npr.
|
||||||
|
# "Žena me tepe" — beseda "Žena" v prejšnjem segmentu pri 78.0s,
|
||||||
|
# nov segment začne pri 78.3s s "tepe"). To pomeni Claude reže
|
||||||
|
# PRED besedo "Žena" → odrezana.
|
||||||
|
#
|
||||||
|
# Strategija: če clip start pade SREDI segmenta (ne tik na začetku),
|
||||||
|
# razširi nazaj na začetek tega segmenta + 0.2s buffer.
|
||||||
|
current_start = clip_range["start"]
|
||||||
|
for seg in corrected_segs:
|
||||||
|
seg_start = float(seg.get("start", 0))
|
||||||
|
seg_end = float(seg.get("end", 0))
|
||||||
|
# Segment ki se prekriva s clip start (start je MED njim)
|
||||||
|
if seg_start < current_start < seg_end:
|
||||||
|
# Razširi nazaj — vendar samo če je segment kratek (<3s) in
|
||||||
|
# se "naslanja" na clip (zadnja beseda lahko vodi v refren)
|
||||||
|
if (seg_end - seg_start) < 3.0:
|
||||||
|
new_start = max(0, seg_start - 0.2) # 0.2s buffer pred prvo besedo
|
||||||
|
if new_start < current_start:
|
||||||
|
print(f" 🎵 Razširim clip začetek {current_start:.1f}s → {new_start:.1f}s "
|
||||||
|
f"(prva beseda refrena je v prejšnjem segmentu)", file=sys.stderr)
|
||||||
|
current_start = new_start
|
||||||
|
break
|
||||||
|
# Segment ki se konča TOČNO ali tik pred clip start (lahko zadnja
|
||||||
|
# beseda refrena = "Žena" se konča na 78.2 ko clip začne 78.3)
|
||||||
|
elif current_start - 0.5 <= seg_end <= current_start + 0.1:
|
||||||
|
# Preveri ali zadnja beseda v segmentu morda **začne refren**
|
||||||
|
seg_text = seg.get("text", "").strip()
|
||||||
|
# Če segment kaže novo frazo (z veliko začetnico po pavzi) ali
|
||||||
|
# vsebuje znake interpunkcije, morda zadnja beseda res začne refren
|
||||||
|
if seg_text and (seg_end - seg_start) < 4.0:
|
||||||
|
# Razširi nazaj na začetek POSLEDJE besede tega segmenta
|
||||||
|
words = seg.get("words", [])
|
||||||
|
if words:
|
||||||
|
last_word = words[-1]
|
||||||
|
new_start = max(0, float(last_word.get("start", seg_start)) - 0.15)
|
||||||
|
else:
|
||||||
|
# Brez word-level: vzemi 0.5s nazaj
|
||||||
|
new_start = max(0, current_start - 0.5)
|
||||||
|
if new_start < current_start:
|
||||||
|
print(f" 🎵 Razširim clip začetek {current_start:.1f}s → {new_start:.1f}s "
|
||||||
|
f"(zadnja beseda prejšnjega segmenta morda začne refren)", file=sys.stderr)
|
||||||
|
current_start = new_start
|
||||||
|
break
|
||||||
|
|
||||||
|
if current_start < clip_range["start"]:
|
||||||
|
clip_range["start"] = round(current_start, 2)
|
||||||
|
clip_range["duration"] = round(clip_range["end"] - current_start, 2)
|
||||||
|
clip_range["reason"] += f" (start extended back)"
|
||||||
|
|
||||||
# Najdi vse segmente ki se začnejo PO trenutnem clip end
|
# Najdi vse segmente ki se začnejo PO trenutnem clip end
|
||||||
for seg in corrected_segs:
|
for seg in corrected_segs:
|
||||||
seg_start = float(seg.get("start", 0))
|
seg_start = float(seg.get("start", 0))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user