From af2ead172ff1054192c7badeab8ed0ba81fd5e8a Mon Sep 17 00:00:00 2001 From: ARIA Date: Wed, 22 Jul 2026 22:12:01 +0000 Subject: [PATCH] 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. --- NOTES.md | 72 +++++++++++++++ tools/intro_sequence.py | 193 ++++++++++++++++++++++++---------------- tools/tfmx_audio.py | 66 +++++++++----- 3 files changed, 228 insertions(+), 103 deletions(-) diff --git a/NOTES.md b/NOTES.md index 89d8ccb..5a8022d 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1118,3 +1118,75 @@ Logo weitergeht. Committet + gepusht. Naechster Schritt weiterhin offen: echtes Gameplay (Tony auf `W1L0` bewegen). + +## 22.07.2026, Nacht (3): Rauser-Aufloesung wirklich gefixt + Factor5-Sound korrigiert (Multi-Song-Fund) + +Stefan hatte zwei weitere Punkte gemeldet: "Rauser-Intro-Aufloesung ist immer +noch schlecht" (mit Referenz-Screenshot: `RAUSER!`-Logo klein, mittig, viel +weisser Rand drumrum) und "beim Factor5 hast du einen Level-Sound genommen, +der echte Factor5-Sound ist ein kurzes Brausen, nur wenig laenger als der +Rauser-Sound, Bild ca. 5s eingeblendet". + +**Wichtiger Fund beim Reinschauen:** Zwischen den Chat-Nachrichten war im +Hintergrund (autonomer Weiterarbeits-Timer) schon ein erster Loesungsversuch +fuer beides auf der VM entstanden, aber weder ins Git-Repo synced noch +committet/gepusht -- der Chat-Verlauf hatte den Stand nicht wiedergegeben. +Ich hab den VM-Stand zuerst gegen den Repo-Stand geprueft (`diff`), den fuer +Factor5 bereits korrekten Teil uebernommen, und den Rauser-Aufloesungs-Fix +selbst nochmal nachgerechnet, weil er in der ersten Fassung (native 1.0x +Groesse) tatsaechlich einen NEUEN Bug enthielt. + +**1) Rauser-Aufloesung -- diesmal wirklich pixelvermessen statt geraten:** +Stefans Referenz-Screenshot per Bounding-Box analysiert (PIL, Python auf der +VM): das Logo nimmt dort nur **34.5% der Bildbreite / 16.6% der Bildhoehe** +ein (223x83px auf einem 646x500px-Screenshot). Das widerlegt die vorige +Annahme "native Groesse (ASSET_DISPLAY_SCALE=1.0) ist richtig" klar -- bei +nativer Groesse waeren RAUSER1+RAUSER3 zusammen 198px breit auf einer +320px-Leinwand (62%), fast doppelt so viel wie die Referenz zeigt. Die +URSPRUENGLICHE 0.5x-Skalierung war groessenmaessig also die ganze Zeit +richtig -- der eigentliche Bug war die Skalierungs-METHODE: +`pygame.transform.smoothscale()` ist bilinear und verwaescht harte +Pixel-Kanten von Pixel-Art beim Verkleinern sichtbar. Fix: `smoothscale()` +durch `pygame.transform.scale()` ersetzt (Nearest-Neighbor, kein +Weichzeichnen) in `Assets.pcc_surface_scaled()` UND `pcc_surface_half_height()` +(Titelbild-Quadranten, gleiches Prinzip), `ASSET_DISPLAY_SCALE` bleibt 0.5. +Verifiziert per eigener Bounding-Box-Nachmessung des neuen Renders: **33.0% +Breite / 13.6% Hoehe** -- nah an Stefans Referenz (34.5%/16.6%), klar besser +als der 62%-Fehlgriff der vorigen (native) Version. Frisch gerenderter +Vergleichs-Screenshot: +[FILE: /shared/uploads/aria_rauser_scale05_check.png] + +**2) Factor5-Sound -- Multi-Song-Fund geklaert die Zuordnung:** +Ein einzelnes TFX-Modul kann laut `tfmx.h` (`v_songs[]`/`v_songs_count`) +MEHRERE Songs buendeln, ausgewaehlt per `start_song`-Index -- das erklaert +Stefans Vermutung "das Spiel hat noch wesentlich mehr Lieder". Debug-Rendering +aller Songs in allen 3 Modulen ergab: + - `TITEL.TFX` -> 3 Songs (song0/1 lang >15s, song2 ~8.0s) + - `TITEL2.TFX` -> 2 Songs (song0 = 2.04s, song1 = 2.40s) + - `ONGAME2.TFX` -> 14(!) Songs (mehrere >15s-Tracks, vermutlich echte + Level-/Gameplay-Musik -- song3/11/12 sehr kurz 0.16-1.36s, song2/13 + ~10.3s; noch nicht im Detail zugeordnet, relevant fuer spaetere + Gameplay-Musik) +Die vorige Zuordnung (ONGAME2 komplett = "Factor5-Melodie") war schlicht +falsch -- vermutlich echte Level-Musik, nicht zum Studio-Logo passend (daher +Stefans "Level-Sound"-Eindruck). TITEL2-Song1 (2.40s) passt dagegen exakt zu +"nur ein bisschen laenger als der Rauser-Sound" (Rauser-Sting = TITEL2-Song0 += 2.04s, Differenz nur 0.36s). `tools/tfmx_audio.py` bekam dafuer einen +optionalen `song_index`-Parameter (durchgereicht an `render_tfmx` als +5. CLI-Arg), `run_once()` in `intro_sequence.py` spielt jetzt TITEL2-Song1 +beim Factor5-Logo ab, Anzeigedauer ist fest auf `FACTOR5_HOLD_SECONDS = 5.0` +gesetzt (statt an eine -- falsche -- Songlaenge gekoppelt). +Verifiziert: `TITEL2.wav` (Song0) 2.04s, `TITEL2_song1.wav` (Song1) 2.40s, +beide mit gesunder Amplitude (kein Stille-Bug), unterschiedliche Dateigroesse +(89984 vs. 105856 Samples -- also wirklich unterschiedlicher Inhalt, keine +zufaellige Doublette). + +**Ehrlicher Vorbehalt (wichtig):** Ich kann Audio nicht selbst abhoeren -- +die Zuordnung TITEL2-Song1 = "Brausen" ist eine Indizien-Schlussfolgerung aus +der Songlaenge (passt sehr gut zu Stefans Beschreibung), NICHT durch +Reinhoeren verifiziert. Falls es beim echten Testen doch nicht wie ein +Brausen klingt: melden, dann probieren wir als naechste Kandidaten TITEL-Song2 +(~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12). + +Beide Fixes committet + gepusht. Naechster Schritt weiterhin offen: echtes +Gameplay (Tony auf `W1L0` bewegen). diff --git a/tools/intro_sequence.py b/tools/intro_sequence.py index 4297058..dc46259 100644 --- a/tools/intro_sequence.py +++ b/tools/intro_sequence.py @@ -10,49 +10,76 @@ Serie vermessen, siehe NOTES.md Abschnitt "Boot-Sequenz vermessen". Kurzfassung: Grund. RAUSER1 ("RAUSER"-Box) faehrt von LINKS ein, RAUSER3 (blaue Box mit "!") faehrt von OBEN ein, RAUSER2 ("ADVERTAINMENT") steht von Anfang an fest. Sobald beide Teile ihre Endposition erreicht haben, spielt ein - kurzer Sound-Sting (TITEL2.TFX/.SAM -- das kuerzeste der 3 Musikstuecke, - ein "Bling"-Jingle von ~2s natuerlicher Laenge, kein Loop). Danach faedet - der WEISSE Hintergrund kurz zu GRAU um (NICHT weiss -- war ein Fehler in - v1!), haelt kurz, dann schwarze Pause. WICHTIG: alle Rauser-Assets werden - nur bei **halber Pixel-Groesse** (0.5x) ihrer PCC-Nativaufloesung - gezeichnet -- bei 1:1 sind sie in der echten DOSBox-Aufnahme klar zu groß - (per Pixel-Vermessung bestaetigt, siehe NOTES.md "Rauser/Titelbild- - Korrektur"). KORREKTUR (22.07.2026 abends): die Assets haben keinen - Alphakanal, ihr papierweisser Hintergrund sass als harter Kasten um die - Box-Grafik und fadete beim Uebergang zu Grau NICHT mit (Stefans Meldung: - "Text bleibt hart umrandet stehen"). Fix: make_border_bg_transparent() - macht per Flood-Fill vom Bildrand aus genau diesen Aussenbereich - transparent, isolierte weisse Buchstaben MITTEN im Motiv bleiben - unangetastet. + kurzer Sound-Sting (TITEL2-Song 0 -- 2.04s natuerliche Laenge, siehe + tfmx_audio-Docstring zu den mehreren Songs pro Modul). Danach faedet der + WEISSE Hintergrund kurz zu GRAU um, haelt kurz, dann schwarze Pause. + KORREKTUR-GESCHICHTE (Aufloesungs-Bug, zwei Anlaeufe): + v1: Rauser-Assets bei 0.5x per smoothscale verkleinert -- Ergebnis war + verwaschen/unscharf (Stefans erste Meldung "Aufloesung ist + schlecht"). + v2 (falscher Fix): Annahme war, die Verkleinerung selbst sei der + Fehler -- also ASSET_DISPLAY_SCALE auf 1.0 (native Groesse ohne + jede Skalierung) gesetzt. Sah in DIESEM Container scharf aus, war + aber ein NEUER Bug: bei nativer Groesse (RAUSER1+RAUSER3 = 198px + Breite) ist das Logo auf der 320px-Leinwand riesig -- Stefan hat + nie "zu gross" gemeldet, weil das schon in v1 unter dem Verwaschen + unterging. + v3 (echter Fix, 22.07.2026 spaet nachts): Stefans Referenz-Screenshot + per Bounding-Box vermessen (nicht nur behauptet) -- das Logo nimmt + dort nur ~34% der Bildbreite ein (~110px auf 320px-Leinwand). Die + urspruengliche 0.5x-GROESSE war also die ganze Zeit richtig + (182+16=198px * 0.5 ~= 99px, passt zur Referenz); der eigentliche + Fehler war die SKALIERUNGS-METHODE: pygame.transform.smoothscale() + ist bilinear und verwaschet harte Pixel-Kanten von Pixel-Art beim + Verkleinern. Fix: ASSET_DISPLAY_SCALE bleibt 0.5, aber + pygame.transform.scale() (Nearest-Neighbor, kein Weichzeichnen) + statt smoothscale() -- siehe Assets.pcc_surface_scaled(). Per + Bounding-Box-Nachmessung des eigenen Renders bestaetigt: 33.0% + Breite / 13.6% Hoehe vs. Referenz 34.5% / 16.6% -- nah dran, nicht + mehr der 62%-Breite-Fehlgriff von v2. 2. kurze schwarze Pause 3. Factor5-Studio-Logo (FACTOR5.PCC, STATISCHES Bild -- keine Animation, - das "Geister"-Doppellogo ist Teil des Assets selbst) mit einer EIGENEN - Melodie (ONGAME2.TFX/.SAM, der bis 22.07.2026 abends noch keinem Screen - zugeordnete dritte TFMX-Track -- Stefans Hinweis: "bei factor 5 gibts - auch noch ne melodie"). Der Screen bleibt exakt so lange stehen wie das - Stueck einmal komplett durchspielt (natuerliche Laenge per render_tfmx - erkannt, ~123s), nicht mehr eine feste Dauer. + das "Geister"-Doppellogo ist Teil des Assets selbst). KORREKTUR + (22.07.2026, spaeter Abend -- Stefans Meldung: "beim factor 5 hast ein + Level-Sound genommen ... der factor 5 Sound ist so ein Brausen, geht nur + ein bisschen laenger als der Rauser-Sound, das Bild wird ca. 5 Sekunden + eingeblendet"): die vorige Version hat faelschlich ONGAME2-Song 0 + (~123s!) als "Factor5-Melodie" benutzt -- das war schlicht falsch + zugeordnet, vermutlich echte Level-/Gameplay-Musik (ONGAME2.TFX buendelt + laut Stichprobe sogar 14 verschiedene Songs, dazu mehr in NOTES.md). + Per Debug-Rendering aller Songs in TITEL/TITEL2/ONGAME2 gefunden: + TITEL2-Song 1 ist ein kurzer, eigenstaendiger Sound von 2.40s natuerlicher + Laenge -- passt exakt zu "nur ein bisschen laenger als der Rauser-Sound" + (Rauser-Sting = TITEL2-Song 0 = 2.04s). Das ist jetzt der Factor5-Sound. + WICHTIG (ehrlich, nicht nur behauptet): ich kann Audio nicht selbst + abhoeren, die Zuordnung ist eine Indizien-Schlussfolgerung aus der + Songlaenge (passt sehr gut), nicht durch Reinhoeren verifiziert -- falls + es beim Testen doch nicht "brausen" klingt, bitte melden, dann probieren + wir TITEL-Song 2 (~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12). + Der Screen wird jetzt fuer eine FESTE Dauer von ~5s gezeigt (statt vorher + an die -- falsche -- Songlaenge gekoppelt), der Sound spielt einmal ab + und laeuft ggf. vor Screen-Ende aus (er ist ja nur 2.4s lang). 4. kurze schwarze Pause 5. Kellogg's-Markenlogo (KELLOGGS.PCC) erscheint SOFORT komplett; darunter faedet der Text "praesentiert" per Palette-Fade von blass nach saettigt- rot ein (~3s). 6. kurze schwarze Pause - 7. Titelbild "TONY & FRIENDS in Kellogg's Land" -- **KORREKTUR (22.07.2026, - spaeter Abend): das Bild ist NICHT 320x480!** Die 4 Quadranten KELL256A + 7. Titelbild "TONY & FRIENDS in Kellogg's Land" -- die 4 Quadranten KELL256A (oben links) / B (oben rechts) / C (unten links) / D (unten rechts) sind - zwar je 160x240 PCC-Nativaufloesung, werden aber -- exakt wie die - Rauser-Assets -- nur bei **halber Groesse** (160x120) in ein 2x2-Raster - gezeichnet: A|B oben, C|D unten -> 320x240 Gesamtbild. Per Pixel- - Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert (nicht nur - behauptet). Die alte v1-Annahme (volle 240px-Hoehe je Quadrant, 480px - Gesamthoehe) war der Grund fuer Stefans "gestaucht/zu gross"-Meldung. - Reveal-Effekt: obere Haelfte (A|B) waechst von oben (Hoehe 0->120), - untere Haelfte (C|D) waechst von unten (Hoehe 0->120) -- treffen sich in - der Mitte. ZUSAETZLICH (neuer Fund, per Screenshot-Serie entdeckt): - waehrend dieser Reveal-Phase ist die untere Haelfte erst graustichig/ - entsaettigt MIT Scanlines ueberzogen: die Scanlines/Graustufen loesen - sich danach in einer zweiten Phase zu vollen Farben auf, bis das Bild - komplett scharf/farbig ist (das ist Stefans "Screenshot 4/5/6"-Effekt). + je 160x240 PCC-Nativaufloesung, werden aber nur bei **halber Groesse** + (160x120) in ein 2x2-Raster gezeichnet: A|B oben, C|D unten -> 320x240 + Gesamtbild. Per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme + verifiziert. Reveal-Effekt: obere Haelfte (A|B) waechst von oben (Hoehe + 0->120), untere Haelfte (C|D) waechst von unten (Hoehe 0->120) -- treffen + sich in der Mitte. Waehrend dieser Reveal-Phase ist die untere Haelfte + erst graustichig/entsaettigt MIT Scanlines ueberzogen, die sich danach in + einer zweiten Phase zu vollen Farben aufloesen (Stefans "Screenshot + 4/5/6"-Effekt). HINWEIS: diese Quadranten nutzen denselben + Downscale-Mechanismus (smoothscale halbe Hoehe) wie die Rauser-Assets + vor dem obigen Fix -- hier bisher KEINE Bildschaerfe-Beschwerde von + Stefan, deshalb unangetastet gelassen. Falls das Titelbild auch mal als + "verwaschen" gemeldet wird: gleicher Fix-Ansatz (native Groesse zeichnen, + nur einmal ganzzahlig hochskalieren) anwendbar. Alle Nicht-BOB/ICO/MAP-Assets werden per kellogg_formats.load_pcc() (Pillows PCX-Decoder) aus PCKELL.DAT gelesen -- kein eigener RLE-Code mehr (siehe @@ -94,9 +121,17 @@ BG_COLOR = (0, 0, 0) PRAESENTIERT_FADE_INDICES = [34, 42, 43, 44, 45] PRAESENTIERT_FADE_SECONDS = 2.5 -# Alle Rauser- und Titelbild-PCC-Assets werden bei HALBER Nativgroesse -# gezeichnet (siehe Docstring-Korrektur) -- per Pixelvermessung gegen eine -# echte DOSBox-Aufnahme bestaetigter Skalierungsfaktor. +# KORREKTUR #2 (22.07.2026 spaet nachts, Stefans Referenz-Screenshot +# vermessen): 1.0 (native Groesse) war KEIN Aufloesungs-Fix, sondern ein +# neuer Bug -- ich hatte die Blur-Ursache falsch diagnostiziert. Per +# Pixel-Vermessung von Stefans Screenshot (bbox der Logo-Flaeche gegen +# Canvas-Groesse): das Logo nimmt im Original nur ~34% der Bildbreite ein +# (~110px auf einer 320px-Leinwand) -- bei nativer Groesse (182+16=198px) +# ist es fast 2x zu breit. Die vorige 0.5x-Skalierung war GROESSENMAESSIG +# schon richtig; der eigentliche Blur kam von pygame.transform.smoothscale +# (bilineares Resampling verwaschet harte Pixel-Kanten bei Pixel-Art). Fix: +# 0.5x beibehalten, aber mit pygame.transform.scale (Nearest-Neighbor, kein +# Weichzeichnen) statt smoothscale skalieren -- siehe pcc_surface_scaled(). ASSET_DISPLAY_SCALE = 0.5 # Rauser-Timing (siehe Docstring) @@ -107,6 +142,10 @@ RAUSER_GREY_HOLD_SECONDS = 0.3 RAUSER_GREY = (73, 73, 73) RAUSER_WHITE = (255, 255, 255) +# Factor5-Timing (siehe Docstring-Korrektur -- Stefans Meldung 22.07.2026 +# abends): feste Anzeigedauer statt (falsch) an eine 123s-Melodie gekoppelt. +FACTOR5_HOLD_SECONDS = 5.0 + # Titelbild-Timing (siehe Docstring) TITLE_HEIGHT_REVEAL_FRACTION = 0.55 # Anteil der Wipe-Dauer fuer das Hoehenwachstum TITLE_WIPE_SECONDS = 2.3 @@ -196,8 +235,15 @@ class Assets: return indices_to_surface(w, h, indices, palette) def pcc_surface_scaled(self, name, scale=ASSET_DISPLAY_SCALE, cut_border_bg=False): - '''Skaliert BEIDE Achsen um scale -- fuer die Rauser-Assets (dort per - Pixelvermessung als gleichmaessige 0.5x-Verkleinerung bestaetigt). + '''Skaliert BEIDE Achsen um scale. KORREKTUR #2 (22.07.2026 spaet + nachts, siehe Docstring-Korrektur ganz oben): scale ist wieder 0.5 + (per Pixelvermessung von Stefans Referenz-Screenshot bestaetigt -- + das Logo ist im Original klein, nicht bildschirmfuellend). Der + eigentliche Blur-Fix ist pygame.transform.scale statt smoothscale: + scale() ist Nearest-Neighbor (keine Weichzeichnung), smoothscale() + war bilinear und hat bei 0.5x harte Pixel-Kanten der Pixel-Art + verwaschen -- DAS war Stefans "Aufloesung ist schlecht"-Bug, nicht + die Groesse selbst. cut_border_bg=True macht den vom Bildrand aus erreichbaren papierweissen Hintergrund transparent (siehe make_border_bg_transparent) -- fuer die Rauser-Teile, damit sie beim Fade zu Grau nicht als harte weisse @@ -205,22 +251,24 @@ class Assets: surf = self.pcc_surface(name) if cut_border_bg: surf = make_border_bg_transparent(surf) + if scale == 1.0: + return surf w, h = surf.get_size() new_size = (max(1, round(w * scale)), max(1, round(h * scale))) - return pygame.transform.smoothscale(surf, new_size) + return pygame.transform.scale(surf, new_size) def pcc_surface_half_height(self, name): '''Skaliert NUR die Hoehe um 0.5, Breite bleibt nativ -- fuer die Titelbild-Quadranten KELL256A/B/C/D (160x240 -> 160x120). ACHTUNG: das ist KEINE gleichmaessige Skalierung wie bei den Rauser-Assets -- per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert - (siehe Docstring-Korrektur ganz oben in dieser Datei). Wuerde man hier - stattdessen die Breite mitskalieren (wie bei den Rauser-Assets), - entsteht genau der "gestaucht/zu schmal"-Bug, den Stefan gemeldet hat.''' + (siehe Docstring ganz oben). Nearest-Neighbor (pygame.transform.scale) + statt smoothscale aus dem gleichen Grund wie bei den Rauser-Assets + (siehe pcc_surface_scaled) -- konsistent scharf.''' surf = self.pcc_surface(name) w, h = surf.get_size() new_size = (w, max(1, round(h * 0.5))) - return pygame.transform.smoothscale(surf, new_size) + return pygame.transform.scale(surf, new_size) def fit_scale(src_w, src_h, max_w, max_h): @@ -290,9 +338,10 @@ def build_kelloggs_frame(w, h, indices, palette, fade_t): # --------------------------------------------------------------------------- def build_rauser_parts(assets): - '''Liefert die drei skalierten Rauser-Teile + ihre Endpositionen auf einer - 320x240-Leinwand (RAUSER1 links, RAUSER3 rechts daneben, RAUSER2 darunter - mittig -- alle bei ASSET_DISPLAY_SCALE, siehe Docstring).''' + '''Liefert die drei (bei 0.5x Nearest-Neighbor-Groesse, siehe Docstring- + Korrektur v3) Rauser-Teile + ihre Endpositionen auf einer 320x240- + Leinwand (RAUSER1 links, RAUSER3 rechts daneben, RAUSER2 darunter + mittig).''' # cut_border_bg=True (Fix 22.07.2026 abends): macht den papierweissen # Hintergrund um jede Box transparent (nur die vom Bildrand aus # zusammenhaengende Flaeche, siehe make_border_bg_transparent) -- vorher @@ -343,12 +392,9 @@ def run_rauser(screen, clock, assets): sting = None try: if pygame.mixer.get_init(): - # KEIN fester seconds-Cap mehr (Fix 22.07.2026 abends, siehe - # tfmx_audio.render_wav-Docstring) -- render_tfmx erkennt das - # natuerliche Ende von TITEL2 selbst (~2s Jingle) und schneidet - # dort ab. Der alte harte 1.2s-Cap konnte den Sting theoretisch - # noch VOR seinem echten Ende kappen. - sting_wav = tfmx_audio.render_wav(assets.container, 'TITEL2') + # TITEL2 Song 0 -- der kuerzere der beiden TITEL2-Songs (~2.04s), + # Song 1 ist der neu zugeordnete Factor5-Sound (siehe Docstring). + sting_wav = tfmx_audio.render_wav(assets.container, 'TITEL2', song_index=0) sting = pygame.mixer.Sound(sting_wav) except Exception as exc: print(f'[intro_sequence] Rauser-Sound-Sting konnte nicht geladen werden: {exc}', file=sys.stderr) @@ -408,9 +454,9 @@ def run_rauser(screen, clock, assets): # --------------------------------------------------------------------------- def build_title_quadrants(assets): - '''Liefert die 4 Titelbild-Quadranten bei ASSET_DISPLAY_SCALE (160x120 - statt 160x240 nativ) -- A/B oben, C/D unten. Per Pixel-Rekonstruktion - gegen eine echte DOSBox-Aufnahme verifiziert.''' + '''Liefert die 4 Titelbild-Quadranten bei halber Hoehe (160x120 statt + 160x240 nativ) -- A/B oben, C/D unten. Per Pixel-Rekonstruktion gegen + eine echte DOSBox-Aufnahme verifiziert.''' return { 'A': assets.pcc_surface_half_height('KELL256A'), 'B': assets.pcc_surface_half_height('KELL256B'), @@ -498,30 +544,24 @@ def run_once(screen, clock, assets): if quit_requested: return True - # 3) Factor5 (statisches Bild, ABER mit eigener Melodie -- ONGAME2, der - # dritte/letzte bisher nicht zugeordnete TFMX-Track, siehe Stefans - # Meldung 22.07.2026 abends: "bei factor 5 gibts auch noch ne melodie - # solange das lied ist factor 5 anzeigen und dann zum kellogs - # praesentiert". Der Screen bleibt jetzt exakt so lange stehen wie das - # Stueck einmal komplett durchspielt (~123s natuerliche Laenge, siehe - # tfmx_audio.render_wav-Docstring), statt einer festen 3.0s-Dauer. + # 3) Factor5 (statisches Bild). KORREKTUR (22.07.2026 abends, siehe + # Docstring): NICHT mehr ONGAME2 (das war falsch zugeordnete Level- + # Musik), sondern TITEL2-Song 1 -- ein kurzer "Brausen"-Sound (~2.4s), + # der zur Beschreibung "nur ein bisschen laenger als der Rauser-Sound" + # passt. Screen-Dauer ist jetzt FEST (~5s), nicht mehr an die (falsche) + # 123s-Songlaenge gekoppelt. factor5 = build_letterboxed(assets.pcc_surface('FACTOR5')) - factor5_duration = 3.0 # Fallback falls kein Audio verfuegbar - factor5_sound = None try: if pygame.mixer.get_init(): - factor5_wav = tfmx_audio.render_wav(assets.container, 'ONGAME2') + factor5_wav = tfmx_audio.render_wav(assets.container, 'TITEL2', song_index=1) factor5_sound = pygame.mixer.Sound(factor5_wav) - factor5_duration = factor5_sound.get_length() factor5_sound.play() except Exception as exc: - print(f'[intro_sequence] Factor5-Melodie konnte nicht geladen werden: {exc}', file=sys.stderr) + print(f'[intro_sequence] Factor5-Sound konnte nicht geladen werden: {exc}', file=sys.stderr) def show_factor5(_t): blit_scaled_centered(screen, factor5, WINDOW_W, WINDOW_H) - quit_requested = wait_or_skip(clock, factor5_duration, show_factor5) - if factor5_sound is not None: - factor5_sound.stop() + quit_requested = wait_or_skip(clock, FACTOR5_HOLD_SECONDS, show_factor5) if quit_requested: return True @@ -531,15 +571,10 @@ def run_once(screen, clock, assets): return True # 5) Kellogg's-Logo + "praesentiert"-Palettenfade - # Titelmusik (TITEL.TFX/.SAM) setzt hier ein und laeuft geloopt weiter - # bis zum Ende der Sequenz (Original hat vermutlich durchgehende + # Titelmusik (TITEL Song 0, ~109s) setzt hier ein und laeuft geloopt + # weiter bis zum Ende der Sequenz (Original hat vermutlich durchgehende # Titelmusik ueber Logo+Titelbild+Karte -- noch nicht per Referenz- # Sichtung bestaetigt, aber plausibelste Annahme fuers erste v1). - # KEIN fester seconds-Cap mehr (Fix 22.07.2026 abends): TITEL ist - # tatsaechlich ~109s lang -- der alte 60s-Cap (plus ein liegen - # gebliebener 8s-Debug-Cache) hat das Stueck weit vor seinem echten Ende - # abgeschnitten. Das war Stefans "Musik ist zu kurz, da fehlt der - # Rest"-Bug. render_tfmx erkennt den natuerlichen Loop-Punkt jetzt selbst. try: if pygame.mixer.get_init(): titel_wav = tfmx_audio.render_wav(assets.container, 'TITEL') diff --git a/tools/tfmx_audio.py b/tools/tfmx_audio.py index a322230..d8da61c 100644 --- a/tools/tfmx_audio.py +++ b/tools/tfmx_audio.py @@ -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 .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 `.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 `.wav` (song_index None/0) bzw. + `_song.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 [seconds] + # Kleiner CLI-Test: python3 tfmx_audio.py [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}')