# Heli-Audio — Konzept V2 Stand: 2026-05-31 · Autor: Atlas · Status: Diskussion mit Thomas ## Thomas-Direktiven (2026-05-31) > Wir brauchen ein geschicktes Konzept, dass nicht irgendwo ewige Stille ist. Gleichzeitig gibt es zentral wichtige Ansagen/Geräusche (Unfall, Warnung, Missionsinfo) und weniger wichtige (geografische Info) — die aber natürlich auch gespielt werden sollen, wenn möglich. > > Ducking: ja. Crossfade weglassen. Geo-Audios werden von Notfall-Zwischenrufen überlagert bzw. gestoppt. Alles andere (Environmental, Reverb, Spatial): machen. ## Audio-Schichten (Architektur-Bild) ``` ┌───────────────────────────────────────────┐ SCHICHT 1 │ AMBIENT (loop, Volume 0.1) │ immer da (Hintergrund) │ • Wind/Wasser/Verkehr je Szenario │ nie still │ • Heli-Rotor (nur Flight-Phase) │ │ • Musik (nur Mission/Results-Phase) │ └───────────────────────────────────────────┘ ▲ Ducking: 0.10 → 0.04 wenn Stimme spielt ┌───────────────────────────────────────────┐ SCHICHT 2 │ SPRACH-PIPE (seriell, prioritätssortiert)│ (Stimmen) │ │ │ CRITICAL (1) — unterbricht alles │ │ • Crash-Warnung, Boden-Alarm │ │ • Tower-Notfunk, Pilot-Übernahme │ │ • Stop!-Ansagen │ │ │ │ HIGH (2) — Standard-Funk-Pipe │ │ • Tower-Wegpunkt-Anweisungen │ │ • Mission-Briefing │ │ • Pilot-Antworten │ │ │ │ MEDIUM (3) — wenn Pipe leer ist │ │ • Geografische Hintergrund-Infos │ │ • Stimmung („Sicht klar", „Wind ruhig")│ │ • Stadt-/Berg-Fakten beim Überflug │ │ │ │ FILLER (4) — Anti-Stille │ │ • Wenn Pipe >12 s leer: Filler-Item │ │ • Cockpit-Geräusche, Pilot-Murmeln │ └───────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────┐ SCHICHT 3 │ FEEDBACK-SOUNDS (kurz, ohne Pipe) │ sofort (UI/Feedback) │ • Klick-OK, Klick-Fehler │ parallel │ • Wegpunkt-Treffer-Glocke │ zur Pipe └───────────────────────────────────────────┘ ``` ## Prioritäten-Logik Aktuell hat die Pipe nur: **seriell + 10s-Gap + `priority:'now'`-Bypass**. Wir brauchen ein **vollwertiges Prioritäten-System** mit 4 Levels. ### Pipe-Verhalten pro Priorität | Level | Verhalten beim Einreihen | Beispiel-Audios | |---|---|---| | **1 CRITICAL** | Laufendes Audio per 120ms-Fade abbrechen, gesamte Queue leeren, sofort spielen | „Achtung! Boden zu nah!", „Pilot übernimmt — wir landen jetzt!", „Stop! Falsche Richtung!" | | **2 HIGH** | Hinten in Queue einreihen, nach Gap (3 s) spielen. Wird von CRITICAL unterbrochen. | Tower-Wegpunkt-Anweisungen, Briefing-Hinweise, Pilot-Antworten | | **3 MEDIUM** | Nur einreihen wenn keine HIGH-Items in Queue. Wird beim Eintreffen eines HIGH-Items aus der Queue entfernt (verlieren ihre Chance), beim Eintreffen eines CRITICAL auch sofort gestoppt wenn laufend. | Geo-Fakten („Wir überfliegen Schwaz, drittgrößte Stadt Tirols"), Stadt-Kommentare | | **4 FILLER** | Wird automatisch eingereiht wenn Pipe >12 s leer war. Wird von HIGH/CRITICAL überschrieben. | Cockpit-Klick, Pilot-Räuspern, „Sicht ist klar", „Höhe stabil" | ### Code-Konzept (Pseudo-JS) ```js // Ersetzt die aktuelle flightAudioQueue durch priority-Queue var pipe = { current: null, // {src, priority, _fadeGain} queue: [], // sorted by priority lastSpokenMs: 0, // für FILLER-Trigger gapMs: { 1: 0, 2: 3000, 3: 5000, 4: 8000 } }; function enqueueSpeech(path, priority) { // 1) CRITICAL: alles wegfegen, sofort spielen if (priority === 1) { abortCurrentWithFade(120); pipe.queue.length = 0; pipe.queue.push({path, priority}); pump(); return; } // 2) HIGH: einreihen, MEDIUM-Items davor aus Queue entfernen if (priority === 2) { pipe.queue = pipe.queue.filter(it => it.priority <= 2); pipe.queue.push({path, priority}); pump(); return; } // 3) MEDIUM: nur einreihen wenn keine HIGH in Queue ist if (priority === 3) { if (pipe.queue.some(it => it.priority <= 2)) return; pipe.queue.push({path, priority}); pump(); return; } // 4) FILLER: separate Strategie (siehe unten) } function pump() { if (pipe.current) return; if (!pipe.queue.length) return; var now = performance.now(); // Gap nur ANWENDEN, wenn dasselbe oder höhere Level wartet var next = pipe.queue[0]; var requiredGap = pipe.gapMs[next.priority]; if (now - pipe.lastSpokenMs < requiredGap) { setTimeout(pump, requiredGap - (now - pipe.lastSpokenMs)); return; } var item = pipe.queue.shift(); playWithDucking(item).then(() => { pipe.current = null; pipe.lastSpokenMs = performance.now(); pump(); }); } // Anti-Stille: alle 2s prüfen, ob FILLER nötig setInterval(() => { if (pipe.current || pipe.queue.length) return; if (performance.now() - pipe.lastSpokenMs < 12000) return; enqueueFiller(); // wählt Filler je nach Phase + Szenario }, 2000); ``` ## Ducking Wenn Stimme spielt → **Rotor-Lautstärke von 0.30 auf 0.08 fadrenken** (300 ms attack), nach Stimmenende **3 s linear zurück auf 0.30** (release). Gleiches für Hintergrund-Musik (von 0.20 auf 0.05). Environmental-Loops bleiben unverändert (sie sind schon leise genug). Code-Skizze: ```js function duckAmbient(on) { var t = audioCtx.currentTime; var targetRotor = on ? 0.08 : 0.30; var targetMusic = on ? 0.05 : 0.20; var rampSec = on ? 0.3 : 3.0; rotorGain.gain.cancelScheduledValues(t); rotorGain.gain.linearRampToValueAtTime(targetRotor, t + rampSec); musicGain.gain.cancelScheduledValues(t); musicGain.gain.linearRampToValueAtTime(targetMusic, t + rampSec); } ``` ## „Keine ewige Stille" — FILLER-Strategie Pool von **15–20 sehr kurzen Filler-Audios** (1–3 s), die je nach Phase + Szenario gewählt werden: | Phase | Filler-Pool | |---|---| | **plan** (Routenplanung) | „Hmm…", „Schauen wir mal", „Lass mich nachdenken", Cockpit-Schalter-Klick | | **flight** Berg | Pilot: „Wind ruhig", „Sicht klar", Höhenmesser-Beep, „Höhe stabil" | | **flight** Wasser | Pilot: „Wellen ruhig", „Patient in Sicht", Wasser-Plätschern | | **flight** Autobahn | Pilot: „Funk-Check", „Verkehr läuft" | | **landing** | Pilot: „Sinkrate gut", „Ein Meter", „Last steady" | Plus: **Cockpit-Ambient-Loops** auf Schicht 1 (siehe oben) — die laufen IMMER, also ist nie wirklich Stille. ## Environmental-Loops (Schicht 1, neu) Pro Szenario ein 30–60 s-Loop, Volume 0.10 (wird bei Stimme auf 0.04 geduckt): | Szenario | Loop | Beschreibung | |---|---|---| | `basis` (Standard) | `amb_general.mp3` | leises Cockpit-Brummen + Wind | | `wasser` | `amb_water.mp3` | Wellen, Möwen, Wind | | `berg-fels` | `amb_mountain.mp3` | Wind, leichter Hall | | `berg-schnee` | `amb_snow.mp3` | Sturm-Säuseln, Schnee | | `autobahn` | `amb_highway.mp3` | Verkehr, Sirenen entfernt | | `stadt` | `amb_urban.mp3` | Stadt-Rauschen, Hupen | | `schlucht` | `amb_canyon.mp3` | Echo-Wind, Wasser | | `tal-dorf` | `amb_village.mp3` | Vogelzwitschern, Kirchglocke selten | → **8 Loops**. Beschaffung: entweder selbst aufnehmen (Wind im Garten, Wasser im Bad…), oder Lizenz-frei (Freesound.org, BBC Sound Effects Library — frei für Bildung). ## Reverb für ausgewählte Missionen Mit einem `ConvolverNode` und einem kurzen Impulse-Response können wir den Funk-Klang räumlich variieren: | Mission-Typ | Reverb-IR | Effekt | |---|---|---| | Schlucht (M6 Canyoning Mellau) | `ir_canyon_short.wav` (0.8 s) | Funk klingt hallend, eng | | Berg-Hochalpin (M19, M20, M24 Glockner/Dachstein) | `ir_mountain.wav` (1.2 s) | weit, kalt | | Tunnel/Autobahn-Tunnel | `ir_tunnel.wav` (1.5 s) | dröhnend | | Standard (Stadt, Wasser, Wald) | kein Reverb | trocken, klar | → **3 Reverb-IRs**. Beschaffung: kostenlose IR-Libraries (z.B. https://www.openair.hosted.york.ac.uk/). ## Spatial Stereo (kleines Feature) Sprecher-Position als Stereo-Pan: - **Tower-Lotsin**: pan -0.4 (links — sitzt im Tower, „neben" dem Heli) - **Pilot:in**: pan +0.3 (rechts — sitzt rechts vom Schüler im Cockpit) - **Sani / Flugretter**: pan 0 (zentriert, hinten im Cockpit) - **Briefing-Offizierin**: pan 0 (vor dem Bildschirm, direkt) Subtil (Pan ±0.4 max), nicht aufdringlich. ## Geo-Audios — wann triggern? Aktueller Code: Geo-Audios (`r_mission_geo_*`, `r_wp_geo_*`) sind in der Standard-Pipe → konkurrieren mit Tower-Anweisungen → 10s-Gap verzögert alles. **Neu** (Prioritäten): - `r_mission_geo_*` (am Kartenflug-Beginn): **MEDIUM** — spielt nur wenn keine Tower-Anweisung wartet - `r_wp_geo_*` (pro Wegpunkt): **MEDIUM** — wird vom nächsten Tower-Funk abgebrochen - `r_tower_*` (Wegpunkt-Anweisungen): **HIGH** - `r_caution_*` (Notfall-Warnung): **CRITICAL** - `r_pilot_takeover`: **CRITICAL** Wenn der Schüler in der Plan-Phase Wegpunkte klickt und der Tower antworten muss („Korrekt, weiter über X"), bricht das die laufende Geo-Info ab. Bei richtigem Tempo bekommt er aber so viel Geo wie möglich. ## Übersicht — was wir bauen | # | Komponente | Aufwand | Wirkung | |---|---|---|---| | 1 | **Prioritäten-Pipe** (4 Levels statt eines) | mittel | Kern der ganzen Logik | | 2 | **Ducking** (Rotor + Musik bei Stimme leiser) | klein | Verständlichkeit massiv besser | | 3 | **FILLER-Strategie** (Anti-Stille mit Pool) | mittel | Keine toten Phasen mehr | | 4 | **Environmental-Loops** (8 Loops, 1 pro Szenario) | mittel (Beschaffung + Wiring) | Stimmung, kontinuierlicher Klangteppich | | 5 | **Audio-Texte umstellen** auf Prioritäten-API | mittel | Skripte sortieren | | 6 | **Reverb für 3 Mission-Typen** | klein (IR-Files + ConvolverNode) | Räumlichkeit | | 7 | **Spatial Stereo** | klein | Goldener Schliff | Reihenfolge bauen: **1 → 2 → 3 → 5 → 4 → 6 → 7**. ## Beziehung zu Heli-Kennzeichen-Konzept Audio-Texte (8 JSON-Dateien, 366 Stellen mit „Christophorus") werden im Zuge von Schritt 5 auf Platzhalter `{code}` umgestellt. Audio-Regenerierung dann **pro Mission gemeinsam mit Thomas** (siehe Heli-Kennzeichen-Konzept Schritt 7). ## Audio-Re-Generierung — offene Text-Korrekturen (zu erledigen wenn Audios neu gebaut werden) Die Audio-Skript-JSON-Dateien (`scripts/*-lines.json` + `scripts/audio-texts.json`) wurden bereits textlich korrigiert. Die zugehörigen MP3-Dateien sind aber noch alt und sagen die ursprünglichen Texte. **Beim nächsten Re-Generieren** müssen folgende Audios neu erstellt werden: ### Inhaltliche Korrekturen (2026-05-31) 1. **„Chefpilot" → „Chefpilotin"** (Marlene als weibliche Mentorin im Onboarding etabliert) - `r_mission_abort` + `r_mission_abort_m` (Drei Berührungen → die Chefpilotin übernimmt) - `r_nav_departure_takeover` (Chefpilotin hat übernommen) - `r_tower_handover_c1` bis `r_tower_handover_c16` (14 Tower-Audios: „Heute übernimmt die Chefpilotin") 2. **„Rotorabwind wäre Steinschlag" → „Rotorabwind kann Steinschlag verursachen"** (kausal richtiger Satz) - `r_caution_berg_fels` + `r_caution_berg_fels_m` (in M3/M6/M7/M12/M13/M21 etc. — alle berg-fels-Missionen) - `r_caution_wasser` ist unverändert sinnvoll, weil dort der Effekt deterministisch ist - **Status:** Text in JSON gefixt am 2026-05-31. Alte MP3 spielt noch den alten Wortlaut bis Re-Generierung. 3.5 **ASCII-Umlaut-Fixes in audio-texts.json** (2026-05-31, zweite Runde) - `r_nav_plan_correct`, `r_nav_plan_done`, `r_nav_seg_arrive`, `r_nav_altitude_mountain`, `r_nav_altitude_valley`, `r_nav_fuel_half`, `r_nav_route_check`, `r_nav_weather_enter`, `r_land_approach`, `r_land_rescuer_down`, `r_land_winch_down`, `r_land_rescuer_ready`, `r_tower_sea_approach_c9` - Vorher: "naechsten", "vollstaendig", "Ueberflughoehe", "moeglich", "Stroemung" etc. — alle MP3s sprechen noch das ASCII-Wort, JSON-Text aber UTF-8-korrekt - **Status:** Text in JSON gefixt am 2026-05-31. Alte MP3s warten auf Re-Generierung. 4. **Marlene als zentrale Chefpilotin überall** (Vereinheitlichung 2026-05-31, vorher 4 rotierende Profile) - Wirkt sich aus auf alle `pilot_*` und `caution_*`-Audios, die per `pilot_by_helikey` ausgewählt werden - **Alle pilot_*_m-Audios (Hannes/Stefan) werden in V1 nicht mehr abgespielt** — bleiben im System für später - Bilder + Funk-Pille zeigen ab sofort immer pilot_b_f (Marlene) statt der jeweiligen Stationspilot:in - Bei Re-Generierung: nur die weiblichen Varianten (ohne `_m`) brauchen Updates 3. **Marken-Bezeichnungen** (siehe `docs/konzept-heli-kennzeichen.md`): - „Christophorus N" → Pool-Kennzeichen oder generisches „R/N-N" (16 + viele weitere Stellen) - ggf. nochmal Audio-Workflow gemeinsam mit Thomas pro Mission ### Total beim nächsten Audio-Bau Ca. 18 Pilot- + Tower-Audios kombiniert mit Marken-Refactor ergeben ~30–40 zu re-generierende MP3s. --- ## Status & nächste Schritte - [ ] Schritt 1 (Prioritäten-Pipe) — warte auf Thomas-OK - [ ] Schritt 2 (Ducking) — warte - [ ] Schritt 3 (FILLER-Strategie) — warte - [ ] Filler-Texte definieren (Pool von 15–20 kurzen Audios) — gemeinsame Auswahl mit Thomas - [ ] Environmental-Loops beschaffen oder aufnehmen - [ ] Audio-Re-Generierung mit korrigiertem Text (siehe oben)