Factor5-Sound vollstaendig: Ausklang-Tail im TFMX-Renderer (loop_mode-Stopp + Fade)
Stefans Meldung "Factor5-Zeit zu kurz, Soundfile nicht ganz abgespielt" -- die
Ursache lag im Renderer: real_song_end markiert nur das Ende der TRACK-SEQUENZ
(2.4s), die dann noch klingenden Samples wurden hart abgeschnitten.
- render_tfmx.c: neues optionales 6. CLI-Arg tail_max_seconds. Im Tail-Modus
wird loop_mode=0 gesetzt -- tfmx.h laesst den Sequencer bei song_end dann
STEHEN (kein Restart, keine neuen Noten), waehrend die Paula-Voices weiter
ausklingen. (Erster Versuch ohne loop_mode-Stopp nahm stattdessen die
naechste Loop-Iteration auf -- per RMS-Selbstaehnlichkeit nachgewiesen.)
Tail endet bei ~300ms Stille oder tail-Limit; bei Limit-Ende (loopende
Samples ohne natuerliches Ende) wird ein 600ms-Fade-out angewendet.
- Factor5 ("Brausen") loopt auf Paula-Ebene endlos -> Tail 2.6s => Gesamt
exakt 5.0s (DOSBox-Messung ~4.8s Screendauer, Stefans "ca. 5 Sekunden"),
sauber ausgeblendet. Rauser-Sting endet natuerlich bei 2.34s.
- audio.py: tail_seconds-Parameter, Cache-Suffix _tail (alte abgeschnittene
Caches greifen nicht mehr). intro.py: Jingles mit Tail, Titelmusik bewusst
ohne (exakter Loop-Punkt-Schnitt fuers nahtlose pygame-Loopen).
Screen haelt weiterhin exakt Songlaenge (jetzt 5.0s), verifiziert: WAV klingt
aus (RMS 2065->261), --once headless exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ccad606a43
commit
949a7eeda2
+22
-7
@@ -62,7 +62,7 @@ def _ensure_binary():
|
||||
subprocess.run(['bash', os.path.join(_PLAYER_DIR, 'build.sh')], check=True)
|
||||
|
||||
|
||||
def render_wav(container, name, seconds=180, force=False, song_index=None):
|
||||
def render_wav(container, name, seconds=180, force=False, song_index=None, tail_seconds=None):
|
||||
"""container: kellogg_formats.DATContainer; name: z.B. 'TITEL' (ohne Extension).
|
||||
song_index: optional -- welcher der ggf. mehreren in <name>.TFX gebuendelten
|
||||
Songs gerendert werden soll (siehe Modul-Docstring). None/0 = Default-
|
||||
@@ -78,13 +78,24 @@ def render_wav(container, name, seconds=180, force=False, song_index=None):
|
||||
laengsten bekannten Stuecke hinaus gewaehlt, damit nichts abgeschnitten
|
||||
wird; im Normalfall stoppt render_tfmx laengst vorher von selbst.
|
||||
|
||||
tail_seconds (24.07.2026, Factor5-"Soundfile nicht ganz abgespielt"-Fix):
|
||||
fuer kurze Jingles/Stinger. real_song_end markiert nur das Ende der
|
||||
TRACK-SEQUENZ -- die dann noch klingenden Samples (Ausklang) wuerden ohne
|
||||
Tail hart abgeschnitten. Mit tail_seconds > 0 rendert render_tfmx nach dem
|
||||
Sequenz-Ende weiter, bis ~300ms Stille erreicht sind (max. tail_seconds).
|
||||
Fuer geloopte lange Tracks (Titelmusik) NICHT setzen -- dort ist der
|
||||
exakte Loop-Punkt-Schnitt fuers nahtlose Loopen der richtige.
|
||||
|
||||
ACHTUNG Cache: der Dateiname ist `<name>.wav` (song_index None/0) bzw.
|
||||
`<name>_song<N>.wav` fuer N>0. Wird `seconds` kleiner gewaehlt als die
|
||||
echte Songlaenge, MUSS `force=True` gesetzt werden, sonst liefert diese
|
||||
Funktion einen ggf. laenger/kuerzer gerenderten alten Cache-Treffer
|
||||
zurueck, egal was `seconds` diesmal sagt."""
|
||||
`<name>_song<N>.wav` fuer N>0; mit tail_seconds kommt `_tail` dazu (damit
|
||||
alte, noch abgeschnittene Cache-Dateien nicht faelschlich weiterverwendet
|
||||
werden). Wird `seconds` kleiner gewaehlt als die echte Songlaenge, MUSS
|
||||
`force=True` gesetzt werden, sonst liefert diese Funktion einen ggf.
|
||||
laenger/kuerzer gerenderten alten Cache-Treffer zurueck."""
|
||||
os.makedirs(_CACHE_DIR, exist_ok=True)
|
||||
suffix = f'_song{song_index}' if song_index else ''
|
||||
if tail_seconds:
|
||||
suffix += '_tail'
|
||||
out_path = os.path.join(_CACHE_DIR, f'{name}{suffix}.wav')
|
||||
if os.path.exists(out_path) and not force:
|
||||
return out_path
|
||||
@@ -100,8 +111,12 @@ def render_wav(container, name, seconds=180, force=False, song_index=None):
|
||||
f.write(sam_data)
|
||||
|
||||
cmd = [_BINARY, tfx_path, sam_path, out_path, str(seconds)]
|
||||
if song_index:
|
||||
cmd.append(str(song_index))
|
||||
if song_index or tail_seconds:
|
||||
# song_index ist Positionsarg vor tail_seconds -- bei tail ohne
|
||||
# expliziten Song -1 uebergeben (= Default-Song, siehe render_tfmx.c)
|
||||
cmd.append(str(song_index if song_index else -1))
|
||||
if tail_seconds:
|
||||
cmd.append(str(tail_seconds))
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f'render_tfmx failed for {name} (song_index={song_index}): {result.stderr}')
|
||||
|
||||
Reference in New Issue
Block a user