Carte che volano: dalla mano al tavolo e, finita la presa, sul mucchietto di chi ha preso (briscola, tressette, scopa; Andrea: «non si capisce quando vengono buttate le carte»). Il valzer della balera ai tavoli da carte (tools/valzer_carte.py: fisarmonica musette, basso, melodia di mandolino col tremolo), musica per primo piano; video «Come si gioca»: cache del Chromium della VM spenta, narratore e personaggi a turno, musica di sottofondo, pronunce del narratore

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
2026-10-26 18:17:23 +01:00
co-authored by Claude Opus 5.5
parent 7e1e6f6144
commit 538bf6cedb
19 changed files with 263 additions and 43 deletions
+24 -1
View File
@@ -22,10 +22,16 @@ OUT = VIDEO / "out"
# Area del gioco nello schermo della VM (Chromium a tutto schermo, zoom 125 %): misurata con «convert -trim».
TAGLIO = "crop=1044:760:278:70"
FASCIA = 110 # px sopra il gioco per le didascalie
# I video «Come si gioca» (--tutorial): ai tavoli la pagina usa tutta l'altezza, comandi sotto compresi; didascalie
# lunghe anche su tre righe.
TAGLIO_TUTORIAL = "crop=1046:900:268:0"
FASCIA_TUTORIAL = 132
FONDO = "0x1B1420" # il colore di fondo della pagina del gioco
VOLUME = 0.5 # musica della demo-prototipo (senza voci)
VOLUME_SOTTO = 0.22 # musica sotto le voci: bassa (Andrea, 25 ottobre)
VOLUME_VOCI = 1.0
VOLUME_NARRATORE = 1.15
VOLUME_TUTORIAL = 0.07 # musica dei video «Come si gioca»: sottofondo # il narratore dei video «Come si gioca», sopra la musica bassa
FADE = 1.2
@@ -42,6 +48,8 @@ def ts(s: float) -> str:
def main() -> None:
d = Path(sys.argv[1]).expanduser()
tutorial = "--tutorial" in sys.argv[2:]
taglio, fascia = (TAGLIO_TUTORIAL, FASCIA_TUTORIAL) if tutorial else (TAGLIO, FASCIA)
meta = json.loads((d / "cues.json").read_text())
nome = meta["clip"]
registro = OUT / f"{nome}-audio.json"
@@ -59,6 +67,10 @@ def main() -> None:
aperta["fine"] = e["t"]
aperta = None
volume = VOLUME_SOTTO
# Nei video «Come si gioca» parla il narratore quasi sempre: la musica resta un sottofondo (Andrea, 26 ottobre:
# «troppo alta la musica del gioco»).
if (OUT / f"{nome}-narrazione.json").exists():
volume = VOLUME_TUTORIAL
else:
musica = json.loads((OUT / "musica.json").read_text())
volume = VOLUME
@@ -105,9 +117,20 @@ def main() -> None:
lungo = f"atrim=0:{v['fine'] - v['t'] + 0.1:.3f}," if "fine" in v else ""
catene.append(f"[{n}:a]{lungo}asetpts=PTS-STARTPTS,volume={VOLUME_VOCI},adelay={int((v['t'] + scarto) * 1000)}:all=1[v{len(pezzi)}]")
pezzi.append(f"[v{len(pezzi)}]")
# Il narratore dei video «Come si gioca» (video/narra.py): ogni frase all'inizio della sua battuta.
narrazione = OUT / f"{nome}-narrazione.json"
if narrazione.exists():
frasi = {v["id"]: v["src"] for v in json.loads(narrazione.read_text())["voci"]}
for c in cues:
if c["id"] not in frasi:
continue
ingressi += ["-i", str(GIOCO / frasi[c["id"]])]
n = ingressi.count("-i")
catene.append(f"[{n}:a]asetpts=PTS-STARTPTS,volume={VOLUME_NARRATORE},adelay={int((c['start'] + scarto) * 1000)}:all=1[n{len(pezzi)}]")
pezzi.append(f"[n{len(pezzi)}]")
mix = "".join(pezzi) + f"amix=inputs={len(pezzi)}:normalize=0:duration=longest[a]"
stile = f"FontName=DejaVu Sans,FontSize=11,Bold=1,MarginV=4,Outline=1,Shadow=0,PrimaryColour=&H007AD2FF"
video = f"[0:v]{TAGLIO},pad=iw:ih+{FASCIA}:0:{FASCIA}:{FONDO},subtitles={srt}:force_style='{stile}'[v]"
video = f"[0:v]{taglio},pad=iw:ih+{fascia}:0:{fascia}:{FONDO},subtitles={srt}:force_style='{stile}'[v]"
finale = OUT / f"{nome}.mp4"
subprocess.run(["ffmpeg", "-loglevel", "error", "-y", "-i", str(raw), *ingressi,
"-filter_complex", ";".join([video, *catene, mix]), "-map", "[v]", "-map", "[a]",