Fixing audio that doesn't silence
This commit is contained in:
+23
-2
@@ -841,9 +841,22 @@ def _import_shared_audio(
|
||||
existing: dict = _read_json(audio_json_path) if audio_json_path.exists() else {}
|
||||
|
||||
added = 0
|
||||
normalized = 0
|
||||
for f in audio_files:
|
||||
audio_id = f.stem.lower()
|
||||
if audio_id in existing:
|
||||
# Backfill the audio schema onto an existing entry: make `loop`
|
||||
# explicit (so it's not an invisible optional the way it was before),
|
||||
# and drop `zoom` — a video-only field that means nothing for audio and
|
||||
# only ended up here from legacy/hand-edited entries.
|
||||
entry = existing[audio_id]
|
||||
if isinstance(entry, dict):
|
||||
if "loop" not in entry:
|
||||
entry["loop"] = None
|
||||
normalized += 1
|
||||
if "zoom" in entry:
|
||||
del entry["zoom"]
|
||||
normalized += 1
|
||||
if verbose:
|
||||
print(f" Skipping {audio_id} (already in audio.json)")
|
||||
continue
|
||||
@@ -851,16 +864,24 @@ def _import_shared_audio(
|
||||
"file": f.name,
|
||||
"is_shared": True,
|
||||
"volume": 1.0,
|
||||
# One-shot by default (null → not looping); set true for background
|
||||
# music/ambience that should loop for the whole video.
|
||||
"loop": None,
|
||||
}
|
||||
added += 1
|
||||
if verbose:
|
||||
print(f" Added shared audio: {audio_id}")
|
||||
|
||||
if added > 0:
|
||||
if added > 0 or normalized > 0:
|
||||
with open(audio_json_path, "w", encoding="utf-8") as fh:
|
||||
json.dump(existing, fh, indent=2)
|
||||
parts = []
|
||||
if added:
|
||||
parts.append(f"+{added} shared audio files")
|
||||
if normalized:
|
||||
parts.append(f"{normalized} field(s) normalized")
|
||||
print(
|
||||
f" Updated {audio_json_path.relative_to(project_path)} (+{added} shared audio files)"
|
||||
f" Updated {audio_json_path.relative_to(project_path)} ({', '.join(parts)})"
|
||||
)
|
||||
else:
|
||||
if verbose:
|
||||
|
||||
+52
-7
@@ -1544,16 +1544,61 @@ def build_filter_complex(
|
||||
)
|
||||
audio_labels_to_mix.append(f"[{label}]")
|
||||
else:
|
||||
# One-shot audio: delay to trigger time. Chunking v2: seek in if
|
||||
# the clip began in an earlier chunk (docs/chunking_v2.md).
|
||||
# One-shot audio. Freeze it through narration pauses too (like the
|
||||
# looping branch above): split the source at each pause and delay
|
||||
# the remainder, so the clip resumes on the exact sample it stopped
|
||||
# on when the cutscene ends — it is never restarted. A pause set on
|
||||
# a cutscene video therefore silences background one-shots for its
|
||||
# duration. `ignore_pauses` opts a clip out (e.g. a stinger meant to
|
||||
# keep playing under the freeze). Chunking v2: src_offset seeks in
|
||||
# when the clip began in an earlier chunk (docs/chunking_v2.md).
|
||||
label = f"aud{i}"
|
||||
delay_ms = int(event.start_time * 1000)
|
||||
_off = getattr(event, "src_offset", 0.0)
|
||||
_seek = f"atrim={_off:.3f},asetpts=PTS-STARTPTS," if _off > 0 else ""
|
||||
filters.append(
|
||||
f"[{audio_idx}:a]{_seek}adelay={delay_ms}|{delay_ms},volume={volume:.2f}[{label}]"
|
||||
relevant_pauses = (
|
||||
[]
|
||||
if event.audio_def.ignore_pauses
|
||||
else [
|
||||
p
|
||||
for p in (plan.narration_pauses or [])
|
||||
if p.output_time > event.start_time
|
||||
]
|
||||
)
|
||||
audio_labels_to_mix.append(f"[{label}]")
|
||||
if not relevant_pauses:
|
||||
delay_ms = int(event.start_time * 1000)
|
||||
_seek = f"atrim={_off:.3f},asetpts=PTS-STARTPTS," if _off > 0 else ""
|
||||
filters.append(
|
||||
f"[{audio_idx}:a]{_seek}adelay={delay_ms}|{delay_ms},volume={volume:.2f}[{label}]"
|
||||
)
|
||||
audio_labels_to_mix.append(f"[{label}]")
|
||||
else:
|
||||
# Play [seg_start, pause) of source, freeze during the pause,
|
||||
# then resume — source position (src_pos) never advances across
|
||||
# the gap. Final segment runs to the source's natural end.
|
||||
src_pos = _off
|
||||
seg_start = event.start_time
|
||||
seg_count = 0
|
||||
for pause in relevant_pauses:
|
||||
if pause.output_time > seg_start:
|
||||
seg_dur = pause.output_time - seg_start
|
||||
seg_label = f"{label}_seg{seg_count}"
|
||||
d_ms = int(seg_start * 1000)
|
||||
filters.append(
|
||||
f"[{audio_idx}:a]atrim={src_pos:.3f}:{src_pos + seg_dur:.3f},"
|
||||
f"asetpts=PTS-STARTPTS,adelay={d_ms}|{d_ms},"
|
||||
f"volume={volume:.2f}[{seg_label}]"
|
||||
)
|
||||
audio_labels_to_mix.append(f"[{seg_label}]")
|
||||
src_pos += seg_dur
|
||||
seg_count += 1
|
||||
seg_start = pause.output_time + pause.duration
|
||||
seg_label = f"{label}_seg{seg_count}"
|
||||
d_ms = int(seg_start * 1000)
|
||||
filters.append(
|
||||
f"[{audio_idx}:a]atrim={src_pos:.3f},"
|
||||
f"asetpts=PTS-STARTPTS,adelay={d_ms}|{d_ms},"
|
||||
f"volume={volume:.2f}[{seg_label}]"
|
||||
)
|
||||
audio_labels_to_mix.append(f"[{seg_label}]")
|
||||
|
||||
# Extract and mix audio from triggered video events
|
||||
_have_audio = video_events_with_audio or set()
|
||||
|
||||
Reference in New Issue
Block a user