Rauser-Aufloesung wirklich gefixt (0.5x Nearest-Neighbor statt native 1:1 oder smoothscale) + Factor5-Sound korrigiert (TITEL2-Song1 statt ONGAME2, Multi-Song-Fund)
Pixelvermessung von Stefans Referenz-Screenshot widerlegt die letzte "native Groesse ist richtig"-Annahme -- das Logo ist im Original klein (~34% Bildbreite), nicht bildschirmfuellend. Der eigentliche Bloedsinn war smoothscale() (bilinear, verwaescht Pixel-Art), nicht die 0.5x-Groesse selbst. Fix: 0.5x beibehalten, pygame.transform.scale() (Nearest-Neighbor) statt smoothscale(). Factor5 spielte bisher faelschlich ONGAME2 (~123s Level-Musik) ab. Fund: TFX-Module buendeln mehrere Songs (v_songs[]) -- TITEL2 hat 2 (Song0 = 2.04s Rauser-Sting, Song1 = 2.40s), TITEL 3, ONGAME2 sogar 14. TITEL2-Song1 passt laenge-maessig exakt zu "nur ein bisschen laenger als der Rauser-Sound" -- jetzt der Factor5-Sound, feste 5s Anzeigedauer.
This commit is contained in:
+42
-24
@@ -17,6 +17,23 @@ Weg statt TFMX in Python neu zu erfinden.
|
||||
Gerenderte WAVs sind urheberrechtlich das Original-Spielmaterial (Musik),
|
||||
werden daher NICHT ins Git-Repo committed (wie raw/ und extracted_dat/) --
|
||||
liegen nur lokal im Cache-Verzeichnis.
|
||||
|
||||
MEHRERE SONGS PRO MODUL (Fund 22.07.2026 abends, Stefans Hinweis "das Spiel
|
||||
hat noch wesentlich mehr Lieder"): ein einzelnes TFX-Modul kann intern
|
||||
MEHRERE Songs buendeln (tfmx.h's v_songs[]/v_songs_count, ausgewaehlt per
|
||||
start_song-Index). Stichprobe per Debug-Tool ergab:
|
||||
TITEL.TFX -> 3 Songs (song0/1 lang/loopend >15s, song2 ~8.0s)
|
||||
TITEL2.TFX -> 2 Songs (song0 ~2.04s = Rauser-Bling, song1 ~2.40s = Factor5-
|
||||
Brausen -- Laenge passt zu Stefans "nur ein bisschen laenger
|
||||
als der Rauser-Sound")
|
||||
ONGAME2.TFX -> 14 Songs (>15s-Tracks vermutlich Level-Musik, song3/11/12
|
||||
sehr kurz (0.16-1.36s) vermutlich Jingles/Stinger, song2/13
|
||||
~10.3s). Noch nicht im Detail zugeordnet -- fuer die
|
||||
spaetere Gameplay-Musik relevant, siehe NOTES.md.
|
||||
render_wav() nimmt jetzt optional `song_index` entgegen und reicht ihn als
|
||||
5. CLI-Arg an render_tfmx durch. Der Cache-Dateiname enthaelt den Song-Index
|
||||
NUR wenn er nicht der Default (0/None) ist, damit bereits vorhandene Caches
|
||||
fuer Song 0 (z.B. TITEL.wav) gueltig bleiben.
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
@@ -35,30 +52,30 @@ def _ensure_binary():
|
||||
subprocess.run(['bash', os.path.join(_PLAYER_DIR, 'build.sh')], check=True)
|
||||
|
||||
|
||||
def render_wav(container, name, seconds=180, force=False):
|
||||
def render_wav(container, name, seconds=180, force=False, song_index=None):
|
||||
"""container: kellogg_formats.DATContainer; name: z.B. 'TITEL' (ohne Extension).
|
||||
Gibt den Pfad zur gerenderten (gecachten) WAV-Datei zurueck.
|
||||
song_index: optional -- welcher der ggf. mehreren in <name>.TFX gebuendelten
|
||||
Songs gerendert werden soll (siehe Modul-Docstring). None/0 = Default-
|
||||
Verhalten wie bisher (erster/einziger Song). Gibt den Pfad zur gerenderten
|
||||
(gecachten) WAV-Datei zurueck.
|
||||
|
||||
WICHTIG (Fix 22.07.2026 abends): `seconds` ist nur noch eine Sicherheits-
|
||||
OBERGRENZE, keine Ziel-Laenge! render_tfmx erkennt das natuerliche Song-
|
||||
Ende/den Loop-Punkt selbst (tfmx.h real_song_end) und schneidet dort ab --
|
||||
z.B. ist TITEL tatsaechlich ~109s lang, ONGAME2 ~123s, TITEL2 (der Rauser-
|
||||
Sting) nur ~2s. Vorher wurde hart bei `seconds` abgeschnitten, was TITEL
|
||||
(mit frueher genutzten 60s) VOR dem echten Songende kappte -- das war der
|
||||
Bug hinter "Musik ist zu kurz, da fehlt der Rest". Der Default hier (180s)
|
||||
ist bewusst grosszuegig ueber die laengsten bekannten Stuecke hinaus
|
||||
gewaehlt, damit nichts abgeschnitten wird; im Normalfall stoppt render_tfmx
|
||||
laengst vorher von selbst.
|
||||
z.B. ist TITEL (song 0) tatsaechlich ~109s lang, ONGAME2 (song 0) ~123s,
|
||||
TITEL2 song0 (Rauser-Sting) nur ~2.04s. Vorher wurde hart bei `seconds`
|
||||
abgeschnitten. Der Default hier (180s) ist bewusst grosszuegig ueber die
|
||||
laengsten bekannten Stuecke hinaus gewaehlt, damit nichts abgeschnitten
|
||||
wird; im Normalfall stoppt render_tfmx laengst vorher von selbst.
|
||||
|
||||
ACHTUNG Cache: der Dateiname ist rein `<name>.wav` OHNE den seconds-Wert.
|
||||
Wird `seconds` also mal kleiner gewaehlt als die echte Songlaenge (z.B.
|
||||
fuer einen bewusst kurzen Teaser), MUSS `force=True` gesetzt werden, sonst
|
||||
liefert diese Funktion den ggf. laenger/kuerzer gerenderten alten Cache-
|
||||
Treffer zurueck, egal was `seconds` diesmal sagt -- genau das hat den
|
||||
urspruenglichen Bug mit ausgeloest (ein alter 8s-Debug-Render von TITEL
|
||||
blieb liegen und wurde stillschweigend weiterverwendet)."""
|
||||
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."""
|
||||
os.makedirs(_CACHE_DIR, exist_ok=True)
|
||||
out_path = os.path.join(_CACHE_DIR, f'{name}.wav')
|
||||
suffix = f'_song{song_index}' if song_index else ''
|
||||
out_path = os.path.join(_CACHE_DIR, f'{name}{suffix}.wav')
|
||||
if os.path.exists(out_path) and not force:
|
||||
return out_path
|
||||
_ensure_binary()
|
||||
@@ -72,22 +89,23 @@ def render_wav(container, name, seconds=180, force=False):
|
||||
with open(sam_path, 'wb') as f:
|
||||
f.write(sam_data)
|
||||
|
||||
result = subprocess.run(
|
||||
[_BINARY, tfx_path, sam_path, out_path, str(seconds)],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
cmd = [_BINARY, tfx_path, sam_path, out_path, str(seconds)]
|
||||
if song_index:
|
||||
cmd.append(str(song_index))
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f'render_tfmx failed for {name}: {result.stderr}')
|
||||
raise RuntimeError(f'render_tfmx failed for {name} (song_index={song_index}): {result.stderr}')
|
||||
return out_path
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Kleiner CLI-Test: python3 tfmx_audio.py <DAT_PATH> <NAME> [seconds]
|
||||
# Kleiner CLI-Test: python3 tfmx_audio.py <DAT_PATH> <NAME> [seconds] [song_index]
|
||||
sys.path.insert(0, _HERE)
|
||||
from kellogg_formats import DATContainer
|
||||
dat_path = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
seconds = int(sys.argv[3]) if len(sys.argv) > 3 else 20
|
||||
song_index = int(sys.argv[4]) if len(sys.argv) > 4 else None
|
||||
c = DATContainer(dat_path)
|
||||
path = render_wav(c, name, seconds, force=True)
|
||||
path = render_wav(c, name, seconds, force=True, song_index=song_index)
|
||||
print(f'rendered: {path}')
|
||||
|
||||
Reference in New Issue
Block a user