diff --git a/HANDBUCH.md b/HANDBUCH.md index aea57ce..96dc85a 100644 --- a/HANDBUCH.md +++ b/HANDBUCH.md @@ -71,7 +71,7 @@ Eine vollständige, minimale Vorlage mit jeder Übungsart liegt in [`tooling/fix 1. **Lehrplan entwerfen.** Schreibe zuerst die Liste der Module auf: 10–16 Module in 2–4 Tracks, vom Einstieg bis zum Profi-Niveau. Frage dich bei jedem Modul: *Was kann ich danach, was ich vorher nicht konnte?* Und: *Würde ein Arbeitgeber das erwarten?* 2. **Ordner anlegen:** `content//` mit `area.yaml` (Abschnitt 4). Die Ordner-ID ist kurz, klein und kebab-case, zum Beispiel `go`, `typescript`, `devops`. -3. **Farbe wählen.** Eine kräftige Farbe, auf der schwarzer Text gut lesbar ist (Kontrast ≥ 4,5 : 1 zu `#0B0B0B`). Vorhandene Farben nicht wiederholen: `#A98BFF` Kotlin, `#FF6A3D` Rust, `#FFD400` Linux, `#4D8BFF` Python, `#2BD97C` Automation, `#FF5CA8` Git, `#22D3EE` Data Analysis, `#C6F432` Testing, `#FF9F1C` PostgreSQL, `#F4A3FF` Erzählhandwerk, `#FF4D6D` Combat Design. Gute freie Kandidaten: `#FFC6A8` (Apricot), `#B5E3FF` (Eisblau). +3. **Farbe wählen.** Eine kräftige Farbe, auf der schwarzer Text gut lesbar ist (Kontrast ≥ 4,5 : 1 zu `#0B0B0B`). Vorhandene Farben nicht wiederholen: `#A98BFF` Kotlin, `#FF6A3D` Rust, `#FFD400` Linux, `#4D8BFF` Python, `#2BD97C` Automation, `#FF5CA8` Git, `#22D3EE` Data Analysis, `#C6F432` Testing, `#FF9F1C` PostgreSQL, `#F4A3FF` Erzählhandwerk, `#FF4D6D` Combat Design, `#A8B8FF` Sounddesign. Gute freie Kandidaten: `#FFC6A8` (Apricot), `#B5E3FF` (Eisblau). 4. **Module schreiben** (Abschnitte 5–7). Ein Modul pro Datei, Dateiname = Modul-ID. 5. **Karten, Projekte, Glossar, Spickzettel, Berufsseite** ergänzen (Abschnitt 8). 6. **Prüfen:** `pnpm content:check `, dann `pnpm verify `, dann `pnpm test` und `pnpm build`. diff --git a/README.md b/README.md index 04f272a..6ce0c54 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # learn.kiumu.app -Private Lernplattform für Kotlin & Android, Rust, Linux, Python, Automation mit Python, Git, Data Analysis mit Excel, SQL und Python, Testing & Softwarequalität, SQL & PostgreSQL für Anwendungsentwickler sowie Combat Design & Game Feel mit Godot 4. Live: [learn.kiumu.app](https://learn.kiumu.app) · Öffentliches Repository: [hernstev97/learning-platform](https://github.com/hernstev97/learning-platform) +Private Lernplattform für Kotlin & Android, Rust, Linux, Python, Automation mit Python, Git, Data Analysis mit Excel, SQL und Python, Testing & Softwarequalität, SQL & PostgreSQL für Anwendungsentwickler, Erzählhandwerk & interaktive Entscheidungen, Combat Design & Game Feel mit Godot 4 sowie Sounddesign & interaktive Musik. Live: [learn.kiumu.app](https://learn.kiumu.app) · Öffentliches Repository: [hernstev97/learning-platform](https://github.com/hernstev97/learning-platform) Die Plattform ist kein Nachschlagewerk. Jedes Modul besteht aus einer Lektion, die ein Denkmodell aufbaut und auf die offizielle Dokumentation verweist, und aus 10–14 Übungen in fester Reihenfolge: verstehen → vorhersagen → schreiben → Fehler finden → erklären → anwenden. Jeder Bereich hat Interview-Karteikarten mit Wiederholung, Projekte und ein Abschlussprojekt, das wie eine Take-Home-Aufgabe im Bewerbungsprozess geschnitten ist. diff --git a/content/sound/area.yaml b/content/sound/area.yaml new file mode 100644 index 0000000..05f9289 --- /dev/null +++ b/content/sound/area.yaml @@ -0,0 +1,35 @@ +id: sound +title: Sounddesign & interaktive Musik +short: Sound +tagline: Eigene Klänge und Musik, die im Spiel Information tragen. +description: > + Kritisch hören, Klänge als Cue-Liste planen, mit dem Handy aufnehmen, in REAPER schneiden, + säubern, schichten und variieren, ein eigenes Stück für mehrere Spielzustände aufbereiten und + alles in Godot 4.7 mischen und abhören. Der Kurs setzt deine Erfahrung aus der Musikproduktion + voraus und zeigt, was Spiele anders verlangen. Jeder Klang, jedes Stück und jede Mischentscheidung + liegt bei dir; eine KI darf nur Technik beschleunigen. „Unterwegs“ geht am Handy mit Kopfhörern, + verlinkte Clips brauchen Netz. „Am PC“ braucht REAPER und Godot; lesen kannst du diese Lektionen + auch unterwegs. +color: "#A8B8FF" +order: 16 +outcomes: + - Du hörst Spiele **kritisch**, benennst, welche Information ein Klang trägt, und begründest, was im Mix Vorrang hat. + - Du planst Klänge als **Cue-Liste und Zustandsdiagramm** aus Animation, Aktion und Spielzustand. + - Du **nimmst auf, schneidest, säuberst, schichtest und variierst** kurze Geräusche in REAPER und stimmst die Varianten auf eine selbst gewählte Aktion ab. + - Du formst **Hüllkurve, Frequenz, Dynamik und Raum** so, dass ein Klang im richtigen Frame, mit seinem Gewicht und an seinem Ort ankommt. + - Du bereitest ein **eigenes Stück für mehrere Zustände** auf – Loops, Stems, Übergänge – und testest die Übergänge mit `AudioStreamInteractive` im Spiel. + - Du **mischst** Musik, Geräusche und Stille nach Priorität, stimmst die **Lautheit** ab und prüfst alles im Spiel. + - Du **dokumentierst Quellen, Lizenzen und Entscheidungen**, sodass jede Datei im Projekt nachvollziehbar bleibt. +tracks: + - title: Hören und planen + description: Was Klänge im Spiel leisten, wie aus Ereignissen eine Cue-Liste wird, Aufnahmen mit dem Handy und fremdes Material mit sauberer Herkunft. + modules: [hoeren-analysieren, klangplan, feldaufnahme, quellen-lizenzen] + - title: Geräusche bauen + description: Hüllkurve, Frequenz, Dynamik und Raum für kurze Spielgeräusche, dann am PC in REAPER schneiden, säubern, schichten, variieren und eigenes Material umbauen. + modules: [klangformung, reaper-werkbank, layering-varianten, foley-umgebung] + - title: Interaktive Musik + description: Musik, die Zuständen folgt, ein eigenes Stück als Loops, Stems und Übergangsstücke und die Umsetzung mit AudioStreamInteractive in Godot. + modules: [musik-zustaende, loops-stems, godot-musik] + - title: Mischen und ins Spiel + description: Prioritäten, Maskierung, Ducking und Stille, dann Lautheit, Export, Import und Hörtest im Spiel und eine Werkstatt mit eigenem Audioabschnitt. + modules: [mischung-stille, export-im-spiel, werkstatt-audioabschnitt] diff --git a/content/sound/career.md b/content/sound/career.md new file mode 100644 index 0000000..3fb3550 --- /dev/null +++ b/content/sound/career.md @@ -0,0 +1,113 @@ +## Game Audio im Beruf + +Game Audio umfasst alles, was ein Spieler hört: Geräusche, Atmo, Musik, Stimmen und das System, das entscheidet, wann was erklingt und wie laut. In Studios teilt sich die Arbeit auf Sound Designer, technische Audio-Rollen, Komponisten, Dialog-Spezialisten und eine Audioleitung. In kleinen Teams und bei Solo-Projekten macht eine Person alles, oft dieselbe, die auch das Spiel entwirft. + +Für dich ist der Anlass dein eigenes Spiel. Diese Seite zeigt trotzdem, wie die Arbeit in Studios aussieht und woran Studios Bewerber messen. Das hilft auch ohne Bewerbung: Wer weiß, was dort zählt, weiß, worauf er beim eigenen Audioabschnitt achten sollte. Die Zahlen und Zitate stammen aus Quellen, die im Oktober 2026 online waren; beendete Anzeigen sind als solche gekennzeichnet. Diese Seite ist keine Arbeitsmarktstudie. + +## Rollen und Jobtitel + +Titel bedeuten in jedem Studio etwas anderes. Lies die Aufgaben, nicht nur die Überschrift. + +| Rolle | Typische Titel | Alltag | +| --- | --- | --- | +| Sounddesign | Sound Designer, Audio Designer | Geräusche und Atmo entwerfen, aufnehmen, bearbeiten, ins Spiel bringen und mischen | +| Technisches Sounddesign | Technical Sound Designer, Tech Audio Designer | Audiosysteme in Middleware und Engine bauen, Skripte und Werkzeuge für das Audioteam | +| Musik | Composer, Music Designer | Musik schreiben und als interaktives System umsetzen | +| Dialog | Dialogue Designer, Voice Designer | Sprachaufnahmen planen, bearbeiten und einbauen | +| Leitung | Lead Sound Designer, Audio Director | Klangästhetik über das ganze Spiel, Planung, Team | + +Beispiele aus Anzeigen: + +- **Larian** sucht in Québec einen Tech Audio Designer, der „the entire audio pipeline, from asset production through implementation, integration, and deployment“ betreut, Wwise-Systeme baut und Skripte für die Pipeline schreibt ([Larian, Tech Audio Designer](https://jobs.lever.co/larian/c0eba700-cfdb-4481-acf2-063f5772d5d8)). Die offene Bewerbung für Sound Designer beschreibt die Arbeit als „sound effects, environmental sounds, dialogue, and dynamic music systems“ ([Larian, Open Application](https://jobs.lever.co/larian/90157c7a-cc80-4cc3-8d78-285e5745f25e)). +- **CD Projekt Red** sucht für The Witcher 4 einen Lead Sound Designer in Warschau. Gefordert sind Qualität „evidenced by a portfolio, demo reel, or game credits“; Feld- und Foley-Aufnahmen stehen unter „Nice to Have“ ([CD Projekt Red](https://jobs.smartrecruiters.com/CDPROJEKTRED/744000152208583-lead-sound-designer)). +- **creā-ture Studios** verlangt von einem Technical Audio Designer „a solid plan for memory budgets, import settings and other technical aspects such as console limitations“ ([creā-ture Studios](https://www.crea-turestudios.com/en/technical-audio-designer/)). + +## Was Studios erwarten + +Brian Schmidt (GameSoundCon) hat 142 Anzeigen für kreative Audio-Rollen aus 26 Ländern ausgewertet, gesammelt vom 21. April bis 14. Juli 2026. Audio-Programmierung zählte er nicht mit, und er schreibt selbst, dass große Studios überrepräsentiert sind ([GameSoundCon, 2026](https://www.gamesoundcon.com/post/game-audio-jobs-and-skills-2026-what-employers-are-looking-for), auch bei [A Sound Effect](https://www.asoundeffect.com/game-audio-jobs-and-skills-2026/)). Die wichtigsten Befunde: + +| Fähigkeit | Anteil aller Anzeigen | +| --- | --- | +| Wwise | 58 % | +| Implementierung | 57 % | +| Unreal | 40 % | +| REAPER | 40 % | +| Pro Tools | 35 % | +| FMOD | 32 % | +| Skripte | 30 % | +| Aufnahme | 27 % | +| Komposition | 26 % | +| Unity | 22 % | + +- **Selbst ins Spiel bringen.** Schmidts Fazit: „employers expect audio people to get content into the game, not simply deliver files to someone else.“ Wer nur Dateien abliefert, deckt die Hälfte der Arbeit ab. +- **REAPER ist verbreitet.** „Reaper was the most frequently listed DAW.“ Deine Erfahrung damit zählt direkt. +- **Middleware.** 38 % der Anzeigen nennen gar keine Middleware. Wo Wwise oder FMOD stehen, oft als Beispiel („such as“); die Begriffe dahinter – Ereignisse, Busse, Zustände, Varianten – kennst du nach diesem Kurs aus Godot. +- **Komponisten machen mehr als Musik.** 37 % der Anzeigen mit „Composer“ im Titel verlangten auch Soundeffekte. „An in-house game composer may be expected to understand the entire audio pipeline, not only write cues.“ +- **Zusammenarbeit und Kommunikation** standen in 77 % bzw. 61 % der Anzeigen, Dokumentation in 26 %. Eine Cue-Liste und ein Entscheidungsprotokoll sind genau das. + +Für Einsteiger ist die Lage eng: Nur 6 % der Anzeigen waren Einstiegsstellen oder Praktika, fast 40 % richteten sich an Senior-, Lead- oder Leitungsrollen. Für Sound Designer verlangten die Anzeigen 2 bis 5 Jahre Erfahrung, rund 70 % verlangten Anwesenheit vor Ort. + +## Die Branche in Deutschland + +Laut Jahresreport 2026 des Verbands game arbeiten in Deutschland 12 235 Menschen bei Entwicklern und Publishern, drei Prozent weniger als im Vorjahr. Es gibt 956 Unternehmen, davon 474 reine Entwicklungsstudios; über drei Viertel haben weniger als zehn Beschäftigte ([game – Jahresreport 2026, PDF](https://www.game.de/wp-content/uploads/2026/08/Jahresreport-der-deutschen-Games-Branche-2026-1-1.pdf), [Übersicht](https://www.game.de/publikationen/jahresreport-2026/)). + +Eigene Audio-Stellen sind hier selten. Auch international gehörte in 27 % der von Schmidt ausgewerteten Anzeigen die Arbeit mit externen Dienstleistern zum Job; viel Audio entsteht bei Dienstleistern und Freiberuflern. Bei der Recherche im Oktober 2026 fand sich keine offene Sound-Designer-Stelle bei einem deutschen Studio. Crytek hatte in Frankfurt einen Junior Technical Audio Designer ausgeschrieben; die Anzeige ist beendet. Sie verlangte „Experience implementing audio in a game engine“, Wwise-Kenntnisse („Personal projects, game jams, university work and mods all count“), Python und „A portfolio, reel or GitHub showing audio work, tools, or both“ ([remotely.de, beendet](https://www.remotely.de/job/crytek-junior-technical-audio-designer)). + +Wege, die nicht über eine Stellenanzeige laufen: + +- **Eigene Spiele und Game Jams.** Ein veröffentlichtes kleines Spiel mit eigenem Ton zählt wie ein ausgeliefertes Projekt; Crytek nennt Game Jams ausdrücklich. +- **Freiberuflich.** Wer Musik oder Klänge als selbständiger Künstler liefert, sollte die [Künstlersozialkasse](https://www.kuenstlersozialkasse.de/) kennen. Nutzungsrechte und Buy-outs regelt der Vertrag; lass ihn prüfen, bevor du unterschreibst. +- **Veranstaltungen.** Die Entwicklerkonferenz der gamescom heißt [gamescom dev](https://dev.gamescom.global/) und findet im August in Köln statt. +- **Jobbörsen.** [Games-Career](https://www.games-career.com/) für die deutschsprachige Branche, international [Hitmarker](https://hitmarker.net/) und [Work With Indies](https://www.workwithindies.com/). Die [Game Audio Network Guild](https://www.audiogang.org/) ist ein internationaler Berufsverband für Game Audio. + +## Demo-Reel und Aufgaben + +Fast jede Audio-Bewerbung braucht ein **Demo-Reel**: ein kurzes Video mit Spielszenen, deren Ton von dir stammt. Richard Ludlow (Hexany Audio) beschreibt, wie sein Studio Bewerbungen sichtet ([GameSoundCon](https://gamesoundcon.com/post/getting-hired-for-game-sound-design-advice-from-hexany-audio-s-richard-ludlow)): + +- „Our very first steps is a blind review of demo reels.“ Der Lebenslauf kommt danach. +- „Audio-only reels are no good. We need to see sound work done to picture to tell what your creative intent was.“ +- „We are 100% fine with sound re-designs that aren't from titles you actually worked on.“ Ein **Redesign** ersetzt den Ton einer veröffentlichten Spielszene durch deinen eigenen. +- „if your link doesn't work, we move on.“ Prüf den Link in einem privaten Fenster. + +Wie eine Aufgabe aussehen kann, zeigt das Mentoring-Programm „Develop at Ubisoft“ 2026/27 mit Standorten in Berlin, Düsseldorf und Mainz. Im Audio-Zweig „you will create and submit an original sound design using … Wwise. You will design and integrate audio for specific events — for example, animations, ambiences, and interactive elements“ ([Ubisoft](https://jobs.smartrecruiters.com/Ubisoft2/744000155797029-develop-at-ubisoft-mentorship-audio-2026-2027)). Das Programm richtet sich an Studierende und Berufseinsteiger aus unterrepräsentierten Geschlechtern und ist kein Einstellungsverfahren. Die Aufgabe ist trotzdem typisch: Ereignisse vertonen, die Atmo bauen, alles einbauen. + +### Ein Reel aus diesem Kurs + +1. **Ein Abschnitt, nicht zwanzig Schnipsel.** Dein Audioabschnitt aus der Werkstatt zeigt Varianten, Musik mit Zustandswechsel, Atmo und Mischung in 60 bis 90 Sekunden. +2. **Zeig den Wechsel.** Lass im Video sehen, wann der Zustand wechselt, und hören, dass die Musik auf dem Takt folgt. +3. **Erklär die Technik daneben.** Eine Seite mit Cue-Liste, Bus-Plan und Übergangstabelle zeigt, dass du implementieren kannst. +4. **Eigene Klänge, Fremdes gekennzeichnet.** Fremdes Material mit Lizenz und Quelle kennzeichnen; generierte Klänge gehören nicht hinein. + +## Im Gespräch + +- **Erklär Entscheidungen, nicht Plugins.** „Ich habe die Transiente auf das Kontaktbild gelegt und die Fahne gekürzt, weil die Aktion nach 18 Frames vorbei ist“ sagt mehr als eine Liste von Effekten. +- **Rechne in Frames und dB.** Wer sagt, ein Klang sei „zu spät“, sollte sagen können, um wie viele Millisekunden und warum. +- **Zeig, dass du testest.** Hörprotokolle mit Kopfhörer, Laptop-Lautsprecher und leiser Wiedergabe sind ein Beleg dafür. +- **Rede über Prioritäten.** Was muss der Spieler hören, wenn alles gleichzeitig passiert? Die Frage kommt in fast jedem Gespräch über Mischung. + +## Middleware und Engine + +Godot hat Busse, Effekte, Zufallsvarianten und mit `AudioStreamInteractive` auch interaktive Musik eingebaut. In Studios laufen dieselben Ideen meist über Wwise oder FMOD. Wwise ist im Indie-Plan für Produktionen mit weniger als 250 000 US-Dollar Budget kostenlos, ohne Grenze bei der Zahl der Klänge ([Audiokinetic, Preise](https://www.audiokinetic.com/en/wwise/pricing/)). FMOD hat eigene Bedingungen ([FMOD, Lizenzen](https://www.fmod.com/licensing)). Ob das jeweilige Authoring-Werkzeug unter CachyOS läuft, hat dieser Kurs nicht geprüft; schau vor dem Einstieg in die Systemanforderungen des Herstellers. + +## Lernplan in Wochen + +Bei zwei bis drei Abenden pro Woche und den Busfahrten dazwischen: + +| Woche | Unterwegs | Am PC | +| --- | --- | --- | +| 1 | Kritisch hören, Klangplan | REAPER-Werkbank | +| 2 | Feldaufnahme, Quellen und Lizenzen | eigene Aufnahmen säubern | +| 3 | Hüllkurve, Frequenz, Dynamik, Raum | Layering und Varianten | +| 4 | – | Foley und Atmo | +| 5 | Musik für Zustände | Loops und Stems | +| 6 | Mischung und Stille | Musik in Godot | +| 7 | – | Export und Hörtest im Spiel | +| 8–9 | Hörprotokolle unterwegs | Werkstatt: Audioabschnitt | + +## Ressourcen + +- [REAPER User Guide 7.81](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf) und [Lernvideos](https://www.reaper.fm/videos.php) +- [Godot Docs: Audio](https://docs.godotengine.org/en/stable/tutorials/audio/index.html) +- Karen Collins, [Game Sound](https://doi.org/10.7551/mitpress/7909.001.0001) (MIT Press, 2008): Geschichte und Theorie von Spielton +- [A Sound Effect](https://www.asoundeffect.com/) und [Designing Sound](https://designingsound.org/): Interviews und Werkstattberichte von Sounddesignern +- [GameSoundCon](https://www.gamesoundcon.com/): Konferenz und jährliche Auswertungen zu Gehalt und Stellen diff --git a/content/sound/cheatsheet.md b/content/sound/cheatsheet.md new file mode 100644 index 0000000..ac0a96d --- /dev/null +++ b/content/sound/cheatsheet.md @@ -0,0 +1,252 @@ +## Zeit: Frames, Millisekunden, Samples + +60 Physik-Frames pro Sekunde, Dateien mit 48 kHz, Godot mischt mit 44,1 kHz. + +| Frames | ms | Samples 48 kHz | Samples 44,1 kHz | +| --- | --- | --- | --- | +| 1 | 16,7 | 800 | 735 | +| 2 | 33,3 | 1 600 | 1 470 | +| 6 | 100 | 4 800 | 4 410 | +| 15 | 250 | 12 000 | 11 025 | +| 60 | 1 000 | 48 000 | 44 100 | + +```text +ms = Frames × 1000 / 60 +Samples = Sekunden × Abtastrate +Bild k bei s Bildern pro Sekunde + beginnt bei (k − 1) / s Sekunden +Stille vor der Transiente + = Latenz +``` + +## Pegel: Dezibel + +| dB | Amplitude | Leistung | +| --- | --- | --- | +| −1 | 0,891 | 0,794 | +| −3 | 0,708 | 0,501 | +| −6 | 0,501 | 0,251 | +| −10 | 0,316 | 0,100 | +| −20 | 0,100 | 0,010 | +| −60 | 0,001 | 0,000001 | + +```text +dB = 20 × log10(Verhältnis) + (Verhältnis der Amplituden) +gleicher Klang, gleiches Sample + ≈ +6 dB +verschiedene Klänge (RMS) + ≈ +3 dB +Abstand verdoppelt (Aufnahme) + bis −6 dB Vorsprung +Kompressor, Schwelle S, Ratio R + aus = S + (ein − S) / R +``` + +Godot: `linear_to_db()`, `db_to_linear()`; Bus-Regler und `volume_db` addieren sich auf dem Weg zu `Master`. + +## Aufnahme und Dateien + +```text +WAV-Größe in Bytes = + Rate × Bytes × Kanäle × s +48 kHz, 24 Bit, 1 min + mono = 8,64 MB + stereo = 17,28 MB +``` + +| Schritt | Merke | +| --- | --- | +| App prüfen | verlustfrei, Pegelautomatik und Filter aus, Probedatei | +| Pegel | Probe-Take; Übersteuern ist endgültig | +| Abstand | nah ran; Verhältnis ändert nur der Abstand | +| Ansage | Ort, Gegenstand, Take am Anfang | +| Takes | mehrere, mit verschiedenen Abständen | +| Room Tone | 30 s, gleiche Position, gleicher Pegel | +| Protokoll | Datum, Ort, Gerät, Abstand, Notiz je Take | +| Rechte | keine verständlichen Stimmen Dritter | + +## Cue-Liste: zehn Spalten + +| Spalte | Inhalt | +| --- | --- | +| ID | Name nach eigenem Schema | +| Auslöser | Bild, Frame oder Signal | +| Typ | One-Shot oder Loop | +| Länge | in ms oder Frames | +| Varianten | Zahl, aus der Häufigkeit | +| Priorität | 1 bis 3 | +| Bus | z. B. Musik, SFX, Atmo, UI | +| Ort | mit Ort (2D) oder ohne | +| Status | geplant, Prüfmuster, aufgenommen, im Spiel, geprüft | +| Quelle | Verweis auf die Quellenliste | + +- Eine Zeile pro Ereignis, nicht pro Datei. +- Trefferklang im Frame der Treffermeldung, nicht beim Einschalten der Hitbox. +- Jeder Loop: ein Start, an jedem Ausgang seines Zustands ein Stopp. +- Gegen Flattern: zwei Schwellen (Hysterese) oder Nachlauf (Ausstieg erst, wenn das Signal eine Zeit lang fehlt). + +## REAPER: Werkbank + +| Aufgabe | Weg | +| --- | --- | +| Region aus Auswahl | Shift+R | +| Region je Item | *Markers: Insert separate regions for each selected item* | +| Regionen benennen | Region/Marker Manager, Doppelklick | +| Stille entfernen | *Item processing → Dynamic split items…* | +| Rauschprofil | ReaFIR, *Subtract*, Profil aus dem Room Tone lernen | +| Normalisieren | im Render unter *Postprocess…*, nach allen FX (LUFS-I, True Peak, Peak …) | +| Viele Dateien rendern | *File → Region Render Matrix*, Source *Region render matrix* | +| Loop mit Fahne | *2nd pass render*, Tail aus | +| Auto-Crossfade | Alt+X | + +Reihenfolge: Schnitt und Fades → Hochpass → ReaFIR → ReaGate → Regionen → Render mit Normalisieren im Postprocess. *Normalize items* ändert die Item-Lautstärke vor den FX und verschiebt Rauschprofil und Gate-Schwelle. + +| Wildcard | wird zu | +| --- | --- | +| `$region` | Name der Region | +| `$track` | Name der Spur | +| `$project` | Name des Projekts | +| `$parenttrack` | Name des Ordner-Tracks | + +Render-Fallen: Tail aus bei Hall auf der Spur schneidet ab; *Silently increment filenames* an legt neue Namen an, das Spiel spielt die alte Datei. + +## Varianten in Godot + +| Modus | direkte Wiederholung | A-B-A | +| --- | --- | --- | +| `PLAYBACK_RANDOM` | 1/n | – | +| `PLAYBACK_RANDOM_NO_REPEATS` (Standard) | nie | 1/(n − 1) | +| `PLAYBACK_SEQUENTIAL` | nie | festes Muster | + +Mit zwei Varianten und `NO_REPEATS` entsteht A B A B. Echte Takes tragen die Abwechslung, Tonhöhe und Lautstärke strecken sie. + +## Atmo und Loops + +```text +Bett: Auffälliges herausschneiden, + Hälften tauschen, + Crossfade in die Mitte + Loop = Länge − Crossfade +Spot-Klänge: + Pause beginnt nach finished +``` + +Mono für Klänge mit Ort, Stereo für Bett und Musik. `AudioStreamPlayer2D` gewichtet Stereo-Kanäle nach Position, statt sie zu mischen. + +## Musik: Raster und Übergänge + +```text +Schlag = 60 / BPM +Takt = Schlag × Schläge je Takt +Samples = Schläge × Schlag × Rate +4 Takte 4/4, 120 BPM, 48 kHz + = 384 000 Samples +nächster Takt (Godot: erste + Grenze nach der Anfrage) + = (floor(t / Takt) + 1) × Takt +Wartezeit: bis zu einem Takt +``` + +| Teil | Achte auf | +| --- | --- | +| Loop | Taktgrenzen, Fahne am Anfang (2nd pass) | +| Stem | gleicher Start, gleiche Länge, gleiche Fahne | +| Übergangsstück | ganze Schläge, gleiche Import-Werte | +| Stinger | beginnt auf der Transiente, klingt aus; liegt über der Musik oder ersetzt sie kurz | +| Prüfloop | Metronom-Klicks, Takt 1 hoch | + +Ogg-Import in Godot 4.7.2: + +| Feld | Standard | Bedeutung | +| --- | --- | --- | +| `loop` | `false` | Datei wiederholen | +| `loop_offset` | `0` | Sprungziel in Sekunden | +| `bpm` | `0` | Tempo; 0 = Takt-Übergänge greifen sofort | +| `beat_count` | `0` | Länge in Schlägen ab Dateianfang | +| `bar_beats` | `4` | Schläge pro Takt | + +`AudioStreamWAV` hat kein `bpm`; Loop-Punkte dort in Samples (`loop_begin`, `loop_end`). + +## Mischung + +- Regeln statt Stellen: Priorität, Stimmenbegrenzung (`max_polyphony`), Ducking, Stille. +- Erst wegnehmen, dann begrenzen, dann ducken, zuletzt lauter. +- Maskierung: in der Frequenz (tiefe Klänge stärker) und in der Zeit (kurz vor und nach einem lauten Klang). +- Ducking in Godot: `AudioEffectCompressor` auf dem Bus, der leiser werden soll, `sidechain` = Name des steuernden Busses. Standard: Threshold 0 dB (muss herunter, sonst duckt nichts), Ratio 4, Attack 20 µs, Release 250 ms (15 Frames). +- Gemessen in 4.7.2: Absenkung etwa doppelt so stark wie die Lehrbuchformel, Rückkehr nach 250 ms Release erst nach rund 1 s. Ein unbekannter Busname hört auf `Master`. Godot bearbeitet die Busse von rechts nach links: Steht der Sidechain-Bus rechts, liest der Kompressor ihn fertig gemischt, mit Fader und Mute; steht er links, liest er ihn vor Fader und Mute. + +## Musik in Godot: AudioStreamInteractive + +Umschalten am laufenden Player: `get_stream_playback()` liefert das Playback, darauf `switch_to_clip_by_name(&"gefahr")`. Am Stream selbst gibt es die Methode nicht, und vor `play()` gibt es kein Playback. + +| Übergang ab | Anfrage bei 5,30 s, 120 BPM, Clip 8 s | +| --- | --- | +| `IMMEDIATE` | 5,30 s | +| `NEXT_BEAT` | 5,50 s | +| `NEXT_BAR` | 6,00 s | +| `END` | 8,00 s | + +| Ziel | Bedeutung | +| --- | --- | +| `SAME_POSITION` | gleiche Stelle im Ziel-Clip | +| `START` | Anfang des Ziel-Clips | +| `PREVIOUS_POSITION` | wo der Ziel-Clip zuletzt war | + +- Ohne passende Regel: nächster Schlag, Ausblendung über einen Schlag. +- `bpm` 0: Schlag- und Takt-Übergänge greifen sofort, `fade_beats` zählt dann in Sekunden. +- Nur anfragen, wenn sich das Ziel ändert; der laufende Clip, erneut angefragt, beginnt von vorn. +- Nachlauf gegen Flattern: Ausstieg erst, wenn das Signal eine Zeit lang fehlt. +- Vertikal: `AudioStreamSynchronized`, Lautstärke je Stream (`stream_N/volume`), −60 dB ist nicht stumm. +- Messen: Prüfloop offline mit `mix_audio()` oder im Spiel mit `AudioEffectRecord` auf `Master`; Movie Maker braucht ein Fenster. + +## Lautheit + +| Maß | Fenster | +| --- | --- | +| LUFS-M (momentan) | 0,4 s | +| LUFS-S (kurzzeitig) | 3 s | +| LUFS-I (integriert) | ganzes Signal, mit Gating | +| True Peak (dBTP) | zwischen den Samples | + +```text +Gain = Ziel − Messwert + (1 LU = 1 dB) +True Peak steigt um denselben Gain +Mono-Datei im Spiel (4.7.2), + verglichen mit der Datei: + AudioStreamPlayer +3 LU + Player2D, Bildmitte −3 LU +EBU R 128: −23 LUFS, max. −1 dBTP +ASWG-R001: −24 LKFS Heimkonsole, + −18 LKFS tragbar, max. −1 dBTP + (ganzes Spiel, mind. 30 Minuten) +``` + +Normen messen das ganze Programm. Lautheitsziele für einzelne Dateien sind deine Platzhalter; ob das Spiel passt, zeigt ein langer Mitschnitt. REAPER: *File → Render… → Postprocess…* normalisiert auf LUFS-I, True Peak und andere Maße und begrenzt mit „Brickwall limit“; danach neu messen. + +## Hörtest im Spiel + +| Spalte | Inhalt | +| --- | --- | +| Gerät | Kopfhörer, Laptop, Handy … | +| Lautstärke | normal, leise | +| Situation | Stelle im Abschnitt | +| Beobachtung | was fehlt, was stört | +| Messwert | LUFS, dBTP, Frame | +| Frage | was du als Nächstes prüfst | + +## Lizenzen + +| Lizenz | Verkauftes Spiel | Pflicht | +| --- | --- | --- | +| eigene Aufnahme | ja | Zeile in der Quellenliste | +| CC0 | ja | keine, Herkunft notieren | +| CC BY | ja | TASL: Titel, Autor, Quelle, Lizenz mit Version und Link | +| CC BY-SA | vorher klären | Nennung, gleiche Lizenz für Bearbeitungen | +| CC BY-ND | nein bzw. vorher klären | Vertonung gilt als Bearbeitung (1a), ND verbietet ihre Weitergabe | +| … NC | nein | – | +| Sonniss, Splice | laut Text ja | Weitergabe der Klänge verboten | +| Library-EULA (NI) | Musik ja, Einzeleffekt offen | beim Hersteller anfragen | + +Quellenliste je Datei: Datei, Herkunft, Autor, Lizenz mit Version, URL, Abrufdatum, Nachweis, Änderung, Credit. Kein generiertes Audio. Kein Rechtsrat. diff --git a/content/sound/glossary.yaml b/content/sound/glossary.yaml new file mode 100644 index 0000000..8691fb3 --- /dev/null +++ b/content/sound/glossary.yaml @@ -0,0 +1,107 @@ +terms: + - term: Cue-Liste + definition: Liste aller Klangereignisse eines Spiels oder Abschnitts mit Name, Auslöser, One-Shot oder Loop, Länge, Varianten, Priorität, Bus und Status. Planungs- und Übergabedokument zwischen Design, Animation, Programmierung und Audio. + - term: Ereignis + definition: Ein Moment im Spiel, an dem ein Klang beginnt, endet oder sich ändert, etwa ein Kontaktbild in der Animation, ein gemeldeter Treffer oder ein Zustandswechsel. In Middleware wie Wwise und FMOD heißt das Objekt, das Code auslöst, ebenfalls Event. + - term: One-Shot + definition: Klang, der einmal abgespielt wird und endet, etwa ein Schritt oder ein Treffer. Gegenstück zum Loop. + - term: Loop + definition: Klang oder Musikabschnitt, der nahtlos wiederholt wird. In Godot meldet ein Loop kein `finished`. + - term: Variante + definition: Eine von mehreren Fassungen desselben Klangs für dasselbe Ereignis. Varianten verhindern, dass ein häufiges Ereignis wie eine Wiederholung derselben Datei klingt. + - term: Maschinengewehr-Effekt + definition: Eindruck eines mechanischen Rasterns, wenn derselbe kurze Klang schnell hintereinander identisch abgespielt wird, etwa bei Schritten. Gegenmittel sind Varianten und kleine Änderungen von Tonhöhe und Lautstärke. + - term: AudioStreamRandomizer + definition: Godot-Ressource, die bei jedem Abspielen aus mehreren Streams wählt und Tonhöhe und Lautstärke zufällig ändern kann. Standardmodus in Godot 4.7 ist `PLAYBACK_RANDOM_NO_REPEATS`. + - term: Layering + definition: Einen Klang aus mehreren Schichten bauen, etwa Transiente, Körper und Fahne oder tiefe, mittlere und hohe Anteile aus verschiedenen Quellen. Jede Schicht hat eine Aufgabe. + - term: Schicht + definition: Ein Bestandteil eines geschichteten Klangs (englisch Layer). In der Musik auch eine Stimme oder Instrumentengruppe, die man bei vertikaler Schichtung zu- oder abschaltet. + - term: Hüllkurve + definition: Verlauf der Lautstärke eines Klangs über die Zeit, oft beschrieben mit Attack, Decay, Sustain und Release. Bei kurzen Spielgeräuschen entscheidet vor allem die Attack, wann der Klang wahrgenommen wird. + - term: Transiente + definition: Kurzer, lauter Anfang eines Klangs, etwa der Aufschlag eines Schritts. Trägt das Timing; liegt sie nicht im richtigen Bild, wirkt der Klang zu früh oder zu spät. + - term: Hallfahne + definition: Nachklang eines Klangs oder Stücks nach dem eigentlichen Ende, etwa durch Hall (englisch Tail). Wird beim Rendern leicht abgeschnitten und muss bei Musik-Loops an den Anfang zurückgeführt werden. + - term: Room Tone + definition: Aufnahme der Stille eines Ortes, ohne gezielte Geräusche. Dient als Rauschprofil für die Rauschreduktion und als Füllmaterial beim Schneiden. + - term: Feldaufnahme + definition: Aufnahme außerhalb eines Studios, an dem Ort, wo der Klang entsteht (englisch Field Recording). + - term: Ansage + definition: Kurze gesprochene Kennung am Anfang einer Aufnahme mit Ort, Gegenstand und Take (englisch Slate). Macht Aufnahmen auch ohne Protokoll zuordenbar. + - term: Foley + definition: Geräusche, die passend zum Bild aufgenommen werden, etwa Schritte, Kleidung und Gegenstände. Benannt nach Jack Foley, der das Verfahren beim Film prägte. + - term: Atmo + definition: Umgebungsklang eines Ortes (englisch Ambience), meist aus einem Bett und darüber verteilten Spot-Klängen. + - term: Bett + definition: Durchgehender, meist geloopter Grundklang einer Atmo, etwa Wind oder Raumrauschen. + - term: Spot-Klang + definition: Einzelner Klang, der über ein Bett verteilt wird, etwa ein Vogelruf oder ein Knarzen. Im Spiel oft zufällig ausgelöst, damit das Bett nicht als Loop auffällt. + - term: Diegetisch + definition: Klang, der in der Spielwelt existiert und von den Figuren gehört werden könnte, etwa Schritte. Nicht diegetisch sind Klänge außerhalb der Welt wie Menütöne oder meist die Musik. + - term: Hörmodi + definition: Nach Michel Chion drei Arten zuzuhören – kausal (Was erzeugt den Klang?), semantisch (Was bedeutet er?) und reduziert (Wie klingt er an sich?). Werkzeug für die Analyse. + - term: Grundton + definition: In R. Murray Schafers Begriffen der Klanghintergrund eines Ortes, den man meist nicht bewusst hört (englisch Keynote Sound). Dazu kommen Signale, die man beachtet, und Klangmarken, die einen Ort unverwechselbar machen. + - term: Priorität + definition: Rangfolge, welche Klänge hörbar bleiben müssen, wenn viele gleichzeitig spielen. Grundlage für Stimmenbegrenzung, Ducking und Lautstärke. + - term: Maskierung + definition: Ein Klang macht einen anderen schwer hörbar, weil er im gleichen Frequenzbereich oder zur gleichen Zeit lauter ist. + - term: Ducking + definition: Automatisches Absenken eines Signals, solange ein anderes spielt, etwa Musik unter wichtigen Geräuschen. Meist mit einem Kompressor, dessen Sidechain vom anderen Signal gesteuert wird. + - term: Sidechain + definition: Steuereingang eines Effekts, der auf ein anderes Signal reagiert als das, das er bearbeitet. In Godot hat `AudioEffectCompressor` dafür die Eigenschaft `sidechain` mit dem Namen eines Busses. + - term: Stimmenbegrenzung + definition: Obergrenze, wie viele Instanzen eines Klangs gleichzeitig spielen dürfen. In Godot `max_polyphony` am Player. + - term: Audiobus + definition: Kanal, über den Ton zum Ausgang läuft, mit Lautstärke, Effekten und Pegelanzeige. In Godot im Panel *Audio*; Player wählen ihren Bus über den Namen. + - term: Lautheit + definition: Wahrgenommene Lautstärke, gemessen in LUFS nach ITU-R BS.1770. Anders als der Spitzenpegel berücksichtigt sie Frequenzgewichtung und Dauer. + - term: LUFS + definition: Loudness Units relative to Full Scale. Integriert über ein ganzes Signal, kurzzeitig über 3 Sekunden, momentan über 400 Millisekunden. Ein LU entspricht einem dB. + - term: True Peak + definition: Spitzenpegel des rekonstruierten analogen Signals zwischen den Samples, angegeben in dBTP. Kann über dem höchsten Sample-Wert liegen. + - term: Headroom + definition: Abstand zwischen dem Pegel eines Signals und 0 dBFS. Platz, damit gleichzeitige Klänge zusammen nicht übersteuern. + - term: dBFS + definition: Dezibel relativ zum größten darstellbaren Wert eines digitalen Signals. 0 dBFS ist das Maximum, alle Pegel darunter sind negativ. + - term: Abtastrate + definition: Samples pro Sekunde, etwa 48 000. Godot mischt standardmäßig mit 44 100 Hz (`audio/driver/mix_rate`) und rechnet andere Raten um. + - term: Region + definition: In REAPER ein benannter Zeitabschnitt im Projekt. Für Spielgeräusche meist eine Region je Klang, die sich einzeln rendern lässt. + - term: Region Render Matrix + definition: Tabelle in REAPER, in der man festlegt, welche Region über welche Spur gerendert wird. Damit entstehen viele Dateien in einem Durchgang. + - term: Wildcard + definition: Platzhalter in REAPERs Render-Dateinamen wie `$region` oder `$project`, den REAPER beim Rendern ersetzt. + - term: Horizontale Neuordnung + definition: Interaktive Musik, die zwischen Abschnitten wechselt, je nach Zustand (englisch Horizontal Re-sequencing). Gegenstück zur vertikalen Schichtung. + - term: Vertikale Schichtung + definition: Interaktive Musik aus gleichzeitig laufenden Schichten, die je nach Zustand lauter oder leiser werden (englisch Vertical Layering). + - term: Stem + definition: Gerenderte Teilspur eines Stücks, etwa Rhythmus oder Melodie, mit gleicher Länge und gleichem Startpunkt wie die übrigen Stems. + - term: Stinger + definition: Kurzes Musikstück, das ein Ereignis markiert, etwa einen Fund oder einen Sieg. Meist liegt es über der laufenden Musik; es kann sie auch kurz ersetzen, danach kehrt die Musik zurück. + - term: Übergangsstück + definition: Kurzer Musikabschnitt, der zwischen zwei Zuständen vermittelt. In Godots `AudioStreamInteractive` als Filler-Clip einer Übergangsregel. + - term: AudioStreamInteractive + definition: Godot-Ressource für interaktive Musik aus Clips mit einer Übergangstabelle. Ein Übergang kann sofort, auf dem nächsten Schlag, dem nächsten Takt oder am Ende des Clips greifen; Schlag und Takt brauchen Tempo und Taktangabe im Import. + - term: AudioStreamSynchronized + definition: Godot-Ressource, die mehrere Streams synchron abspielt und je Stream eine Lautstärke hat. Werkzeug für vertikale Schichtung. + - term: Hysterese + definition: Zwei Schwellen gegen schnelles Hin- und Herschalten zwischen zwei Zuständen. Der Zustand beginnt bei einem Wert und endet erst bei einem anderen, etwa Kampfmusik ab einem Abstand von 100 Pixeln und Ruhe erst ab 150 (Platzhalter). + - term: Nachlauf + definition: Ein Zustand endet erst, wenn sein Signal eine Zeit lang ununterbrochen fehlt. Verhindert Flattern wie die Hysterese und kostet Reaktionszeit; bei Musik auf dem Takt kommt die Wartezeit bis zur nächsten Taktgrenze dazu. + - term: Prüfloop + definition: Technisches Testsignal aus Metronom-Klicks mit hörbarem Taktanfang, mit dem man prüft, wo ein Musikübergang tatsächlich greift. Kein Stück für das Spiel. + - term: Platzhalter + definition: Wert oder Klang, der nur zum Testen da ist und keine Empfehlung darstellt. In diesem Kurs sind alle Zahlen für dein Spiel Platzhalter. + - term: Redesign + definition: Neuvertonung einer veröffentlichten Spiel- oder Filmszene mit eigenem Ton, üblich für Demo-Reels. + - term: CC0 + definition: Creative-Commons-Verzicht, mit dem ein Urheber so weit wie möglich auf seine Rechte verzichtet. Namensnennung ist nicht verlangt, aber zur Nachvollziehbarkeit sinnvoll. + - term: CC BY + definition: Creative-Commons-Lizenz, die Nutzung, Bearbeitung und kommerziellen Einsatz erlaubt, wenn du den Urheber nennst, auf die Lizenz verweist und Änderungen kenntlich machst. Die Zusätze NC und ND schließen kommerzielle Nutzung bzw. Bearbeitungen aus, SA verlangt dieselbe Lizenz für Bearbeitungen. + - term: Quellenliste + definition: Datei mit einer Zeile je Audiodatei im Projekt – Herkunft, Urheber, Lizenz mit Version, URL, Abrufdatum, Nachweis und Bearbeitung. + - term: Entscheidungsprotokoll + definition: Aufzeichnung von Gestaltungsentscheidungen mit Grund, verworfener Alternative, Test und Ergebnis. Macht nachvollziehbar, warum ein Klang so ist, wie er ist. diff --git a/content/sound/interview.yaml b/content/sound/interview.yaml new file mode 100644 index 0000000..a47407b --- /dev/null +++ b/content/sound/interview.yaml @@ -0,0 +1,383 @@ +cards: + - id: aufgaben-eines-klangs + module: hoeren-analysieren + q: Welche Aufgaben kann ein Klang im Spiel haben? + a: | + Rückmeldung auf eine Eingabe, Warnung vor etwas, das gleich passiert, Information über etwas außerhalb des Bilds, Orientierung im Raum, Stimmung und Kontinuität zwischen Szenen. Oft hat ein Klang mehrere davon zugleich: Ein Schritt bestätigt die Bewegung und sagt, auf welchem Boden ich stehe. Wenn ich einen Klang beurteile, frage ich zuerst, welche Aufgabe er hat, und erst danach, ob er gut klingt. + tags: [hoeren, funktion] + level: 1 + - id: diegetisch-transdiegetisch + module: hoeren-analysieren + q: Was heißt diegetisch, und wo wird die Unterscheidung im Spiel unscharf? + a: | + Diegetisch ist ein Klang, dessen Quelle in der Spielwelt liegt, etwa ein Schritt. Nicht diegetisch sind Menütöne oder meist die Musik. Im Spiel kreuzen viele Klänge die Grenze: Musik, die anschwillt, wenn ein Gegner kommt, liegt außerhalb der Welt und informiert trotzdem über sie. Kristine Jørgensen nennt solche Klänge transdiegetisch. Für die Arbeit heißt das: Auch Musik kann eine Warnung tragen und braucht dann eine Priorität. + tags: [hoeren, diegese] + level: 2 + - id: unbekanntes-spiel-analysieren + module: hoeren-analysieren + q: Wie analysierst du den Ton eines Spiels, das du nicht kennst? + a: | + Ich nehme einen kurzen Clip und höre ihn mehrmals mit Kopfhörern, einmal mit geschlossenen Augen. Jeden Klang trage ich mit Zeit, Quelle im Bild oder außerhalb, Aufgabe und Priorität ein. Dann frage ich, welche Information nur über den Ton kommt, wie viel Vorlauf die Warnungen haben und wo es still wird. Was mir gefällt, notiere ich getrennt; ein schöner Klang kann trotzdem eine Warnung verdecken. + tags: [hoeren, analyse] + level: 1 + - id: hoerweisen-chion + module: hoeren-analysieren + q: Was bringen dir Chions drei Hörweisen bei der Analyse? + a: | + Sie ergeben drei Durchgänge. Kausal frage ich, was den Klang erzeugt. Semantisch, was er bedeutet, etwa „gleich greift er an“. Reduziert, wie er an sich klingt: Hüllkurve, Lage, Länge. Den reduzierten Durchgang mache ich zuletzt, sonst beurteile ich Klangfarbe, bevor ich weiß, welche Aufgabe der Klang hat. + tags: [hoeren, analyse] + level: 2 + - id: cue-liste-wozu + module: klangplan + q: Was ist eine Cue-Liste, und was steht darin? + a: | + Eine Liste mit einer Zeile pro Klangereignis, nicht pro Datei. Darin stehen ID, Auslöser als Bild, Frame oder Signal, Typ (One-Shot oder Loop), Länge, Zahl der Varianten, Priorität, Bus, ob der Klang einen Ort hat, Status und Quelle. Sie ist die Übergabe zwischen Design, Animation, Programmierung und Audio: Wer sie liest, weiß, was fehlt, was fertig ist und wo jeder Klang ausgelöst wird. + tags: [planung, cue-liste] + level: 1 + - id: neuer-gegner-ton + module: klangplan + q: Wir bekommen einen neuen Gegner. Wie planst du seinen Ton? + a: | + Ich lasse mir die Animationsliste und den Zustandsautomaten geben. Daraus schreibe ich eine Cue-Liste mit Auslöser, Typ, Varianten aus der geschätzten Häufigkeit, Priorität, Bus und Ort. Für jeden Loop prüfe ich am Diagramm alle Ausgänge, damit er überall stoppt. Dann setze ich Prüfmuster ein und vergleiche im Log, ob sie im richtigen Frame kommen. Erst danach entstehen die Klänge. + tags: [planung, cue-liste, technik] + level: 2 + - id: treffer-ton-wann + module: klangplan + q: In welchem Frame gehört der Trefferklang ausgelöst? + a: | + In dem Frame, in dem der Treffer gemeldet wird, nicht in dem, in dem die Hitbox angeht. Sonst klingt auch ein Schlag ins Leere wie ein Treffer. Den Klang des Schwungs löse ich getrennt aus, am Bild der Animation, das ihn trägt. In der Cue-Liste stehen deshalb zwei Zeilen mit zwei Auslösern. + tags: [planung, synchronisation, kampf] + level: 1 + - id: wie-viele-varianten + module: klangplan + q: Wie viele Varianten braucht ein Klang? + a: | + Das folgt aus der Häufigkeit. Ein Schritt, der hundertmal pro Minute spielt, braucht mehr als ein Klang, der einmal pro Level kommt. Bei n zufällig gewählten Varianten kommt dieselbe mit Wahrscheinlichkeit 1/n direkt wieder. Es gibt auch Klänge, die bewusst gleich bleiben: Ein Signal, das der Spieler lernen soll, etwa eine Warnung, darf nicht jedes Mal anders klingen. + tags: [planung, varianten] + level: 1 + - id: loop-ausgaenge + module: klangplan + q: Ein Loop läuft weiter, obwohl die Figur längst etwas anderes tut. Wo suchst du? + a: | + Im Zustandsdiagramm. Ein Loop braucht einen Start und an jedem Ausgang seines Zustands einen Stopp. Meist wurde ein Ausgang vergessen, etwa ein Treffer, der das Laufen unterbricht, oder ein Szenenwechsel. Ich gehe alle Pfeile aus dem Zustand ab und prüfe, ob jeder den Loop beendet. + tags: [planung, technik, fehlersuche] + level: 2 + - id: aufnahme-vorbereiten + module: feldaufnahme + q: Wie bereitest du eine Aufnahme für Spielgeräusche vor? + a: | + Ich prüfe vorher, ob das Gerät verlustfrei und ohne Filter aufnimmt, und mache einen Probe-Take für den Pegel. Ich nehme nah auf, sage jeden Take an und nehme mehrere Takes und Abstände auf, weil das Spiel Varianten braucht. Am Ende kommt Room Tone mit derselben Position und demselben Pegel. Ich führe ein Protokoll und benenne die Dateien nach dem Schema des Projekts. Stimmen Dritter nehme ich nicht auf. + tags: [aufnahme, feld] + level: 1 + - id: abstand-statt-pegel + module: feldaufnahme + q: Die Aufnahme hat zu viel Hintergrund. Hilft es, den Pegel herunterzudrehen? + a: | + Nein. Der Pegel ändert beide gleich, Klang und Hintergrund. Das Verhältnis ändert nur der Abstand: Jede Verdopplung des Abstands kostet bis zu 6 dB Vorsprung vor dem Hintergrund. Also näher ran, und Hall nehme ich möglichst trocken auf, denn im Spiel kann ich ihn dazugeben, aus der Datei nehmen kann ich ihn nicht. + tags: [aufnahme, feld, pegel] + level: 1 + - id: room-tone-wozu + module: feldaufnahme + q: Wozu nimmst du Room Tone auf? + a: | + Room Tone ist die Stille des Orts, mit derselben Position und demselben Pegel wie die Takes. Ich brauche ihn als Rauschprofil für die Rauschreduktion und als Material, um Lücken beim Schneiden zu füllen, ohne dass der Hintergrund springt. Dreißig Sekunden reichen meist, aufgenommen direkt nach den Takes. + tags: [aufnahme, feld, saeubern] + level: 1 + - id: nc-im-verkauf + module: quellen-lizenzen + q: Darfst du einen Klang mit CC BY-NC in einem Spiel verwenden, das du verkaufst? + a: | + Nein. Der Lizenztext definiert nicht kommerziell als „nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet“, und ein verkauftes Spiel ist auf Vergütung gerichtet. CC0 und CC BY passen. BY-SA und BY-ND kläre ich vorher: Die 4.0-Lizenzen werten die Vertonung von Bewegtbildern immer als Bearbeitung, SA verlangt dann dieselbe Lizenz, ND verbietet die Weitergabe. Ich bin kein Jurist; ich lese den Lizenztext und frage im Zweifel den Rechteinhaber. + tags: [lizenzen, creative-commons] + level: 1 + - id: namensnennung-tasl + module: quellen-lizenzen + q: Wie sieht eine korrekte Namensnennung für einen CC-BY-Klang aus? + a: | + Nach TASL: Titel, Autor mit dem Namen, den er gewählt hat, Quelle mit Link und Lizenz mit Version und Link zum Lizenztext, dazu, was ich geändert habe. „Klänge von Freesound“ reicht nicht, die Plattform ersetzt nicht die Person. Wo die Nennung im Spiel steht, entscheide ich; eine eigene Credits-Seite mit allen Einträgen ist üblich. + tags: [lizenzen, namensnennung] + level: 1 + - id: alle-klaenge-lizenziert + module: quellen-lizenzen + q: Kurz vor dem Release fragt die Produzentin, ob alle Klänge sauber lizenziert sind. Was antwortest du? + a: | + Ich zeige die Quellenliste. Jede Datei im Projekt hat eine Zeile mit Herkunft, Lizenz mit Version, Abrufdatum und Nachweis, auch die eigenen Aufnahmen. Ein Skript prüft vor jedem Build auf Lücken und auf NC oder ND. Fälle, die ein Lizenztext offen lässt, habe ich beim Hersteller angefragt; bis zur Antwort sind sie markiert und nicht im Build. + tags: [lizenzen, quellenliste] + level: 2 + - id: library-effekt + module: quellen-lizenzen + q: Darfst du einen einzelnen Effekt aus einer Kontakt-Library als WAV ins Spiel legen? + a: | + Das ist offen. Die EULA von Native Instruments erlaubt Samples für Musik- und Audioproduktionen und verbietet, einzelne Samples als Soundeffekte oder Library weiterzugeben. Ein Stück aus Kontakt ist klar eine Produktion. Ob ein einzelner gerenderter Effekt im Spiel als Produktion oder als weitergegebenes Sample zählt, beantwortet der Text nicht. Ich frage den Hersteller und lege die Antwort zu den Nachweisen. + tags: [lizenzen, libraries] + level: 3 + - id: treffer-wirkt-spaet + module: klangformung + q: Unser Trefferklang wird im richtigen Frame ausgelöst und wirkt trotzdem spät. Woran kann es liegen? + a: | + Erst die Datei: Ich messe, wie viel Stille vor der Transiente liegt, und rechne sie in Frames um; 40 ms sind bei 60 Frames pro Sekunde mehr als zwei Frames. Dann die Hüllkurve: Eine weiche Attack oder ein Kompressor mit schneller Attack nimmt der Transiente die Spitze. Dann die Länge: Eine lange Fahne verwischt den nächsten Treffer oder wird von der Stimmenbegrenzung abgeschnitten. Erst danach prüfe ich im Log den Auslöse-Frame. + tags: [klangformung, huellkurve, timing] + level: 2 + - id: mono-fuer-ort + module: klangformung + q: Warum lieferst du Klänge mit Ort in Mono? + a: | + Weil die Engine aus der Position das Kanalverhältnis macht. Godot 4.7.2 mischt Stereo-Dateien in `AudioStreamPlayer2D` nicht zu Mono, sondern gewichtet jeden Kanal nach der Position; was nur im linken Kanal der Datei liegt, bleibt links, auch wenn die Quelle rechts steht. Mono-Dateien verteilen sich dagegen nach der Position. Stereo bleibt für Bett und Musik, die keinen Ort haben. + tags: [klangformung, raum, godot] + level: 2 + - id: hall-trocken-oder-eingebacken + module: klangformung + q: Hall in die Datei oder im Spiel? + a: | + Trocken liefern und im Spiel auf einem Hall-Bus verhallen spart Dateilänge und folgt dem Ort: Derselbe Schritt klingt im Gang anders als im Freien. Eingebacken passt, wenn der Raum zum Klang gehört, etwa bei einem Stinger. In Godot leitet ein `Area2D` mit `audio_bus_override` auf einen Hall-Bus, aber nur, wenn `area_mask` des Players die Ebene enthält; der Standard ist 0. + tags: [klangformung, raum, godot] + level: 2 + - id: frequenz-rollen + module: klangformung + q: Wie verteilst du einen Klang auf die Frequenzbereiche? + a: | + Unten liegt Gewicht, in der Mitte die Identität, oben Detail. Hyper Light Drifter hat Schichten bewusst nach Bändern verteilt, die Wucht kam aus dem Bass. Dazu prüfe ich, was auf kleinen Lautsprechern übrig bleibt: Auf einem Handy fehlt der tiefe Bass, die Information muss dann in den Mitten stecken. + tags: [klangformung, frequenz] + level: 2 + - id: sechzig-varianten-neu + module: reaper-werkbank + q: Wir brauchen alle 60 Schritt-Varianten mit einem anderen Hochpass. Wie lange dauert das? + a: | + Kurz, wenn das Projekt so gebaut ist. Die Kette liegt auf der Spur, nicht in den Dateien; jede Variante ist eine Region mit ihrem Dateinamen; der Export ist ein Preset über die Region Render Matrix. Ich ändere den Filter einmal, höre drei Varianten im Spiel gegen, rendere alles neu, die Namen bleiben gleich, die Engine importiert neu. Steckt die Bearbeitung in sechzig einzeln exportierten Dateien, muss ich jede einzeln anfassen. + tags: [reaper, workflow] + level: 2 + - id: region-render-matrix + module: reaper-werkbank + q: Wozu nutzt du in REAPER Regionen und die Region Render Matrix? + a: | + Eine Region je Datei, ihr Name ist der Dateiname. In der Matrix lege ich fest, welche Region über welche Spur gerendert wird, und mit `$region` im Dateinamen entstehen alle Dateien in einem Durchgang. So bleibt das Rohmaterial unverändert, und jede neue Fassung überschreibt die alte unter demselben Namen, den das Spiel kennt. + tags: [reaper, render] + level: 1 + - id: neue-fassung-alte-im-spiel + module: reaper-werkbank + q: Du hast neu gerendert, aber im Spiel klingt noch die alte Fassung. Was prüfst du? + a: | + Den Dateinamen. Ist „Silently increment filenames“ an, legt REAPER eine neue Datei mit Nummer an, statt die alte zu überschreiben, und das Spiel spielt weiter die alte. Godot importiert neu, wenn sich der Inhalt einer Datei ändert, aber nur unter dem Namen, den die Szene kennt. Danach prüfe ich, ob der Render in den richtigen Ordner ging. + tags: [reaper, render, fehlersuche] + level: 2 + - id: hall-abgeschnitten + module: reaper-werkbank + q: Nach dem Render bricht der Hall jeder Datei hart ab. Woran liegt das? + a: | + Am Tail. Ohne Tail endet die Datei am Ende der Region, und ein Hall auf der Spur bricht dort ab. Entweder schalte ich Tail ein und aktiviere es in den Render-Einstellungen für Regionen, oder ich verlängere die Regionen bis zum Ende der Fahne. Vorher frage ich, ob der Hall überhaupt in die Datei gehört oder im Spiel auf einem Bus liegt. + tags: [reaper, render, fehlersuche] + level: 2 + - id: schritte-nerven + module: layering-varianten + q: Unsere Schritte nerven nach zwei Minuten. Was machst du? + a: | + Erst zählen, wie oft sie pro Minute spielen und wie viele Varianten es gibt, dann den Modus des Containers prüfen; mit zwei Varianten und Wiederholungsschutz entsteht ein starres A-B-A-B. Danach mehr echte Takes statt nur Tonhöhe, eventuell Schichten, die die Engine kombiniert. Jede Änderung höre ich im Spiel gegen die alte Fassung, möglichst blind, und ich höre mindestens so lange, wie es dauert, bis es nervt. + tags: [varianten, fehlersuche] + level: 2 + - id: no-repeats-modus + module: layering-varianten + q: Was ändert `PLAYBACK_RANDOM_NO_REPEATS` gegenüber `PLAYBACK_RANDOM`? + a: | + `PLAYBACK_RANDOM` wählt jedes Mal frei, dieselbe Variante kommt mit 1/n direkt wieder; mit vier Varianten waren es gemessen knapp 25 %. `PLAYBACK_RANDOM_NO_REPEATS` verhindert die direkte Wiederholung, dafür kommt A-B-A häufiger, mit 1/(n − 1). Mit nur zwei Varianten entsteht ein strenges A B A B, und das hört man auch. Der Standard in Godot 4.7.2 ist der Modus ohne Wiederholung. + tags: [varianten, godot] + level: 2 + - id: schichten-ausrichten + module: layering-varianten + q: Zwei Schichten klingen allein gut, zusammen dünn. Was prüfst du? + a: | + Die Ausrichtung. Wenige Millisekunden Versatz zwischen zwei ähnlichen Schichten löschen tiefe Frequenzen teilweise aus, wie ein Kammfilter. Ich richte die Schichten an der Transiente aus, in REAPER mit dem Snap Offset, und prüfe die Polarität. Danach höre ich die Summe, nicht die Schichten einzeln. + tags: [layering, phase] + level: 2 + - id: vorgerendert-oder-laufzeit + module: layering-varianten + q: Varianten vorrendern oder zur Laufzeit kombinieren? + a: | + Vorgerendert heißt: Jede Kombination ist geprüft, aber es entstehen viele Dateien. Zur Laufzeit kombiniert die Engine Schichten, etwa mit `AudioStreamSynchronized` und einem Randomizer je Schicht; das spart Dateien und ergibt mehr Kombinationen, aber nicht jede ist geprüft. Ich entscheide nach Häufigkeit und Speicher und höre beide Wege im Spiel gegeneinander. + tags: [varianten, godot] + level: 3 + - id: ambience-zwanzig-minuten + module: foley-umgebung + q: Wie baust du eine Ambience, die nach zwanzig Minuten nicht nervt? + a: | + Ich trenne Bett und Spot-Klänge. Aus dem Bett schneide ich alles Auffällige heraus, mache es mit getauschten Hälften und Crossfade nahtlos und halte es lang genug für die Verweildauer am Ort. Die herausgeschnittenen Stücke werden Spot-Klänge mit Varianten, die die Engine in zufälligen Pausen spielt, erst nach dem Ende des vorigen. Dann höre ich im Spiel so lange zu, wie ein Spieler dort bleibt. + tags: [ambience, loop] + level: 2 + - id: nahtloser-loop + module: foley-umgebung + q: Wie machst du aus einer Aufnahme einen nahtlosen Loop? + a: | + Ich teile sie an einer ruhigen Stelle etwa in der Mitte, tausche die Hälften und verbinde sie am neuen Stoß mit einem Crossfade. Danach liegen an der Naht des Loops zwei Samples, die in der Aufnahme nebeneinanderlagen; der alte Sprung sitzt in der Mitte unter dem Crossfade. Der Crossfade kürzt den Loop um seine Länge. Gerendert wird ohne Tail, geprüft in Schleife. + tags: [ambience, loop, reaper] + level: 2 + - id: spot-klaenge-ueberlappen + module: foley-umgebung + q: Deine Spot-Klänge spielen manchmal übereinander. Wie verhinderst du das? + a: | + Die zufällige Pause beginnt erst, wenn der vorige Klang zu Ende ist, also nach `finished`, nicht nach dem Start. Sonst überholt eine kurze Pause einen langen Klang. Mark Hollier beschreibt für Eon Altar genau dieses Problem und diese Lösung. + tags: [ambience, godot] + level: 2 + - id: horizontal-vertikal + module: musik-zustaende + q: Was ist der Unterschied zwischen horizontaler Neuordnung und vertikaler Schichtung? + a: | + Horizontal wechselt die Musik zwischen Abschnitten, je nach Zustand: Erkunden, dann ein Kampfteil. Vertikal laufen Schichten synchron, und der Zustand regelt, welche hörbar sind. Horizontal erlaubt große Wechsel, braucht aber Übergänge; vertikal reagiert sofort und bleibt im Takt, verlangt aber Schichten, die in jeder Kombination tragen. Viele Spiele mischen beides. + tags: [musik, interaktiv] + level: 1 + - id: kampfmusik-jederzeit + module: musik-zustaende + q: Die Musik soll auf Kämpfe reagieren, die jederzeit beginnen und enden können. Wie gehst du vor? + a: | + Erst klären, was die Musik dem Spieler sagen soll und was Geräusche schon sagen. Dann Zustände als Diagramm mit Auslösern, und für jeden Übergang Zeitpunkt, Ziel und Art. Wechsel auf den Takt klingen musikalisch, kommen aber bis zu einem Takt später; was sofort ankommen muss, trägt ein Stinger oder ein Geräusch. Gegen Flattern setze ich Hysterese oder Nachlauf. Getestet wird mit einem Prüfloop und einem Log. + tags: [musik, interaktiv, planung] + level: 2 + - id: naechster-takt-wartezeit + module: musik-zustaende + q: Wie lange kann ein Wechsel „auf den nächsten Takt“ dauern? + a: | + Bis zu einem ganzen Takt. Bei 120 BPM im 4/4 sind das bis zu 2 Sekunden oder 120 Frames bei 60 pro Sekunde. Für einen Kampfbeginn kann das zu spät sein. Dann wechsle ich auf den nächsten Schlag, setze einen Stinger darüber oder lasse ein Geräusch die Information tragen, bis die Musik folgt. + tags: [musik, uebergaenge, timing] + level: 2 + - id: hysterese-flattern + module: musik-zustaende + q: Die Musik springt am Rand eines Kampfgebiets ständig hin und her. Was fehlt? + a: | + Eine Regel gegen Flattern. Hysterese heißt zwei Schwellen: Der Kampf beginnt bei einem Abstand und endet erst bei einem größeren. Nachlauf heißt: Der Kampf endet erst, wenn sein Signal eine Zeit lang ununterbrochen fehlt. Beides kostet Reaktionszeit, deshalb rechne ich die Wartezeiten mit dem Übergang auf den Takt zusammen, bevor ich Werte festlege. + tags: [musik, zustaende, fehlersuche] + level: 2 + - id: stinger-wann + module: musik-zustaende + q: Wann setzt du einen Stinger ein? + a: | + Wenn ein Ereignis sofort markiert werden soll, die Musik aber nicht sofort wechseln kann oder soll, etwa ein Fund. Meist liegt der Stinger über der laufenden Musik; er kann sie auch kurz ersetzen, dann plane ich die Rückkehr mit. Liegt er darüber, muss er über allem tragen, was zu diesem Zeitpunkt laufen kann, denn ich weiß nicht, was gerade läuft; das prüfe ich gegen jeden Clip und jede Schichtkombination. + tags: [musik, stinger] + level: 2 + - id: adaptive-musik-liefern + module: loops-stems + q: Wie lieferst du Musik für ein adaptives System? + a: | + Tempo und Taktart stehen vorher fest, jeder Teil ist eine Region auf Taktgrenzen, die Länge in Samples vorher gerechnet. Loops rendere ich im 2nd pass, damit die Fahne am Anfang liegt, Stinger mit ausklingender Fahne. Stems haben gleichen Start und gleiche Länge. Zu jeder Datei gibt es eine Zeile mit Tempo, Schlägen, Taktart und Loop, denn nach dem Import steht in Godot `bpm` auf 0 und `loop` aus. Vor dem Einbau läuft ein Prüfloop aus Metronom-Klicks. + tags: [musik, loops, lieferung] + level: 2 + - id: loop-laenge-samples + module: loops-stems + q: Wie lang ist ein Loop von vier Takten im 4/4 bei 120 BPM bei 48 kHz? + a: | + Ein Schlag dauert 60/120 = 0,5 Sekunden, vier Takte sind 16 Schläge, also 8 Sekunden oder 384 000 Samples. Ich rechne das vor dem Rendern aus und prüfe die Datei danach. Ist sie länger, hört man es im Prüfloop als zwei hohe Klicks hintereinander am Loop-Punkt. + tags: [musik, loops, rechnen] + level: 1 + - id: loop-fahne + module: loops-stems + q: Am Loop-Punkt wird die Musik plötzlich trocken. Was ist passiert? + a: | + Die Hallfahne fehlt am Anfang der Datei. Beim ersten Durchlauf klingt am Anfang nichts nach, beim Loop müsste das Ende des vorigen Durchlaufs nachklingen. REAPERs 2nd pass render spielt den Bereich vor dem Rendern einmal ab, damit die Fahne in den Anfang der Datei kommt. Eine Fahne hinter dem Loop-Ende hilft nicht; Godot blendet sie hinter `beat_count` aus. + tags: [musik, loops, fehlersuche] + level: 2 + - id: stems-laufen-auseinander + module: loops-stems + q: Zwei Stems laufen im Spiel nach ein paar Durchläufen auseinander. Woran liegt es? + a: | + Sie sind nicht gleich lang. Ein Stem, der einen Schlag länger ist, wandert in `AudioStreamSynchronized` pro Durchlauf um diesen Schlag weiter. Stems brauchen gleichen Start, gleiche Länge und gleiche Fahnenbehandlung, und in Godot denselben `beat_count`. Ich prüfe die Längen in Samples, bevor ich sie importiere. + tags: [musik, stems, fehlersuche] + level: 2 + - id: boss-warnung-unhoerbar + module: mischung-stille + q: Im Bosskampf hört man die Warnung des Bosses nicht. Was tust du? + a: | + Ich nehme die Stelle auf und schreibe auf, was im Moment der Warnung gleichzeitig spielt. Dann prüfe ich die Prioritätentabelle: Hat die Warnung den Rang, den sie braucht, und hält sich der Mix daran? Danach Maskierung in der Lage und in der Zeit, etwa eine laute Transiente direkt davor. Erst nehme ich etwas weg, begrenze Stimmen oder ducke die Musik mit einer Regel; lauter drehe ich zuletzt. + tags: [mischung, prioritaet, fehlersuche] + level: 2 + - id: summenpegel + module: mischung-stille + q: Zwei gleich laute Klänge spielen gleichzeitig. Wie viel lauter wird die Summe? + a: | + Kommt derselbe Klang zweimal im selben Frame, addieren sich die Wellen: +6 dB, bei jeder weiteren Verdopplung wieder. Zwei verschiedene Klänge ergeben im Mittel etwa +3 dB, Spitzen können trotzdem bis +6 dB gehen. In Godot 4.7.2 habe ich genau das gemessen. Deshalb begrenze ich Stimmen, statt jeden Klang leiser zu machen. + tags: [mischung, pegel, rechnen] + level: 2 + - id: ducking-erklaeren + module: mischung-stille + q: Was ist Ducking, und wann setzt du es nicht ein? + a: | + Ducking senkt ein Signal ab, solange ein anderes spielt, meist über einen Kompressor mit Sidechain; in Godot hat `AudioEffectCompressor` dafür die Eigenschaft `sidechain` mit einem Busnamen. Ich setze es für wenige, begründete Regeln ein. Als Mittel gegen jeden Lautstärkestreit erzeugt es Pumpen und versteckt das eigentliche Problem, eine fehlende Priorität. Overwatch beschreibt genau so ein ungeplantes Ducking. + tags: [mischung, ducking] + level: 2 + - id: stille-als-werkzeug + module: mischung-stille + q: Wozu setzt du Stille im Mix ein? + a: | + Als Kontrast. Nach einer dünnen Stelle wirkt das Nächste lauter und wichtiger, ohne dass ich den Pegel ändere. Stille ist im Spiel selten Null: Meist fällt etwas weg, und anderes bleibt, etwa das Bett. Die Dichte folgt dem Rhythmus des Abschnitts, mit ruhigen Stellen vor den Momenten, die zählen. + tags: [mischung, stille] + level: 1 + - id: portfolio-entscheidung + module: werkstatt-audioabschnitt + q: Erzähl uns von einem Stück in deinem Portfolio. Was hast du entschieden und warum? + a: | + Ich zeige den Abschnitt im Spiel, dann eine Entscheidung aus dem Protokoll, mit Grund, verworfener Alternative und dem Test, der sie bestätigt hat. Ich spiele Fassung 1 und die letzte Fassung nacheinander vor und sage, welche Änderung welchen Unterschied macht. Ich nenne die Herkunft jeder fremden Datei mit Lizenz und sage, was ich selbst aufgenommen habe. + tags: [portfolio, dokumentation] + level: 2 + - id: entscheidungsprotokoll + module: werkstatt-audioabschnitt + q: Warum führst du ein Entscheidungsprotokoll? + a: | + Damit nachvollziehbar bleibt, warum ein Klang so ist, wie er ist. Jeder Eintrag nennt Entscheidung, Grund, verworfene Alternative, Test und Ergebnis. Wenn drei Wochen später jemand fragt, warum die Musik erst auf dem Takt wechselt, steht die Antwort da, mit Messung. Und ich sehe, welche Entscheidungen ich ohne Test getroffen habe. + tags: [dokumentation] + level: 1 + - id: kampfmusik-zu-spaet + module: godot-musik + q: Unsere Kampfmusik setzt manchmal Sekunden zu spät ein und manchmal gar nicht. Woran liegt es? + a: | + Ich lasse ein Log mit Frame der Anfrage und Frame des tatsächlichen Wechsels laufen und schneide den Ton mit. Wartet ein Takt-Wechsel bis zur Eins, ist „zu spät“ das Raster, kein Fehler, und die dringende Information gehört in ein Geräusch. Kommt der Wechsel gar nicht, prüfe ich, ob der Zustand kürzer ist als der Weg zum Takt und ob der Code die Anfrage zurücknimmt oder den laufenden Clip erneut anfragt. Dann setze ich einen Nachlauf und frage nur bei Änderungen an. + tags: [godot, interaktive-musik, fehlersuche] + level: 3 + - id: takt-wechsel-voraussetzung + module: godot-musik + q: Was braucht ein Wechsel „auf den nächsten Takt“ in Godots `AudioStreamInteractive`? + a: | + Eine Regel in der Übergangstabelle und Tempo, Schlagzahl und Schläge pro Takt aus dem Import: `bpm`, `beat_count` und `bar_beats`. Steht `bpm` auf 0, dem Standard nach dem Import, greift der Wechsel in Godot 4.7.2 sofort, ohne Fehlermeldung. Schlag und Takt rechnet Godot aus der Position im laufenden Clip. Ohne passende Regel wechselt er auf den nächsten Schlag mit einer Ausblendung über einen Schlag. + tags: [godot, interaktive-musik] + level: 2 + - id: clip-erneut-angefragt + module: godot-musik + q: Warum fragst du in Godot nie den Clip an, der schon läuft? + a: | + Weil Godot 4.7.2 das als neue Anfrage behandelt: Der laufende Clip verstummt bis zum Wechselpunkt und beginnt dort von vorn. Wer jeden Frame anfragt, bekommt Stille. Dasselbe passiert, wenn ich eine wartende Anfrage zurücknehme. Die Musikregie fragt deshalb nur an, wenn sich das Ziel ändert, und verlässt einen Zustand erst nach einem Nachlauf. + tags: [godot, interaktive-musik, fehlersuche] + level: 3 + - id: spiel-zu-laut + module: export-im-spiel + q: Spieler sagen, unser Spiel sei viel lauter als andere. Wie gehst du vor? + a: | + Ich messe nicht einzelne Dateien, sondern das Spiel. Ich schneide mindestens 30 Minuten aus repräsentativen Abschnitten mit und messe integrierte Lautheit und True Peak nach BS.1770. Als Bezug nenne ich die Empfehlung für die Plattform, etwa ASWG-R001 mit −24 LKFS für Heimkonsolen, und frage, ob wir uns daran halten wollen. Dann suche ich, wo die Lautheit herkommt: Musikbus, Effekte, Ducking, Limiter. Ich ändere eine Sache auf einmal und messe mit demselben Ablauf neu. + tags: [lautheit, mischung] + level: 2 + - id: lufs-statt-spitze + module: export-im-spiel + q: Warum gleichst du Klänge nach LUFS an und nicht nach dem Spitzenpegel? + a: | + Weil die Spitze wenig mit dem Eindruck zu tun hat. Ein Klick-Loop mit Spitze bei −7,1 dBFS maß −26,4 LUFS, ein Sinus mit Spitze bei −20 dBFS −20,0 LUFS: nach Spitze 13 dB lauter, nach Lautheit 6,4 LU leiser. LUFS nach BS.1770 messen die Energie mit Frequenzgewichtung und Gating. Für die Frage, ob etwas übersteuert, nehme ich True Peak, denn zwischen den Samples können höhere Werte entstehen. + tags: [lautheit, messen] + level: 2 + - id: mono-misst-anders + module: export-im-spiel + q: Eine Mono-Datei misst −23 LUFS, im Spielmitschnitt aber −20. Wer hat recht? + a: | + Beide. BS.1770 misst einen Kanal; ein `AudioStreamPlayer` spielt die Mono-Datei in Godot auf beiden Kanälen in voller Höhe, und zwei gleiche Kanäle messen 3 LU mehr. Deshalb messe ich Effekte so, wie sie im Spiel ankommen, oder gleiche den Unterschied beim Rendern aus; REAPER bietet dafür eine Mono-Korrektur um 3 dB. Für Klänge mit Ort gilt das nicht: Über `AudioStreamPlayer2D` in der Bildmitte maß dieselbe Datei −26,0 LUFS, 3 LU unter der Datei. + tags: [lautheit, godot] + level: 3 + - id: plugin-laeuft-nicht + q: Ein Plugin, auf das du dich verlässt, läuft auf dem Rechner des Projekts nicht. Was tust du? + a: | + Ich trenne Werkzeug und Ergebnis. Pflichtschritte baue ich mit Bordmitteln, die überall laufen, in REAPER etwa ReaEQ, ReaComp oder ReaFIR. Was nur mit dem Plugin geht, rendere ich an einem Rechner, auf dem es läuft, und lege das Ergebnis mit Vermerk ins Projekt. Den offenen Stand schreibe ich auf, statt einen Abend mit einer Brücke zu verlieren, die nicht stabil ist. + tags: [praxis, werkzeuge] + level: 1 + - id: generierter-klang + q: Ein Kollege schlägt vor, fehlende Klänge mit einem KI-Generator zu füllen. Was sagst du? + a: | + Für mein Projekt nein: Klänge, Musik und Mischentscheidungen sind meine Arbeit, und generiertes Material hat oft unklare Rechte. Als Platzhalter nehme ich einen eigenen Prüfklick, denn Platzhalter landen gern im Release. KI nutze ich für Technik, etwa ein Skript, das die Quellenliste prüft oder Dateien umbenennt. + tags: [praxis, ki] + level: 1 + - id: feedback-zum-klang + q: Die Lead Designerin findet deinen Klang „zu viel“. Wie gehst du damit um? + a: | + Ich frage, woran sie es festmacht: zu laut, zu lang, zu oft, verdeckt er etwas? Dann übersetze ich das in eine Größe, die ich ändern kann, und biete zwei Fassungen an, die sich nur darin unterscheiden. Wir hören beide im Spiel, nicht im Editor. Wenn sie danach bei ihrer Einschätzung bleibt, gilt ihre Entscheidung für das Spiel, und ich notiere sie im Protokoll. + tags: [praxis, zusammenarbeit] + level: 1 diff --git a/content/sound/modules/export-im-spiel.yaml b/content/sound/modules/export-im-spiel.yaml new file mode 100644 index 0000000..cbd632c --- /dev/null +++ b/content/sound/modules/export-im-spiel.yaml @@ -0,0 +1,624 @@ +id: export-im-spiel +title: "Lautheit, Export, Import und Hörtest im Spiel" +level: 3 +minutes: 150 +summary: Wie du Klänge und Musik nach Lautheit statt nach Spitzenpegel abstimmst, aus REAPER mit Normalisieren und Begrenzen renderst, in Godot über Busse mit Reglern und Ducking mischst und das Ergebnis im Spiel mit Mitschnitt, Messung und Hörprotokoll prüfst. +place: pc +goals: + - Du unterscheidest integrierte, kurzzeitige und momentane Lautheit und True Peak nach ITU-R BS.1770 und EBU R 128 und misst sie an einer Datei und an einem Mitschnitt aus dem Spiel. + - Du rechnest den Gain bis zu einem Platzhalter-Ziel und sagst vorher, ob danach ein Limiter greift. + - Du stellst die Nachbearbeitung im REAPER-Render-Dialog ein und weißt, was ein Neu-Rendern in Godot ändert und was nicht. + - Du legst Busse mit Reglern an und richtest Ducking mit `AudioEffectCompressor` und `sidechain` so ein, wie es in Godot 4.7.2 gemessen funktioniert. + - Du führst einen Hörtest im Spiel mit Protokoll auf verschiedenen Geräten und Lautstärken durch. +resources: + - title: ITU-R BS.1770 · Algorithms to measure audio programme loudness and true-peak audio level + url: https://www.itu.int/rec/R-REC-BS.1770 + - title: EBU R 128 (2023) · Loudness normalisation and permitted maximum level (PDF) + url: https://tech.ebu.ch/docs/r/r128.pdf + - title: Sony ASWG-R001 v1.10 · Average Loudness and Peak Levels (PDF, Kopie bei Game Audio Podcast) + url: https://www.gameaudiopodcast.com/ASWG-R001.pdf + - title: REAPER User Guide 7.81 · Render-Dialog und Postprocess (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + - title: Godot Docs · AudioEffectCompressor · sidechain + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-sidechain +lesson: | + Zwei Prüfsignale, beide auf beiden Kanälen: ein Loop aus Klicks mit seiner Spitze bei −7,1 dBFS und ein Sinuston von 1 kHz mit seiner Spitze bei −20,0 dBFS. Nach Spitzenpegel ist der Klick-Loop 13 dB lauter. Gemessen nach Lautheit ist es umgekehrt: Der Klick-Loop hat −26,4 LUFS, der Sinus −20,0 LUFS, 6,4 LU mehr. Wer nach Spitzenpegel normalisiert, stimmt Klänge also nach einer Zahl ab, die mit dem Eindruck wenig zu tun hat. + + Aus der Musik kennst du LUFS als Ziel für einen Master. Im Spiel gibt es diesen einen Master nicht. Jeder Klang kommt als eigene Datei, und erst die Engine mischt sie, je nach Situation anders. Dieses Modul misst Lautheit an Dateien und am Spiel, stellt den Export in REAPER ein, baut Busse, Regler und Ducking in Godot und endet mit einem Hörtest. Prioritäten und Bus-Plan hast du in [Mischung: Musik, Geräusche und Stille](/sound/mischung-stille) geplant; WAV oder Ogg, Busse und Dezibel stehen in [Audio zuordnen](/godot/audio). Alle Zielwerte hier sind Platzhalter für Rechnungen. Normen darf man nennen, entscheiden musst du, und jede Mischentscheidung bleibt deine. + + ## Lautheit messen: LUFS, LU und True Peak + + Die Messung ist genormt in [ITU-R BS.1770](https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-5-202311-I!!PDF-E.pdf#page=4), aktuell Version 5 von 2023. Der Algorithmus hat vier Stufen: eine Frequenzgewichtung „K“, das mittlere Quadrat je Kanal, eine gewichtete Summe der Kanäle und ein Gating über Blöcke von 400 ms, die sich zu 75 % überlappen. Das Gating hat zwei Schwellen, „the first at −70 LKFS“ und „the second at −10 dB relative to the level measured after application of the first threshold“. LKFS und LUFS sind dieselbe Einheit; die EBU schreibt LUFS. Ein **LU** ist ein Abstand, wie ein dB. + + Die EBU legt in [Tech 3341](https://tech.ebu.ch/docs/tech/tech3341.pdf#page=5) drei Zeitfenster fest, und REAPERs JS Loudness Meter zeigt sie so ([S. 120](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=120)): **momentan** (LUFS-M) über 400 ms, **kurzzeitig** (LUFS-S) über 3 s, **integriert** (LUFS-I) über die ganze Messung mit Gating. Dazu kommt **True Peak**, laut Guide der „highest level of the oversampled signal“. Die Spitze zwischen zwei Samples kann höher liegen als jedes Sample. Ein Prüfsignal zeigt das: ein Sinus auf einem Viertel der Abtastrate, dessen Samples nie auf der Spitze liegen. Rechnerisch liegt die Spitze der Welle bei −0,45 dBFS, 3,0 dB über den Samples. ffmpeg maß Sample Peak −3,5 dBFS und True Peak +0,2 dBTP; sein Schätzer zeigt hier also 0,65 dB mehr als die Rechnung. + + Ohne REAPER misst `ffmpeg` dasselbe ([ebur128](https://ffmpeg.org/ffmpeg-filters.html#ebur128)). Für den Klick-Loop vom Anfang: + + ```console + $ ffmpeg -nostats -i pruefloop_lr.wav -af ebur128=peak=true -f null - + … + Integrated loudness: + I: -26.4 LUFS + Threshold: -36.4 LUFS + … + True peak: + Peak: -7.1 dBFS + ``` + + Das Gating erklärt, warum ein Spiel mit vielen Pausen nicht leiser misst. Ein Modell mit Blockwerten statt Audio, nach den Formeln der Norm: + + ```python run + import math + + + def integriert(bloecke): + """Integrierte Lautheit aus Blockwerten (je 400 ms) mit dem Gating von ITU-R BS.1770.""" + def mittel(werte): + return 10 * math.log10(sum(10 ** (l / 10) for l in werte) / len(werte)) + ueber_absolut = [l for l in bloecke if l > -70] + relativ = mittel(ueber_absolut) - 10 + return round(mittel([l for l in ueber_absolut if l > relativ]), 1) + + + kampf = [-20] * 50 + [-10] * 50 + print(integriert(kampf)) + print(integriert(kampf + [-80] * 100)) # Pausen + print(integriert(kampf + [-28] * 100)) # sehr leise Atmo + print(integriert(kampf + [-22] * 100)) # leise Atmo + ``` + + Halb −20, halb −10 LUFS ergibt −12,6 LUFS, nicht −15: Gemittelt wird die Energie, die lauten Blöcke zählen mehr. Pausen unter −70 fallen weg. Die Atmo bei −28 liegt unter der relativen Schwelle und fällt auch weg, die bei −22 nicht, und dann sinkt der Wert. Bei einem einzelnen Klick wirkt dasselbe: Ein Klick in einer Datei von 0,5 s und in einer von 2 s maß beide Male −24,8 LUFS. + + ### Mono-Dateien: im Spiel lauter oder leiser + + Die Norm misst einen Sinus von 1 kHz und 0 dBFS in einem Kanal mit −3,01 LKFS (BS.1770-5, S. 9; genau gemeint sind 997 Hz). Eine Mono-Datei mit einem Sinus von 997 Hz bei −20 dBFS misst deshalb −23,0 LUFS, dieselbe Welle in beiden Kanälen −20,0 LUFS. Im Spiel entscheidet der Player, gemessen mit Godot 4.7.2 am Mitschnitt des Masters: + + | Wiedergabe | je Kanal | gemessen | + | --- | --- | --- | + | Datei, mono | – | −23,0 LUFS | + | `AudioStreamPlayer` | volle Höhe | −20,0 LUFS | + | `AudioStreamPlayer2D` in der Bildschirmmitte | halbe Amplitude, −6 dB | −26,0 LUFS | + + Über `AudioStreamPlayer` misst der Mitschnitt 3 LU mehr als die Datei, über `AudioStreamPlayer2D` in der Bildschirmmitte 3 LU weniger. Weiter weg wird der 2D-Player leiser und wandert zur Seite ([Audio zuordnen](/godot/audio)). Miss Effekte also so, wie sie im Spiel ankommen, Klänge mit Ort an der Stelle, an der sie klingen. Für Mono-Dateien ohne Ort bietet REAPER in der Render-Nachbearbeitung „Adjust mono files by +3dB/-3dB“ (Guide S. 414, siehe unten). + + ## Referenzwerte: Normen als Startpunkt, nicht als Vorgabe + + [EBU R 128](https://tech.ebu.ch/docs/r/r128.pdf#page=3) normalisiert Fernsehprogramme auf −23,0 LUFS und erlaubt höchstens −1 dBTP. Gemessen wird das Programm „in its entirety, without emphasis on specific foreground elements such as speech, music or sound effects“ ([S. 4](https://tech.ebu.ch/docs/r/r128.pdf#page=4)). Für Spiele hat Sonys Audio Standards Working Group 2013 die Empfehlung [ASWG-R001](https://www.gameaudiopodcast.com/ASWG-R001.pdf#page=6) in Version 1.10 veröffentlicht. Sie nennt für Titel auf Heimkonsolen −24 (±2) LKFS, für tragbare Geräte −18 (±2) LKFS und höchstens −1 dBTP. Gemessen wird der ganze Inhalt, „for a minimum of 30 minutes“, und zwar ein repräsentativer Querschnitt des Spiels. Version 1.10 hat den Wert für Heimkonsolen von −23 auf −24 gesenkt, passend zu ATSC und ARIB. Das PDF liegt als Kopie bei Game Audio Podcast, nicht bei Sony. + + Zwei Folgen für deine Arbeit. Erstens messen beide Werke das ganze Programm, nicht einzelne Dateien. Ein Zielwert für eine Effekt-Datei ist deshalb eine eigene Festlegung, ein Platzhalter, mit dem du Kategorien gegeneinander abstimmst. Ob das Spiel als Ganzes passt, zeigt erst ein langer Mitschnitt. Zweitens sind beides keine Pflichten für ein PC-Spiel. Sie sagen dir, wo andere Inhalte liegen, neben denen deins läuft. + + > [!PRAXIS] Wie Sony gemessen hat + > Garry Taylor, damals Audio Director bei Sony Computer Entertainment Europe, beschreibt 2012 die Arbeit an den Empfehlungen ([Designing Sound](https://designingsound.org/2012/07/30/video-games-and-loudness-standards-interview-with-sonys-garry-taylor/)). Die Gruppe hatte „over 120 games, films, TV shows and trailers“ gemessen. Für ein Spiel empfahl er, das Ausgangssignal in eine DAW zu führen und „for a minimum of 30 minutes“ zu messen. Als Schwierigkeit nennt er „reproducibility and consistency when measuring non-linear material“. Für deine Testszene hast du dafür ein Werkzeug: Das Drehbuch aus der [Combat-Testszene](/combat/testszene-angriff) spielt jeden Lauf gleich ab, also misst du jede Fassung unter gleichen Bedingungen. + + ## Normalisieren und Begrenzen in REAPER + + Im Render-Dialog (*File → Render…*) öffnet *Postprocess…* die Nachbearbeitung. Laut Guide normalisiert sie auf „RMS-I, LUFS-I, True peak, Peak, LUFS-M max or LUFS-S max“ und begrenzt mit „Brickwall limit to Peak or True peak“. Bei mehreren Dateien wählst du „Normalize each file separately, to loudest file, or as if files play together“ ([S. 414](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414)). Dort sitzen auch Stille-Trimmen und Fades. Was die Wahl bedeutet: + + | Einstellung | Wirkung | + | --- | --- | + | `each file separately` | jede Datei für sich aufs Ziel, Unterschiede verschwinden | + | `to loudest file` | lauteste aufs Ziel, Abstände bleiben | + | `as if files play together` | alle zusammen gemessen, Abstände bleiben | + + Für Varianten eines Klangs ist „separately“ oft gewollt, denn sie sollen gleich laut sein ([Layering und Varianten](/sound/layering-varianten)). Für Stems einer Musik zerstört es die Balance ([Eigenes Stück aufbereiten](/sound/loops-stems)). Ob Ziel und Decke nach der Nachbearbeitung stimmen, prüfst du ohne Datei mit *Dry Run (no output)*; das Fenster mit den Render-Statistiken zeigt danach Lautheit, Spitzen und Übersteuerungen (Guide S. 415 f.). + + Bevor du ein Ziel einträgst, misst du die Datei mit dem JS Loudness Meter und rechnest, was passiert. Gain ist Ziel minus Messwert, und um genau diesen Gain steigt auch der True Peak: + + ```python run + lufs_i = -26.4 # Klick-Loop, gemessen + true_peak = -7.1 # gemessen + ziel = -18.0 # Platzhalter + decke = -1.0 # Platzhalter für True Peak + + gain = ziel - lufs_i + spitze = true_peak + gain + print(f"Gain {gain:+.1f} dB, True Peak danach {spitze:+.1f} dBTP") + print("Limiter greift" if spitze > decke else "Limiter greift nicht") + ``` + + Mit ffmpeg nachgemessen: Nach +8,4 dB hatte der Klick-Loop −18,0 LUFS und +1,3 dBTP. Ein Limiter mit der Platzhalter-Decke von −1 dB (`alimiter` in ffmpeg) hielt die Spitzen bei −1,0 dBTP und senkte die Lautheit auf −19,8 LUFS. Bei spitzen Klängen kostet der Limiter also Lautheit. Miss nach dem Rendern deshalb noch einmal und entscheide, ob du das Ziel, die Decke oder den Klang änderst. + + ## Formate, Abtastrate und Neu-Rendern + + WAV für kurze Effekte, Ogg für Musik, das steht in [Audio zuordnen](/godot/audio). Für die Abtastrate gilt: Godot mischt standardmäßig mit 44,1 kHz (`audio/driver/mix_rate`, gemessen 44100), die Doku rät, den Wert dem System zu überlassen ([mix_rate](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-audio-driver-mix-rate)). Dateien mit anderer Rate rechnet Godot um. Eine Datei mit 48 kHz und einem Ton von 1000 Hz über 2 s lief bei 44,1 kHz gemessen genau 2,000 s mit 1000,0 Hz. Was auf deinem Rechner gilt, zeigt `AudioServer.get_mix_rate()`. Der Movie Maker mischt dagegen mit 48 kHz. Über 48 kHz bringt laut Import-Doku nur etwas, wenn du Klänge zur Laufzeit verlangsamst (Best practices, siehe Übung zur Mischrate). Was Godot beim Import aus Bittiefe und Rate macht, steht in [REAPER als Werkbank](/sound/reaper-werkbank). + + Renderst du in den Projektordner, ersetzt jedes Neu-Rendern die Datei. Godot importiert neu, sobald sich die Prüfsumme der Quelle ändert, „applying the preset configured for that specific asset“ ([Automatic reimport](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport)). Die Einstellungen in der `.import`-Datei bleiben also stehen, auch wenn sie nicht mehr passen. Gemessen: Ein Loop mit vier Takten wurde unter gleichem Namen mit zwei Takten neu gerendert. Die `.import`-Datei blieb Byte für Byte gleich, mit `beat_count` 16. Ein Übergang „Clip-Ende“, angefragt bei 5,0 s, kam danach erst bei 12,0 s statt bei 8,0 s. Umgekehrt beim Umbenennen im Dateimanager: Die Datei unter neuem Namen bekam neue Standardwerte (`loop` aus, `bpm` 0) und eine neue UID, die alte `.import`-Datei blieb liegen. Ordner und Namen legst du einmal fest, als Platzhalter etwa so: + + ```text + res://audio/musik/ Ogg, mit .import-Dateien + res://audio/sfx/ WAV + res://audio/atmo/ WAV oder Ogg + ~/projekte/sound/ REAPER-Projekte und Rohaufnahmen, nicht im Spiel + ``` + + Die `.import`-Dateien gehören laut derselben Seite in die Versionsverwaltung, der Ordner `.godot/` nicht. + + ## Busse, Regler und Ducking in Godot + + Den Bus-Plan aus der Mischung setzt du im Panel *Audio* um. Mit den Platzhalter-Namen Musik, SFX, Atmo und UI und einem Kompressor auf `Musik` speichert Godot das Layout so (gekürzt): + + ```ini title=default_bus_layout.tres + [sub_resource type="AudioEffectCompressor" id="AudioEffectCompressor_hr3ii"] + threshold = -20.0 + sidechain = &"SFX" + + [resource] + bus/1/name = &"Musik" + bus/1/send = &"Master" + bus/1/effect/0/effect = SubResource("AudioEffectCompressor_hr3ii") + bus/1/effect/0/enabled = true + bus/2/name = &"SFX" + bus/2/send = &"Master" + bus/3/name = &"Atmo" + bus/3/send = &"Master" + bus/4/name = &"UI" + bus/4/send = &"Master" + ``` + + Ein Regler im Menü setzt die Lautstärke eines Busses. `set_bus_volume_linear()` nimmt einen Wert von 0 bis 1 und rechnet in dB um: + + ```gdscript title=bus_regler.gd + extends HSlider + + ## Lautstärkeregler für einen Bus. Wert 0 bis 1, linear in der Amplitude. + + @export var bus_name := &"Musik" + + @onready var _bus := AudioServer.get_bus_index(bus_name) + + + func _ready() -> void: + min_value = 0.0 + max_value = 1.0 + step = 0.01 + value = AudioServer.get_bus_volume_linear(_bus) + value_changed.connect(_on_value_changed) + + + func _on_value_changed(v: float) -> void: + AudioServer.set_bus_volume_linear(_bus, v) + ``` + + Gemessen: 0,5 ergibt −6,02 dB, 0,1 ergibt −20,00 dB, 0 ergibt `-inf`. Die untere Hälfte des Reglers deckt damit alles unter −6 dB ab. Ob sich das für Spieler gleichmäßig anfühlt, ist eine Frage für deinen Hörtest. Beim Namen zählen Groß- und Kleinschreibung: `get_bus_index(&"musik")` gab −1 zurück, und der Regler meldete danach „Index p_bus = -1 is out of bounds“. + + ### Ducking: gemessen statt gerechnet + + **Ducking** heißt: Ein Bus wird leiser, solange ein anderer klingt. In Godot hängt dafür ein `AudioEffectCompressor` am leiser werdenden Bus, und `sidechain` nennt den Bus, auf den er hört ([sidechain](https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-sidechain)). Der Messaufbau: Musik als Dauerton im linken Kanal auf `Musik`, ein Effekt als Ton von 0,5 s im rechten Kanal auf `SFX` mit −6 dBFS, Kompressor mit `threshold` −20 dB, `ratio` 4 und den Standardwerten 20 µs Attack und 250 ms Release. Aus der Musikproduktion erwartest du 14 dB über der Schwelle mal (1 − 1/4), also 10,5 dB Absenkung. Gemessen: + + | Zeitpunkt | Musik | + | --- | --- | + | 20 ms nach Beginn des Effekts | −21,8 dB | + | während des Effekts | −21,8 dB | + | 0,10 s nach seinem Ende | −13,8 dB | + | 0,25 s nach seinem Ende | −7,6 dB | + | 0,50 s nach seinem Ende | −2,8 dB | + | 1,00 s nach seinem Ende | −0,4 dB | + + Doppelt so viel. Der Quelltext multipliziert die Überschreitung mit 2,08 ([audio_effect_compressor.cpp, 4.7.2](https://github.com/godotengine/godot/blob/4.7.2-stable/servers/audio/effects/audio_effect_compressor.cpp)). Und „Release 250 ms“ heißt nicht, dass die Musik nach 250 ms zurück ist: Nach einer Sekunde fehlten noch 0,4 dB. Stell Ducking deshalb nach Messung und Gehör ein, nicht nach den Zahlen im Inspektor. + + Dazu kommt die Reihenfolge der Busse. Godot bearbeitet sie von rechts nach links, also von der höchsten Nummer zur niedrigsten (Quelltext `audio_server.cpp` im selben Stand 4.7.2). Der Kompressor liest den Sidechain-Bus in dem Zustand, in dem er gerade ist. Gemessen mit demselben Aufbau: + + | SFX-Bus | Name | SFX | Musik | + | --- | --- | --- | --- | + | rechts von Musik | richtig | normal | −21,8 dB | + | rechts von Musik | richtig | stumm | 0 dB | + | rechts von Musik | richtig | −80 dB | 0 dB | + | links von Musik | richtig | stumm | −21,8 dB | + | links von Musik | falsch | normal | 0 dB | + | rechts von Musik | falsch | normal | −21,8 dB | + + Steht der Sidechain-Bus rechts, ist er schon fertig gemischt, mit Fader und Mute. Ein stummer Auslöser-Bus wirkt dann nicht. Steht er links, liest der Kompressor ihn vor Fader und Mute. Ein falscher Name ist am tückischsten: Godot meldet nichts und nimmt statt des Busses den Master. Dort liegt bereits alles, was rechts von `Musik` fertig ist. Im Messlauf mit falschem Namen duckte die Musik deshalb um 21,8 dB, auch als nur die Atmo einen Ton spielte. + + > [!WARNING] Ein Ducking, das funktioniert, kann trotzdem falsch sein + > Mit einem falschen Sidechain-Namen und dem SFX-Bus rechts von `Musik` duckt die Musik wie geplant, aber auch bei allem anderen, was rechts von `Musik` liegt; gemessen mit der Atmo. Prüf deshalb jede Regel mit einem Gegentest: Spielt nur ein Bus, der nicht auslösen soll, darf die Musik nicht leiser werden. + + ## Mitschnitt und Hörtest im Spiel + + ### Messen am Master + + Gemessen wird am Ende das, was der Spieler hört: der Master-Bus. Ein `AudioEffectRecord` nimmt ihn auf, `get_recording()` liefert danach ein `AudioStreamWAV`. Dieses Testgerüst lief so mit Godot 4.7.2: + + ```gdscript title=mitschnitt.gd + extends Node + + ## Testgerüst: nimmt den Master-Bus auf und speichert nach „sekunden“ eine WAV-Datei. + + @export var sekunden := 60.0 + @export var datei := "user://mitschnitt.wav" + + var _rec := AudioEffectRecord.new() + var _start := 0 + + + func _ready() -> void: + AudioServer.add_bus_effect(0, _rec) + _rec.set_recording_active(true) + _start = Time.get_ticks_msec() + + + func _process(_delta: float) -> void: + if _rec.is_recording_active() and Time.get_ticks_msec() - _start > sekunden * 1000: + _rec.set_recording_active(false) + _rec.get_recording().save_to_wav(datei) + print("Mitschnitt gespeichert: ", ProjectSettings.globalize_path(datei)) + ``` + + Den Mitschnitt misst du mit dem JS Loudness Meter in REAPER oder mit ffmpeg. So entsteht die Zahl, die eine Norm meint: die Lautheit des Spiels in einem Abschnitt, nicht die einer Datei. Für einen Vergleich zwischen Fassungen läuft derselbe Abschnitt mit dem Drehbuch. + + ### Hörtest mit Protokoll + + Messwerte sagen, wie laut etwas ist, nicht, ob man es hört. Dafür gibt es den Hörtest. Spiel denselben Abschnitt auf mindestens zwei Wiedergabegeräten, die du hast, etwa Kopfhörern und eingebauten Lautsprechern, und mit mindestens zwei Systemlautstärken, darunter eine leise. Leise hören zeigt, welche Klänge zuerst verschwinden. Prüf dabei die Prioritäten aus deinem Plan: Ist ein Warnklang, der Vorrang haben soll, über der Musik zu hören? Pumpt das Ducking hörbar? Bleibt eine geplante Stille leer? Ein Protokoll hält das fest, als Platzhalter-Spalten: + + ```csv + datum,fassung,geraet,systemlautstaerke,abschnitt,lufs_i,beobachtung,offene_frage + ,,kopfhoerer,leise,,,, + ``` + + Die spitzen Klammern sind Platzhalter für deine Einträge. Wichtig ist die Trennung: Beobachtung ohne Bewertung, Messwert aus dem Mitschnitt, offene Frage statt schneller Lösung. Eine Änderung testest du einzeln, wie in [Tuning als Experiment](/combat/tuning). + + > [!INTERVIEW] + > „Spieler sagen, unser Spiel sei viel lauter als andere. Wie gehst du vor?“ Gute Antwort, etwa als Audio Designer: Ich messe nicht einzelne Dateien, sondern das Spiel. Ich schneide mindestens 30 Minuten aus repräsentativen Abschnitten mit und messe integrierte Lautheit und True Peak nach BS.1770. Als Bezug nenne ich die Empfehlung, die für die Plattform gilt, etwa ASWG-R001 mit −24 LKFS für Heimkonsolen, und frage, ob wir uns daran halten wollen. Dann suche ich, wo die Lautheit herkommt: Musikbus, Effekte, Ducking, Limiter. Erst danach ändere ich, eine Sache auf einmal, und messe wieder mit demselben Ablauf. + + ## Häufige Fehler + + - **Nach Spitze normalisiert.** Ein Klick-Loop mit −7,1 dBFS war 6,4 LU leiser als ein Sinus mit −20 dBFS. + - **Mono-Datei gemessen, im Spiel gehört.** Über `AudioStreamPlayer` 3 LU mehr, über `AudioStreamPlayer2D` in der Bildschirmmitte 3 LU weniger. + - **Gain ohne Blick auf True Peak.** Der Gain hebt die Spitzen genauso; danach greift der Limiter und kostet Lautheit. + - **Neu gerendert, Import vergessen.** `bpm` und `beat_count` bleiben stehen, Übergänge laufen falsch. + - **Im Dateimanager umbenannt.** Neue Standardwerte, neue UID. + - **Sidechain-Name falsch.** Kein Fehler, der Kompressor hört auf den Master. + - **Auslöser-Bus stumm geschaltet und rechts von Musik.** Kein Ducking. + - **Release als Rückkehrzeit gelesen.** 250 ms Release brauchten gemessen rund eine Sekunde bis fast 0 dB. + + ## Zusammenfassung + + - LUFS nach BS.1770 messen Energie mit Gating; momentan 0,4 s, kurzzeitig 3 s, integriert über alles. True Peak misst zwischen den Samples. + - Normen (EBU R 128, ASWG-R001) messen das ganze Programm. Ziele für Dateien sind deine Platzhalter; ob das Spiel passt, zeigt ein langer Mitschnitt. + - REAPERs Postprocess normalisiert auf LUFS-I, True Peak und andere Maße und begrenzt auf Peak oder True Peak; einzeln, zur lautesten Datei oder gemeinsam. + - Gain ist Ziel minus Messwert, der True Peak steigt mit. Danach neu messen. + - Godot mischt standardmäßig mit 44,1 kHz und rechnet andere Raten um. Neu-Rendern behält die Import-Werte, Umbenennen im Dateimanager verliert sie. + - Ducking in Godot 4.7.2: Absenkung etwa doppelt so stark wie die Lehrbuchformel, Rückkehr deutlich länger als der Release-Wert, Bus-Reihenfolge und Name entscheiden, was der Kompressor hört. + - Hörtest mit Protokoll: mehrere Geräte, leise und normal, Beobachtung, Messwert, offene Frage. +exercises: + - id: welches-mass + type: choice + title: Welche Zahl beantwortet die Frage? + prompt: | + Du willst wissen, ob ein Treffer-Effekt nach dem Normalisieren übersteuern kann, wenn Godot ihn auf 44,1 kHz umrechnet. Welche Messung sagt dir das am ehesten? + options: + - text: True Peak + correct: true + why: True Peak schätzt die Spitze zwischen den Samples, also auch, was nach dem Umrechnen auftauchen kann. REAPERs Guide beschreibt ihn genau so. + - text: Sample Peak + correct: false + why: Er sieht nur die gespeicherten Samples. Bei einem Prüfsignal lagen sie rechnerisch 3,0 dB unter der Spitze der Welle; ffmpeg maß 3,7 dB Abstand zum True Peak. + - text: LUFS-I + correct: false + why: Die integrierte Lautheit sagt, wie laut der Klang wirkt, nicht, wie hoch seine Spitze ist. Der Klick-Loop hatte −26,4 LUFS bei −7,1 dBTP. + - text: LUFS-S max + correct: false + why: Das ist die höchste Lautheit über 3 Sekunden, für einen kurzen Effekt ohnehin wenig aussagekräftig und kein Spitzenmaß. + explanation: | + Lautheit und Spitze sind verschiedene Fragen. Ob etwas übersteuert, ist eine Frage der Spitze, und weil beim Umrechnen und Filtern zwischen den Samples höhere Werte entstehen können, ist True Peak das passende Maß. ASWG-R001 nennt als Grund für Headroom ausdrücklich Resampling und Filtern in der Engine und setzt die Grenze bei −1 dBTP. Für den Eindruck „wie laut“ nimmst du dagegen LUFS. + resources: + - title: REAPER User Guide 7.81 · JS Analysis Loudness Meter (S. 120) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=120 + - title: Sony ASWG-R001 v1.10 (S. 6) + url: https://www.gameaudiopodcast.com/ASWG-R001.pdf#page=6 + + - id: vom-render-zum-hoertest + type: order + title: Von der Region bis zum Hörtest + prompt: | + Bring die Schritte für einen neuen Effekt in eine sinnvolle Reihenfolge. + lang: prose + lines: | + Den Effekt mit dem JS Loudness Meter messen, den Gain bis zum Platzhalter-Ziel rechnen und prüfen, ob der True Peak danach über der Decke liegt + Im Render-Dialog unter Postprocess Normalisieren und Begrenzen einstellen + Mit Dry Run prüfen, ob Ziel und Decke nach dem Postprocess getroffen sind + In den Projektordner rendern, Godot importiert neu + Im Import-Dock prüfen, ob die Werte zur neuen Datei passen + Den Abschnitt mit dem Drehbuch spielen und den Master mitschneiden + Mitschnitt messen und den Hörtest protokollieren + explanation: | + Erst messen und rechnen, dann einstellen: So weißt du vorher, ob der Limiter eingreift. Der Dry Run zeigt danach, ob die Nachbearbeitung Ziel und Decke trifft, ohne eine Datei zu schreiben. Nach dem Rendern importiert Godot neu, behält aber die alten Import-Werte; deshalb der Blick ins Import-Dock. Gemessen und gehört wird am Ende im Spiel, mit einem wiederholbaren Lauf, sonst vergleichst du verschiedene Situationen. + resources: + - title: REAPER User Guide 7.81 · Dry Run Render Stats (S. 416) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=416 + + - id: mischrate + type: choice + title: 44,1 oder 48 kHz? + prompt: | + Welche Aussagen zu Abtastraten in Godot 4.7.2 stimmen nach Messung und Doku? Mehrere Antworten richtig. + options: + - text: Ohne Änderung steht `audio/driver/mix_rate` auf 44100. + correct: true + why: Gemessen mit `ProjectSettings.get_setting()`. Die tatsächliche Rate meldet `AudioServer.get_mix_rate()`. + - text: Eine Datei mit 48 kHz spielt bei 44,1 kHz Mischrate um rund 9 % zu tief. + correct: false + why: Godot rechnet um. Ein 1000-Hz-Ton aus einer 48-kHz-Datei lief gemessen mit 1000,0 Hz und genau 2,000 s. + - text: Der Movie Maker schreibt seine WAV-Datei standardmäßig mit 48 kHz. + correct: true + why: "`editor/movie_writer/mix_rate` ist 48000; im Movie-Maker-Modus meldete `AudioServer.get_mix_rate()` 48000." + - text: Die Doku empfiehlt 96 kHz für alle Effekte, damit Tonhöhenänderungen sauber bleiben. + correct: false + why: Sie sagt, über 48 kHz bringe nur etwas, wenn du Klänge zur Laufzeit verlangsamst. Eine Pflicht zu 96 kHz steht dort nicht. + explanation: | + Die Mischrate ist die Rate, mit der Godot alles zusammenrechnet; Dateien mit anderer Rate rechnet es dabei um, ohne Tonhöhe oder Länge zu ändern. Welche Rate du renderst, ist eine Abwägung aus Größe, Bearbeitung und Laufzeit-Effekten. Für Messungen merkst du dir: Ein Mitschnitt aus dem Movie Maker hat 48 kHz, einer aus `AudioEffectRecord` die Mischrate des Spiels. + resources: + - title: Godot Docs · ProjectSettings audio/driver/mix_rate + url: https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-audio-driver-mix-rate + - title: Godot Docs · Importing audio samples, Best practices + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#use-appropriate-quality-settings + + - id: gain-bis-ziel + type: output + title: Greift der Limiter? + prompt: | + Eine Effekt-Datei misst −23,7 LUFS-I und −4,2 dBTP. Das Ziel ist ein Platzhalter von −16 LUFS-I, die Decke −1 dBTP. Was gibt das Programm aus? + lang: python + code: | + lufs_i = -23.7 + true_peak = -4.2 + ziel = -16.0 + decke = -1.0 + + gain = ziel - lufs_i + spitze = true_peak + gain + print(f"Gain {gain:+.1f} dB") + print(f"True Peak danach {spitze:+.1f} dBTP") + print("Limiter greift" if spitze > decke else "Limiter greift nicht") + expected: | + Gain +7.7 dB + True Peak danach +3.5 dBTP + Limiter greift + hints: + - Gain ist Ziel minus Messwert. + - Der True Peak steigt um denselben Gain. + explanation: | + Bis −16 fehlen 7,7 dB. Um genau diese 7,7 dB steigt auch der True Peak, von −4,2 auf +3,5 dBTP, 4,5 dB über der Decke. Der Limiter muss also kräftig arbeiten, und danach misst die Datei leiser als das Ziel; beim Klick-Loop kostete ein Limiter mit −1 dB Decke gemessen 1,8 LU. Ob du das hinnimmst, das Ziel senkst oder den Klang änderst, ist deine Entscheidung. Die Rechnung sagt dir nur vorher, dass du sie treffen musst. + resources: + - title: REAPER User Guide 7.81 · Render-Dialog, Postprocess (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: gating-leise + type: output + title: Leiser und doch lauter? + prompt: | + Das Gating-Modell aus der Lektion. Ein Abschnitt hat 30 Blöcke mit −18 LUFS, 30 leise Blöcke und 40 Blöcke Pause. Einmal liegen die leisen Blöcke bei −30, einmal bei −32 LUFS. Was gibt das Programm aus? + lang: python + code: | + import math + + + def integriert(bloecke): + def mittel(werte): + return 10 * math.log10(sum(10 ** (l / 10) for l in werte) / len(werte)) + ueber_absolut = [l for l in bloecke if l > -70] + relativ = mittel(ueber_absolut) - 10 + return round(mittel([l for l in ueber_absolut if l > relativ]), 1) + + + print(integriert([-18] * 30 + [-30] * 30 + [-90] * 40)) + print(integriert([-18] * 30 + [-32] * 30 + [-90] * 40)) + expected: | + -20.7 + -18.0 + hints: + - Die Pausen fallen am absoluten Gate weg. + - Die relative Schwelle liegt 10 LU unter dem Mittel der übrigen Blöcke, im ersten Fall bei rund −30,7. + explanation: | + Ohne die Pausen mitteln die übrigen 60 Blöcke im ersten Fall auf etwa −20,7 LUFS, die relative Schwelle liegt also bei rund −30,7. Blöcke bei −30 liegen knapp darüber und zählen mit: −20,7 LUFS. Im zweiten Fall liegt die Schwelle bei rund −30,8, Blöcke bei −32 liegen darunter und fallen weg, übrig bleiben nur die −18. So wird ein Abschnitt mit leiserer Atmo in der Messung lauter. Für dich heißt das: Die integrierte Lautheit sagt etwas über den Vordergrund, nicht darüber, wie leise die ruhigen Stellen sind. Das zeigen dir die kurzzeitigen Werte (LUFS-S) und dein Ohr. + resources: + - title: ITU-R BS.1770-5 · Gating (S. 4) + url: https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-5-202311-I!!PDF-E.pdf#page=4 + + - id: mono-im-mitschnitt + type: output + title: Dieselbe Datei im Spiel + prompt: | + Eine Mono-Datei mit einem Sinus bei −20 dBFS misst in ffmpeg −23,0 LUFS. Ein `AudioStreamPlayer` spielt sie in Godot 4.7.2 ab, Bus-Regler auf 0 dB, und ein `AudioEffectRecord` schneidet den Master mit. Wie viel LUFS-I misst der Mitschnitt? Antworte mit der Zahl. + lang: text + code: | + sinus_mono.wav -23.0 LUFS + mitschnitt.wav (Stereo) ??? + expected: | + -20.0 + accept: + - "-20" + - "-20,0" + - "−20,0" + - "−20" + explanation: | + Godot legt eine Mono-Datei auf beide Kanäle in voller Höhe. Der Mitschnitt ist also Stereo mit derselben Welle links und rechts, und BS.1770 addiert die Energie beider Kanäle: 3 dB mehr. Gemessen −20,0 LUFS. Das gilt für `AudioStreamPlayer`. Über `AudioStreamPlayer2D` in der Bildschirmmitte bekommt jeder Kanal die halbe Amplitude, und derselbe Mitschnitt maß −26,0 LUFS. Renderst du Effekte ohne Ort als Mono, rechne die 3 LU ein oder nutze in REAPER die Option, Mono-Dateien um 3 dB anzupassen. + resources: + - title: ITU-R BS.1770-5 · 1-kHz-Sinus in einem Kanal (S. 9) + url: https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-5-202311-I!!PDF-E.pdf#page=9 + + - id: render-blatt + type: gap + title: Die Nachbearbeitung für Varianten + prompt: | + Ergänze das Render-Blatt mit den Begriffen aus REAPERs Postprocess-Dialog. Ziel ist die integrierte Lautheit, die Spitzen sollen auch zwischen den Samples unter der Decke bleiben, und jede Variante soll für sich auf das Ziel. + lang: prose + code: | + Normalize to: ⟦LUFS-I⟧, Ziel -18.0 (Platzhalter) + Brickwall limit: ⟦True peak⟧, Decke -1.0 (Platzhalter) + Mehrere Dateien: Normalize each file ⟦separately⟧ + Prüfen: ⟦Dry Run⟧ (no output), danach die Render-Statistik + gaps: + - label: Maß für das Ziel + hint: Integrierte Lautheit, wie REAPER sie abkürzt. + - label: Art der Spitze + hint: Nicht Sample Peak. + accept: ["truepeak"] + - label: Einzeln + hint: Jede Datei für sich. + - label: Probelauf + hint: Rendern ohne Datei. + accept: ["dryrun"] + explanation: | + LUFS-I macht die Varianten gleich laut, True Peak hält die Spitzen auch nach dem Umrechnen unter der Decke. „separately“ ist hier gewollt, weil jede Variante gleich laut sein soll; für Stems oder eine Gruppe mit gewollten Abständen wäre „as if files play together“ oder „to loudest file“ richtig. Der Dry Run zeigt das Ergebnis, bevor Dateien im Projektordner landen. Die Zahlen sind Platzhalter. + resources: + - title: REAPER User Guide 7.81 · Render-Dialog, Postprocess (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: regler-und-ducking + type: gap + title: Regler und Ducking im Code + prompt: | + Ergänze: Der Regler setzt den Bus linear von 0 bis 1. Der Kompressor auf „Musik“ hört auf den Bus „SFX“. + lang: gdscript + code: | + func _on_value_changed(v: float) -> void: + AudioServer.⟦set_bus_volume_linear⟧(_bus, v) + + + func _ducking_einrichten() -> void: + var comp := AudioEffectCompressor.new() + comp.threshold = -20.0 + comp.⟦sidechain⟧ = &"SFX" + AudioServer.add_bus_effect(AudioServer.⟦get_bus_index⟧(&"Musik"), comp) + gaps: + - label: Lineare Lautstärke + hint: Das Gegenstück zu `set_bus_volume_db`. + - label: Auslöser + hint: Die Eigenschaft, die einen Bus mit Namen nennt. + - label: Bus finden + hint: Liefert die Nummer zu einem Namen, −1 wenn es ihn nicht gibt. + explanation: | + `set_bus_volume_linear()` rechnet 0 bis 1 in dB um, 0,5 sind −6,02 dB. `sidechain` ist ein Name, kein Index, und Godot prüft ihn nicht: Ein falscher Name lässt den Kompressor auf den Master hören. `get_bus_index()` liefert −1 für einen unbekannten Namen; dann scheitert `add_bus_effect()` mit einer Fehlermeldung, die du wenigstens siehst. + resources: + - title: Godot Docs · AudioServer · set_bus_volume_linear + url: https://docs.godotengine.org/en/stable/classes/class_audioserver.html#class-audioserver-method-set-bus-volume-linear + + - id: sidechain-name + type: bug + title: Die Musik duckt auch bei der Atmo + prompt: | + Die Musik soll leiser werden, wenn ein Effekt spielt. Das tut sie. Sie wird aber genauso leiser, wenn nur die Atmo oder ein UI-Klang spielt. Godot meldet keinen Fehler. Finde die Zeile im Bus-Layout und korrigiere sie. + lang: ini + code: | + [sub_resource type="AudioEffectCompressor" id="AudioEffectCompressor_hr3ii"] + threshold = -20.0 + sidechain = &"Sfx" + + [resource] + bus/1/name = &"Musik" + bus/1/effect/0/effect = SubResource("AudioEffectCompressor_hr3ii") + bus/2/name = &"SFX" + bus/3/name = &"Atmo" + bus/4/name = &"UI" + lines: [3] + fix: + - line: 3 + answers: + - sidechain = &"SFX" + - sidechain = "SFX" + explanation: | + Einen Bus „Sfx“ gibt es nicht, denn bei Bus-Namen zählen Groß- und Kleinschreibung. Für einen unbekannten Namen nimmt Godot 4.7.2 den Master-Bus als Sidechain. Weil Godot die Busse von rechts nach links bearbeitet, liegt im Master schon alles, was rechts von `Musik` steht: SFX, Atmo und UI. Gemessen mit genau diesem Namen duckte die Musik bei Effekt und Atmo um 21,8 dB, mit richtigem Namen nur beim Effekt. Prüf jede Ducking-Regel mit einem Bus, der nicht auslösen soll. + resources: + - title: Godot Docs · AudioEffectCompressor · sidechain + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-sidechain + + - id: beat-count-alt + type: bug + title: Nach dem Neu-Rendern kommt das Ende zu spät + prompt: | + Du hast einen Loop von vier auf zwei Takte gekürzt und unter gleichem Namen neu gerendert (120 BPM, 4/4, jetzt 4 Sekunden). Seitdem kommt der Übergang „Clip-Ende“ manchmal Sekunden zu spät; im Messlauf, angefragt bei 5,0 s, erst bei 12,0 s statt bei 8,0 s. Finde die Zeile in der Import-Datei und korrigiere sie. + lang: ini + code: | + [params] + + loop=true + loop_offset=0 + bpm=120 + beat_count=16 + bar_beats=4 + lines: [6] + fix: + - line: 6 + answers: + - beat_count=8 + explanation: | + Godot importiert nach dem Neu-Rendern mit den alten Einstellungen; die `.import`-Datei blieb im Messlauf unverändert. Mit `beat_count` 16 hält Godot das Ende für 8 Sekunden nach dem Clip-Anfang, die Datei schleift aber schon nach 4. Bei 5,0 s steht der Clip bei Position 1,0, Godot wartet also 7 Sekunden. Zwei Takte zu vier Schlägen sind 8 Schläge. Ändert sich die Länge, gehört die Import-Zeile in denselben Arbeitsschritt. + resources: + - title: Godot Docs · Import process, Automatic reimport + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport + + - id: ducking-tabelle + type: explain + title: Warum der stumme Bus nur manchmal duckt + prompt: | + Erkläre diese Messung mit Godot 4.7.2. Kompressor auf „Musik“, `sidechain` „SFX“, ein Effekt spielt auf „SFX“. + lang: text + code: | + SFX-Bus SFX Musik + rechts von Musik normal -21.8 dB + rechts von Musik stumm 0.0 dB + rechts von Musik -80 dB 0.0 dB + links von Musik stumm -21.8 dB + links von Musik -80 dB -21.8 dB + points: + - Godot bearbeitet die Busse von der höchsten Nummer zur niedrigsten, also von rechts nach links. + - Rechts von Musik ist der SFX-Bus schon fertig, mit Fader und Mute; der Kompressor hört dann nichts mehr. + - Links von Musik liest der Kompressor den Bus, bevor Fader und Mute angewendet sind. + - Ein stummer Auslöser-Bus funktioniert deshalb nur links von Musik, und das ist ein Verhalten der Version, kein dokumentiertes Versprechen. + explanation: | + Der Kompressor liest den Sidechain-Bus in dem Zustand, in dem er im selben Mischschritt gerade ist. Godot bearbeitet die Busse von hinten nach vorn: Effekte, Lautstärke und Mute, dann Weitergabe an den Ziel-Bus. Steht „SFX“ rechts, ist er schon fertig, und bei Mute oder −80 dB liegt dort Stille. Steht er links, kommt er erst nach „Musik“ dran, und der Kompressor sieht ihn roh, vor Fader und Mute. Darauf kannst du einen unhörbaren Auslöser-Bus bauen, solltest aber in den Kommentar schreiben, dass er von der Reihenfolge abhängt, und es nach jedem Godot-Update nachmessen. + resources: + - title: Godot Quelltext 4.7.2 · audio_server.cpp + url: https://github.com/godotengine/godot/blob/4.7.2-stable/servers/audio/audio_server.cpp + + - id: busse-messen + type: practice + title: Busse, Regler und Ducking messen + prompt: | + Richte in deiner Kopie der Testszene Busse nach deinem Plan ein (die Namen Musik, SFX, Atmo, UI sind Platzhalter), mit einem Regler je Bus und einer Ducking-Regel. Miss die Regel mit Prüftönen: Musik als Dauerton, ein Effekt-Ton von 0,5 s, ein Atmo-Ton zu einer anderen Zeit. Schneide den Master mit `mitschnitt.gd` mit und lies ab, wie tief und wie lange die Musik duckt und ob sie bei der Atmo ruhig bleibt. + lang: gdscript + solution: | + # Busse (Panel Audio, von links nach rechts): Master, Musik, SFX, Atmo, UI + # Musik: AudioEffectCompressor, threshold -20, ratio 4, sidechain = SFX (Platzhalter) + # Je Bus ein HSlider mit bus_regler.gd und passendem bus_name. + # Prüftöne: Dauerton auf Musik, Effekt-Ton bei 2,0 s auf SFX, Atmo-Ton bei 3,0 s auf Atmo. + # Mitschnitt.sekunden = 5, danach Auswertung pro 20-ms-Fenster. + # + # Erwartung mit diesen Platzhaltern (gemessen mit Godot 4.7.2): + # Effekt bei 2,0 s: Musik -21.8 dB, nach 1 s zurück bis auf -0.4 dB + # Atmo bei 3,0 s: Musik bleibt (nur Rest der Release) + # sidechain falsch geschrieben: Musik duckt auch bei der Atmo + checklist: + - Jeder Bus hat einen Regler; bei Wert 0,5 zeigt der Bus −6 dB. + - Die Ducking-Regel ist mit Prüftönen gemessen, nicht mit Musik und Effekten aus dem Spiel. + - Du hast notiert, wie tief die Musik duckt und nach wie vielen Millisekunden sie wieder bis auf 1 dB zurück ist. + - Der Gegentest mit einem Bus, der nicht auslösen soll, ist gemessen und ohne Ducking. + - Du hast die Reihenfolge der Busse im Panel notiert und ob der Auslöser-Bus links oder rechts von Musik steht. + explanation: | + Ducking ist eine Mischentscheidung, aber ob es so arbeitet, wie du denkst, ist eine technische Frage. Die Messung mit Prüftönen trennt beides: Erst stimmt die Technik, dann hörst du mit echter Musik, ob die Tiefe und die Rückkehr passen. In Godot 4.7.2 ist das besonders nötig, weil die Absenkung stärker ausfällt als die Lehrbuchformel und die Rückkehr länger dauert als der Release-Wert vermuten lässt. + resources: + - title: Godot Docs · AudioEffectRecord · get_recording + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectrecord.html#class-audioeffectrecord-method-get-recording + - title: Godot Docs · Audio buses + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html + + - id: eigenes-hoerprotokoll + type: practice + title: Dein Hörtest im Spiel + prompt: | + Spiel einen Abschnitt deines Spiels oder der Testszene mit deinen eigenen Klängen und deiner Musik, mit Drehbuch, damit jeder Lauf gleich ist. Schneide den Master mit und miss LUFS-I und True Peak. Hör denselben Lauf auf mindestens zwei Geräten und mit mindestens zwei Systemlautstärken, eine davon leise. Führe ein Protokoll mit Datum, Fassung, Gerät, Lautstärke, Messwert, Beobachtung und offener Frage. Prüf dabei die Prioritäten aus deinem Mix-Plan. + lang: prose + checklist: + - Für jede Kombination aus Gerät und Lautstärke gibt es eine Zeile im Protokoll. + - LUFS-I und True Peak stammen aus einem Mitschnitt des Spiels, nicht aus einzelnen Dateien. + - Zu jeder Priorität aus deinem Mix-Plan steht, ob sie auf jedem Gerät zu hören war. + - Beobachtungen beschreiben, was du hörst, ohne Wertung wie „gut“ oder „zu viel“. + - Jede offene Frage nennt genau eine Änderung, die du als Nächstes einzeln testen könntest. + - Die Fassung (Datum oder Nummer) steht in jeder Zeile, damit du später vergleichen kannst. + explanation: | + Der Hörtest beantwortet, was keine Zahl beantwortet: ob der Spieler hört, was er hören soll. Das Protokoll macht daraus etwas Vergleichbares. Mit Drehbuch und Mitschnitt kannst du die nächste Fassung unter denselben Bedingungen prüfen. Wie laut dein Spiel sein soll und was Vorrang hat, entscheidest du; das Protokoll zeigt dir, ob deine Entscheidung im Spiel ankommt. + resources: + - title: Godot Docs · AudioEffectRecord + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectrecord.html#class-audioeffectrecord-method-get-recording + - title: EBU Tech 3341 · Loudness Metering (S. 5) + url: https://tech.ebu.ch/docs/tech/tech3341.pdf#page=5 diff --git a/content/sound/modules/feldaufnahme.yaml b/content/sound/modules/feldaufnahme.yaml new file mode 100644 index 0000000..7474b97 --- /dev/null +++ b/content/sound/modules/feldaufnahme.yaml @@ -0,0 +1,464 @@ +id: feldaufnahme +title: "Feldaufnahme mit dem Handy: Pegel, Abstand, Ansage und Room Tone" +level: 1 +minutes: 90 +summary: Wie du mit dem Handy brauchbares Rohmaterial für Spielgeräusche aufnimmst – App-Einstellungen, Pegel, Abstand, Raum, Wind und Griffgeräusche, Ansage, mehrere Takes und Room Tone –, ohne Stimmen Dritter mitzuschneiden, und warum ein Aufnahmeprotokoll dir später die Suche erspart. +place: unterwegs +goals: + - Du prüfst in deiner Aufnahme-App Format, Filter, Pegelregelung und Aufnahmemodus und nennst, was Apple dazu für Sprachmemos dokumentiert. + - Du stellst den Pegel über den Abstand ein und begründest mit dem Abstandsgesetz, warum „nah ran“ den Vorsprung vor dem Hintergrund vergrößert. + - Du erkennst Übersteuern, Wind, Griffgeräusche und Raumhall beim Abhören und vermeidest sie bei der Aufnahme. + - Du nimmst mit Ansage, mehreren Takes und Room Tone auf und führst ein Aufnahmeprotokoll. + - Du rechnest Samples und Dateigröße einer WAV-Aufnahme aus. + - Du hältst Stimmen Dritter aus deinen Aufnahmen heraus und kennst den Wortlaut von § 201 StGB, ohne ihn als Rechtsrat zu nehmen. +resources: + - title: Apple Support · Aufnahme mit Sprachmemos auf dem iPhone + url: https://support.apple.com/de-de/guide/iphone/iph4d2a39a3b/ios + - title: NPR Training · Field recording checklist + url: https://training.npr.org/2017/06/30/get-great-sound-every-time-with-this-field-recording-checklist/ + - title: NPR Training · Audio production FAQ + url: https://training.npr.org/2017/05/19/audio-production-faq-headphones-levels-mics/ + - title: Sound On Sound · Abstand und Nahbesprechungseffekt + url: https://www.soundonsound.com/sound-advice/q-does-strength-proximity-effect-depend-volume-source + - title: Gesetze im Internet · § 201 StGB + url: https://www.gesetze-im-internet.de/stgb/__201.html +lesson: | + Du nimmst am Küchentisch einen Klatscher auf, als Prüfmuster für [Audio zuordnen](/godot/audio). Mit Kopfhörern hörst du danach: eine halbe Sekunde Rascheln, dann den Klatscher, oben abgeschnitten, dahinter den Kühlschrank und eine Stimme aus dem Nebenzimmer. Am Tisch war nichts davon laut. Das Handy hat aufgenommen, was im Raum war, und die App hat noch etwas dazugetan. + + Eine **Feldaufnahme** ist eine Aufnahme außerhalb des Studios, hier mit dem Handy. Pegel, Mikrofonabstand und Raum kennst du aus der Musik. Für Spielgeräusche kommt hinzu: Du brauchst von jedem Klang mehrere brauchbare Takes, saubere Pausen für den Schnitt, einen Room Tone zum Füllen und ein Protokoll, mit dem du in drei Monaten noch weißt, welche Datei was ist. Dieses Modul zeigt, wie das mit Handy und Kopfhörern geht. Was du aufnimmst, entscheidest du. Säubern und Schneiden kommt in [REAPER als Werkbank](/sound/reaper-werkbank). + + ## Das Handy als Recorder: erst die App prüfen + + Aufnahme-Apps sind für Sprache gebaut. Viele bearbeiten den Klang schon während der Aufnahme: Sie filtern Hintergrund weg, regeln den Pegel nach oder speichern verlustbehaftet. Für eine Notiz ist das praktisch. Für Rohmaterial ist es ein Eingriff, den du später nicht zurücknimmst. Prüf deshalb vor der ersten Session vier Punkte: + + | Punkt | Wonach du suchst | + | --- | --- | + | Format | verlustfrei (WAV, FLAC) oder verlustbehaftet (AAC, MP3)? | + | Filter | Rauschunterdrückung, Sprachfilter | + | Pegel | Regler, Automatik oder nichts? | + | Kanäle | Mono, Stereo, 3D | + + Für Sprachmemos auf dem iPhone beschreibt Apple im Handbuch zu iOS 27: + + - Den Pegel stellst du über den Abstand ein: „Verringere oder vergrößere den Abstand des Mikrofons zur aufgezeichneten Audioquelle, um den Aufnahmepegel anzupassen“ ([Aufnahme mit Sprachmemos](https://support.apple.com/de-de/guide/iphone/iph4d2a39a3b/ios#iph30340c381)). Einen Regler nennt die Anleitung nicht. + - Im Kontrollzentrum wählst du zwischen „Stimmisolation“, die Hintergrundgeräusche herausfiltert, und „Breites Spektrum“, das sie einbezieht. „Breites Spektrum“ gibt es nicht auf allen Modellen. Bei Geräuschen ist der „Hintergrund“ oft der Klang, den du willst. + - Unter Einstellungen → Apps → Sprachmemos → „Aufnahmemodus“ stehen Mono, Stereo und 3D-Audio ([Aufnahmemodus](https://support.apple.com/de-de/guide/iphone/iph4d2a39a3b/ios#iphc30ff1a19)). Auf unterstützten Modellen nimmt das eingebaute Mikrofon laut derselben Seite automatisch in 3D-Audio auf. + - „Aufnahme optimieren“ reduziert beim Abspielen Hintergrundgeräusche und Echo, „Studio-Sound“ Hintergrund und Hall in 3D-Audioaufnahmen ([Aufnahme wiedergeben](https://support.apple.com/de-de/guide/iphone/iphebbd067ce/ios#iph4b15d91b6)). Für Rohmaterial bleiben beide aus. + + In welchem Format und mit welcher Abtastrate Sprachmemos speichert, steht auf diesen Seiten nicht. Prüf es an einer Probedatei auf dem PC. Auf Android hängen Format, Filter und Pegelautomatik an Hersteller und App. Such in deiner App nach den vier Punkten und notier, was du findest und was sie nicht anzeigt. + + Warum der Aufwand? Die Godot-Doku trennt Quelle und Spieldatei: „While keeping pristine-quality audio sources is important if you're performing editing, using the same quality in the exported project is not necessary“ ([Best practices](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices)). Dieselbe Seite sagt, eine Abtastrate über 48 kHz bringe im Spiel nur etwas, wenn du den Ton zur Laufzeit verlangsamst. Für die Aufnahme ist das eine eigene Frage. Der Sounddesigner Jamie Hardt nimmt kurze Klänge nach eigener Aussage möglichst mit 192 kHz auf, Hintergründe mit 96 oder 48 kHz ([A Sound Effect](https://www.asoundeffect.com/jamie-hardt-field-recording-immersive-audio/)). Was dein Handy kann, zeigt die Probedatei. + + > [!TIP] Der erste Take ist ein Prüfmuster + > Fang jede Session mit einem Klatscher an. Er zeigt dir beim Abhören, ob die App filtert, ob der Pegel reicht und ob die Datei vorne Stille hat. Ein kurzer, trockener Klatscher ohne Stille am Anfang ist zugleich der Prüfklick für [Audio zuordnen](/godot/audio). Er ist ein Messwerkzeug, kein Klang für dein Spiel. + + ## Pegel: Übersteuern lässt sich nicht reparieren + + Headroom kennst du aus der Musik. Im Feld gilt er strenger: Ein Klang, der einmal passiert, kommt nicht wieder, und eine übersteuerte Spitze bleibt abgeschnitten. Kurze Schläge sind tückisch, weil ihre Spitze weit über dem liegt, was du als Lautstärke wahrnimmst. Eine App ohne Pegelanzeige verrät dir das erst beim Abhören. + + Leiser aufnehmen und später anheben hilft nicht. NPR Training antwortet auf diese Frage: „Recording at a lower level will not record less background noise. It will simply record — at a lower level — the same ratio of direct sound (your subject speaking) to background sound“ ([Audio production FAQ](https://training.npr.org/2017/05/19/audio-production-faq-headphones-levels-mics/)). Dazu kommt das Rauschen der Elektronik, das beim Anheben mitkommt. Das Verhältnis zum Hintergrund änderst du nur über den Abstand. + + Am Handy heißt das: Probe-Take mit dem lautesten Moment, den du erwartest, abhören, Abstand anpassen, erst dann die Takes. NPR beginnt seine Checkliste mit „Always wear your headphones. If you can't hear problems, you can't prevent them“ ([Field recording checklist](https://training.npr.org/2017/06/30/get-great-sound-every-time-with-this-field-recording-checklist/)). + + > [!WARNING] Kopfhörer mit Funk oder Geräuschunterdrückung + > NPR rät für die Feldarbeit von Bluetooth- und Noise-Cancelling-Kopfhörern ab: Bluetooth reduziert die Qualität für die Übertragung, die Geräuschunterdrückung kann verdecken, was du aufnimmst. Schalt die Unterdrückung zum Abhören aus und hör heikle Takes später am PC noch einmal. + + ## Abstand, Raum und Perspektive + + Das Abstandsgesetz kennst du aus der Musik. Am Handy zählt es mehr: Wo die App keinen Pegelregler hat, wie Sprachmemos laut Anleitung, ist der Abstand dein einziger Regler. Eine punktförmige Quelle verliert mit jeder Verdopplung des Abstands etwa 6 dB, in der Praxis laut Hugh Robjohns „rarely as much as 6dB per doubling of distance“ ([Sound On Sound](https://www.soundonsound.com/sound-advice/q-does-strength-proximity-effect-depend-volume-source)). Der Hintergrund – Kühlschrank, Straße, Raumhall – bleibt dabei ungefähr gleich laut. Das Modell rechnet aus, wie viel Vorsprung vor dem Hintergrund bleibt; die 30 dB bei 10 cm sind ein Platzhalter: + + ```python run + from math import log10 + + REF = 10 # cm, Platzhalter + VORSPRUNG = 30 # dB, Platzhalter + + for d in [10, 20, 40, 80, 160]: + verlust = 20 * log10(d / REF) + vor = VORSPRUNG - verlust + print(f"{d} cm: {vor:.1f} dB") + ``` + + Bei 160 cm sind von 30 dB noch 5,9 übrig. Den Nahbesprechungseffekt kennst du ebenfalls; welche Bauart das Mikrofon deines Handys hat, steht aber meist nirgends. Hör es dir an: derselbe Klang bei 5, 20 und 50 cm. + + Raumhall in der Datei ist für Spiele ein eigenes Problem. Die Godot-Doku empfiehlt auf der schon zitierten Seite, Hall über Busse im Spiel zu erzeugen, statt ihn in die Datei zu backen; die Abwägung steht in [Klangformung](/sound/klangformung). Hall lässt sich im Spiel dazugeben, aber nicht herausnehmen, und ein Take mit Badhall klingt in jedem Raum des Spiels nach Bad. Für die Aufnahme heißt das: nah ran, und ein Raum mit Polstern, Vorhängen und Kleidung wirft weniger zurück als ein Bad. + + Für Spiele zählt auch die Perspektive. Watson Wu nimmt Waffen für Filme nach den Winkeln der Szenen auf, für Spiele dagegen „multiple perspectives such as close, medium, far, and incoming“ ([A Sound Effect](https://www.asoundeffect.com/weapon-sound-effects-recording/)). Ob du nahe und ferne Fassungen brauchst, hängt an deinem Spiel. Am Handy kostet ein zweiter Abstand einen Take. + + ## Wind, Griffgeräusche und Störungen + + Wind ist bewegte Luft direkt an der Mikrofonöffnung. Er klingt als tiefes Rumpeln, das sich später kaum sauber entfernen lässt. NPR: „Omnidirectional microphones experience less wind noise and handle p-pops better than cardioids and shotguns“ ([Which mic should I use?](https://www.npr.org/sections/npr-training/2025/05/28/g-s1-66934/which-mic-should-i-use-mics-part-1)). Was dein Handy hat, weißt du nicht. Schütz es mit deinem Körper, einer Wand oder einem Hauseingang. + + Griffgeräusche sind Schall, der über das Gehäuse ins Mikrofon kommt. NPR beschreibt „fingers sliding across the surface of the mic or tendons and bones creaking“ und warnt, dass das gern bei leisen Atmos auftaucht. Am Handy heißt das: nicht in der Hand halten, sondern auf eine weiche Unterlage legen, Finger weg von der Mikrofonöffnung. + + Störungen planst du weg: Kühlschrank aus und danach wieder an, Fenster zu, Benachrichtigungen aus. Die Start- und Stopptöne von Sprachmemos schaltest du laut derselben Apple-Anleitung mit dem Stummmodus ab. NPR schreibt, bei Nachrichten und Dokumentationen schade ein Hochpassfilter im Feld nie. Bei Spielgeräuschen sitzt das Gewicht oft unten; diesen Schnitt machst du besser später in REAPER, wo er sich zurücknehmen lässt. + + ## Ansage, Takes, Room Tone und Protokoll + + Eine **Ansage** (Slate) ist laut Sound On Sound „a verbal identification recorded just before each take to help identify it subsequently“ ([Glossar](https://www.soundonsound.com/glossary/slate)). Du sagst vor jedem Take kurz, was kommt, und lässt danach Stille, damit sich Stimme und Klang beim Schneiden trennen lassen: + + ```text + „Take vier, Schublade, + zwanzig Zentimeter, langsam“ + – 2 s Stille – + Schublade auf, Schublade zu + – ausklingen lassen, 3 s – + ``` + + Nimm gleich mehrere Takes auf. Im Spiel läuft ein Klang oft viele Male hintereinander, und identische Wiederholungen fallen auf. Wu schreibt dazu: „Video games are non-linear so more variation sounds are often needed.“ Wie viele Varianten ein Klang braucht, leitest du aus der Cue-Liste ab ([Vom Ereignis zur Cue-Liste](/sound/klangplan)). Was du daraus baust, kommt in [Layering und Varianten](/sound/layering-varianten). Bei der Aufnahme gilt: lieber ein Take zu viel. + + Ein **Room Tone** ist eine Aufnahme des Raums ohne Aktion, mit derselben Position und demselben Pegel wie die Takes. Du brauchst ihn, um Lücken im Schnitt zu füllen und in REAPER ein Rauschprofil zu nehmen. NPR nennt ihn „probably the most commonly forgotten audio element“, empfiehlt für Reportagen 90 Sekunden pro Szene und gibt die Regel dazu: „keep your microphone in the same place it was in during the interview and don't adjust levels on the recorder“. Der Kurs nimmt 30 Sekunden als Platzhalter. Bei 48 kHz sind das 1 440 000 Samples. + + > [!PRAXIS] Profis nehmen auch mit dem Telefon auf + > Jamie Hardt erzählt, dass er ein Stereomikrofon für das iPhone immer an der Tasche trägt: „a lot of the short sounds in my library are from that“. Watson Wu benennt seine Waffenaufnahmen nach einem festen Schema aus Waffentyp, Name, Kaliber, Schussart, Perspektive und Versionsnummer. Beides zusammen ist der Kern einer Session: Aufnahmen, die du jederzeit machen kannst, und Namen, die dir sagen, was drin ist. + + ### Das Aufnahmeprotokoll + + Ein Aufnahmeprotokoll hält fest, was du aufgenommen hast, solange du es noch weißt. Eine Zeile pro Take reicht, im Notizprogramm des Handys: + + ```text + 2026-10-24 Küche, Fenster zu + Handy, App: Sprachmemos + Breites Spektrum, Mono + T1–T3 Schublade 20 cm + T4–T5 Schublade 60 cm + T6 Room Tone 30 s, Stelle T1 + Notiz: T2 Kühlschrank lief noch + ``` + + Die Ansage im Take und die Zeile im Protokoll sagen dasselbe. Fehlt eins, rettet dich das andere. Benenne die Rohdatei nach der Session nach einem eigenen Schema, etwa mit Datum, Ort und Gegenstand. Das Platzhalter-Schema `___.wav` aus [Vom Ereignis zur Cue-Liste](/sound/klangplan) gilt erst für die geschnittenen Varianten aus [REAPER als Werkbank](/sound/reaper-werkbank), eine Datei pro Variante. Eine Session-Datei enthält dagegen mehrere Takes und den Room Tone. Die Originaldatei überschreibst du nie. Sprachmemos bietet nach dem Kürzen „Als neue Aufnahme sichern“ an, das laut Apple das Original unverändert lässt ([Sprachaufnahme bearbeiten](https://support.apple.com/de-de/guide/iphone/iphc9bdaee83/ios#iph80099e945)). + + Wie viel Platz Rohmaterial braucht, rechnest du aus Abtastrate, Bittiefe und Kanälen. Eine unkomprimierte WAV-Datei speichert jeden Sample-Wert: + + ```python run + # MB pro Minute, unkomprimiert + def mb_min(rate, bits, ch): + pro_s = rate * bits // 8 * ch + return pro_s * 60 / 1_000_000 + + formate = [ + (48000, 24, 1), + (48000, 24, 2), + (44100, 16, 2), + ] + print("Hz Bit Kanäle MB/min") + for rate, bits, ch in formate: + mb = mb_min(rate, bits, ch) + print(rate, bits, ch, mb) + ``` + + Eine Stunde Mono mit 48 kHz und 24 Bit sind rund 518 MB. Übertrag die Originaldatei auf den PC und prüf dort, ob Format, Abtastrate und Länge zu deinem Protokoll passen. + + ## Fremde Stimmen und fremder Grund + + Stimmen anderer Menschen gehören nicht in deine Aufnahmen. § 201 Abs. 1 StGB bestraft, „wer unbefugt 1. das nichtöffentlich gesprochene Wort eines anderen auf einen Tonträger aufnimmt oder 2. eine so hergestellte Aufnahme gebraucht oder einem Dritten zugänglich macht“ ([§ 201 StGB](https://www.gesetze-im-internet.de/stgb/__201.html)). Was im Einzelfall „nichtöffentlich“ ist, ist eine Rechtsfrage. Dieser Kurs gibt keinen Rechtsrat. Er gibt dir eine Arbeitsregel, die ohne Juristen auskommt: + + - Keine verständlichen Stimmen Dritter. Hörst du beim Abhören eine, schneidest du die Stelle heraus und löschst das Original mit ihr. + - Musik aus einem Radio oder Laden im Hintergrund gehört jemandem. Warum, steht in [Quellen und Lizenzen](/sound/quellen-lizenzen). + - Auf fremdem Grund fragst du vorher, auch im Hausflur. + + > [!INTERVIEW] + > „Wie bereitest du eine Aufnahme für Spielgeräusche vor?“ Gute Antwort, etwa als Sound Designer: Ich prüfe vorher, ob das Gerät verlustfrei und ohne Filter aufnimmt, und mache einen Probe-Take für den Pegel. Ich nehme nah auf, sage jeden Take an und nehme mehrere Takes und Abstände auf, weil das Spiel Varianten braucht. Am Ende kommt Room Tone mit derselben Position und demselben Pegel. Ich führe ein Protokoll, benenne die Rohdateien nach Datum, Ort und Gegenstand und erst die geschnittenen Varianten nach dem Schema des Projekts. Stimmen Dritter nehme ich nicht auf. + + ## Häufige Fehler + + - **Filter der App an.** „Stimmisolation“ oder Rauschunterdrückung können den Hintergrund wegnehmen, der ein Geräusch ausmacht. Vorher prüfen, Probe-Take hören. + - **Zu leise, um sicher zu sein.** Der Hintergrund kommt beim Anheben mit, das Rauschen der Elektronik dazu. Näher ran statt leiser. + - **Handy in der Hand.** Griffgeräusche und ein Daumen auf der Öffnung. Auf eine weiche Unterlage legen. + - **Kein Room Tone oder ein anderer.** Room Tone von einer anderen Stelle oder mit anderem Pegel passt nicht zu den Takes. + - **Takes ohne Ansage.** Nach zwei Wochen weißt du nicht mehr, welcher Take der nahe war. + - **Original überschrieben.** Kürzen ist gut, Überschreiben nicht. Als neue Aufnahme sichern. + - **Eine Stimme im Hintergrund.** Herausschneiden und das Original löschen. + + ## Zusammenfassung + + - Aufnahme-Apps sind für Sprache gebaut. Prüf Format, Filter, Pegelregelung und Kanäle, bevor du Rohmaterial aufnimmst. + - Bei Sprachmemos stellst du den Pegel laut Apple über den Abstand ein; „Breites Spektrum“ behält den Hintergrund, „Stimmisolation“ filtert ihn. + - Übersteuern ist endgültig. Leiser aufnehmen ändert das Verhältnis zum Hintergrund nicht, nur der Abstand tut das. + - Jede Verdopplung des Abstands kostet bis zu 6 dB Vorsprung vor dem Hintergrund: nah ran, nah bleiben. + - Hall lässt sich im Spiel dazugeben, aber nicht aus der Datei nehmen. Nimm möglichst trocken auf. + - Ansage, mehrere Takes, Room Tone mit gleicher Position und gleichem Pegel, eine Protokollzeile pro Take. + - Eine Minute Mono-WAV mit 48 kHz und 24 Bit sind 8,64 MB. Originale bleiben unverändert. + - Keine verständlichen Stimmen Dritter; § 201 StGB nennt den Rahmen, der Kurs ersetzt keinen Rechtsrat. +exercises: + - id: app-pruefen + type: choice + title: Was die App vorher verraten muss + prompt: | + Du willst mit dem Handy Rohmaterial für Spielgeräusche aufnehmen. Welche Aussagen stimmen? Mehrere Antworten richtig. + options: + - text: Du prüfst, ob die App verlustfrei speichert, etwa als WAV oder FLAC, oder verlustbehaftet als AAC oder MP3. + correct: true + why: Verlustbehaftete Formate werfen Information weg, die du beim Bearbeiten nicht zurückbekommst. Die Godot-Doku rät, Quellen in hoher Qualität zu behalten, solange du sie bearbeitest. + - text: In Sprachmemos stellst du den Pegel laut Apple über den Abstand zum Mikrofon ein. + correct: true + why: So steht es im iPhone-Handbuch. Einen Regler beschreibt die Anleitung nicht. + - text: „Stimmisolation“ ist für Geräusche die bessere Wahl, weil sie das Rauschen entfernt. + correct: false + why: Sie filtert laut Apple Hintergrundgeräusche heraus. Bei Geräuschen ist der Hintergrund oft der Klang, den du willst. „Breites Spektrum“ bezieht ihn ein. + - text: Unter 192 kHz taugt eine Aufnahme nicht für Spiele. + correct: false + why: Laut Godot-Doku bringt eine Abtastrate über 48 kHz im Spiel nur etwas, wenn du den Ton zur Laufzeit verlangsamst. Ob du für späteres Herunterstimmen höher aufnimmst, ist deine Entscheidung, keine Pflicht. + - text: Eine leise Aufnahme ist die sichere Wahl, weil man sie später ohne Nachteil anhebt. + correct: false + why: Beim Anheben kommen Hintergrund und Rauschen der Elektronik mit. NPR schreibt, leiser aufnehmen nehme nicht weniger Hintergrund auf, sondern dasselbe Verhältnis, nur leiser. + explanation: | + Die App entscheidet mit, was in der Datei landet. Format und Filter wirken, bevor du irgendetwas hörst, und lassen sich später nicht zurücknehmen. Der Pegel hängt bei Sprachmemos am Abstand, das ist zugleich das Werkzeug gegen Hintergrund. Eine hohe Abtastrate ist eine Option, kein Muss; ein sauberer, naher, unverfälschter Take zählt mehr. + resources: + - title: Apple Support · Hintergrundgeräusche filtern + url: https://support.apple.com/de-de/guide/iphone/iph4d2a39a3b/ios#iph896bc6087 + - title: Godot Docs · Importing audio samples, Best practices + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices + + - id: ablauf-session + type: order + title: Eine Session von vorn bis hinten + prompt: | + Bring die Schritte einer kurzen Aufnahme-Session in eine sinnvolle Reihenfolge. + lang: prose + lines: | + App-Einstellungen prüfen und einen Klatscher als Probe aufnehmen + Die Probe mit Kopfhörern anhören und den Abstand anpassen + Aufnahme starten, den ersten Take ansagen, zwei Sekunden warten + Mehrere Takes aufnehmen, nah und weiter weg, jeder mit Ansage + Ohne das Handy zu bewegen 30 Sekunden Room Tone aufnehmen + Die Takes im Protokoll nachtragen und die Datei umbenennen + explanation: | + Erst prüfen, dann aufnehmen: Die Probe zeigt Filter und Pegel, bevor ein echter Take verloren geht. Ansage und Stille kommen vor jeden Take. Der Room Tone folgt direkt nach den Takes, solange Position und Pegel noch dieselben sind; danach bewegst du das Handy. Das Protokoll schreibst du zum Schluss fertig, solange du dich noch erinnerst. + resources: + - title: NPR Training · Field recording checklist + url: https://training.npr.org/2017/06/30/get-great-sound-every-time-with-this-field-recording-checklist/ + + - id: arbeitsregel-stimmen + type: choice + title: Welche Aufnahme behältst du nicht so? + prompt: | + Die Arbeitsregel des Moduls: keine verständlichen Stimmen Dritter; ist doch eine drin, Stelle herausschneiden und Original löschen. Welche Aufnahmen verstoßen gegen die Regel, so wie sie sind? Mehrere Antworten richtig. + options: + - text: Im Café nimmst du Geschirrklappern auf. Am Nebentisch versteht man zwei Leute, die sich unterhalten. + correct: true + why: Verständliche Stimmen Dritter. Herausschneiden oder die Aufnahme verwerfen und das Original löschen. + - text: In deiner Wohnung nimmst du allein Schubladen und Türen auf. + correct: false + why: Keine fremden Stimmen, eigener Raum. Hier gibt es nichts zu entscheiden. + - text: Ein Take vom Balkon, auf dem der Nachbar am Telefon verständlich zu hören ist, aber nur leise. + correct: true + why: Leise ist nicht unverständlich. Die Regel fragt, ob man die Worte versteht. + - text: Du sprichst selbst die Ansage vor jedem Take. + correct: false + why: Deine eigene Stimme ist keine Stimme Dritter. Die Ansage schneidest du später ohnehin ab. + explanation: | + § 201 StGB schützt das nichtöffentlich gesprochene Wort; was im Einzelfall darunterfällt, beantwortet dieser Kurs nicht. Die Arbeitsregel ist enger als jede Auslegung und deshalb ohne Jurist anwendbar: Was man verstehen kann, kommt nicht in deine Dateien. So musst du später nicht entscheiden, ob eine Stelle heikel ist. + resources: + - title: Gesetze im Internet · § 201 StGB + url: https://www.gesetze-im-internet.de/stgb/__201.html + + - id: naeher-ran + type: output + title: Vom Tisch näher heran + prompt: | + Im Modell aus der Lektion liegt dein Klang bei 40 cm 12 dB über dem Hintergrund. Der Hintergrund bleibt gleich laut. Was gibt das Programm aus? + lang: python + code: | + from math import log10 + + # Platzhalter + vorsprung = 12.0 # dB bei 40 cm + for d in [40, 20, 10]: + gewinn = 20 * log10(40 / d) + v = vorsprung + gewinn + print(d, round(v, 1)) + expected: | + 40 12.0 + 20 18.0 + 10 24.0 + hints: + - 20 × log10(2) ist etwa 6. + explanation: | + Jede Halbierung des Abstands bringt im Modell 6 dB, von 40 auf 10 cm also 12 dB mehr Vorsprung. Laut Robjohns ist der echte Gewinn meist kleiner, weil kaum eine Quelle punktförmig ist. Die Richtung stimmt trotzdem, und sie ist der einzige Hebel: Am Pegel der App zu drehen ändert den Vorsprung nicht. Nähe hat ihren eigenen Preis, bei Richtmikrofonen den Nahbesprechungseffekt. + resources: + - title: Sound On Sound · Abstand und Nahbesprechungseffekt + url: https://www.soundonsound.com/sound-advice/q-does-strength-proximity-effect-depend-volume-source + + - id: speicher-pro-minute + type: output + title: Wie groß wird die Datei? + prompt: | + Du nimmst eine Minute in Stereo mit 48 kHz und 24 Bit auf, unkomprimiert. Was gibt das Programm aus? + lang: python + code: | + rate = 48000 + bits = 24 + kanaele = 2 + sekunden = 60 + pro_s = rate * bits // 8 * kanaele + groesse = pro_s * sekunden + print(groesse) + mb = groesse / 1_000_000 + print(round(mb, 2), "MB") + expected: | + 17280000 + 17.28 MB + hints: + - 24 Bit sind 3 Byte. Pro Sekunde also 48 000 × 3 × 2. + explanation: | + Pro Sekunde fallen 48 000 Samples je Kanal an, jeder mit 3 Byte, bei zwei Kanälen 288 000 Byte. Mal 60 sind das 17 280 000 Byte, gut 17 MB pro Minute, doppelt so viel wie Mono. Für Klänge, die im Spiel einen Ort haben, brauchst du später ohnehin Mono (siehe [Klangformung](/sound/klangformung)). Ob du stereo aufnimmst und später zusammenfasst, entscheidest du; die Rechnung sagt dir, was es kostet. + resources: + - title: Godot Docs · Supported audio formats (Größenvergleich) + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#supported-audio-formats + + - id: samples-rechnen + type: gap + title: Sekunden, Frames und Samples + prompt: | + Ergänze die Werte für eine Aufnahme mit 48 kHz. Ganze Zahlen ohne Tausenderpunkte. + lang: text + code: | + Room Tone 30 s = ⟦1440000⟧ Samples + Stille 2 s = ⟦96000⟧ Samples + 1 Frame (60/s) = ⟦800⟧ Samples + 24 Bit mono = ⟦144000⟧ Byte/s + gaps: + - label: 30 s + hint: 30 × 48 000 + accept: ["1.440.000", "1 440 000"] + - label: 2 s + hint: 2 × 48 000 + accept: ["96.000", "96 000"] + - label: 1 Frame + hint: 48 000 / 60 + - label: Byte pro Sekunde + hint: 48 000 × 3 Byte + accept: ["144.000", "144 000"] + explanation: | + Abtastrate mal Sekunden ergibt Samples. Ein Frame bei 60 Frames pro Sekunde sind 800 Samples; diese Zahl brauchst du wieder, wenn Stille vor einem Klang zur Verzögerung im Spiel wird. Die 2 s nach der Ansage sind 96 000 Samples, genug Abstand für einen sauberen Schnitt. Rechnest du in Samples, sind Schnitte und Längen in REAPER und Godot exakt vergleichbar. + resources: + - title: Godot Docs · Edit > Trim + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-trim + + - id: begriffe-feld + type: gap + title: Die Wörter einer Session + prompt: | + Ergänze die Fachbegriffe. + lang: prose + code: | + Die gesprochene Kennung vor jedem Take heißt ⟦Ansage⟧ oder Slate. Eine Aufnahme des Raums ohne Aktion, mit gleicher Position und gleichem Pegel wie die Takes, heißt ⟦Room Tone⟧. Fassungen desselben Klangs aus verschiedenen Abständen, etwa nah, mittel und fern, nimmt Watson Wu für Spiele als mehrere ⟦Perspektiven⟧ auf. + gaps: + - label: Kennung + hint: Das deutsche Wort für Slate. + - label: Raum ohne Aktion + hint: Englischer Begriff aus Film und Radio, zwei Wörter. + accept: ["Roomtone", "Raumton"] + - label: Nah, mittel, fern + hint: Wie beim Zeichnen der Standpunkt, von dem aus man auf etwas schaut. + accept: ["Perspektive", "perspectives", "perspective"] + explanation: | + Ansage, Room Tone und Perspektive sind drei Begriffe, die in Sessions für Spielgeräusche vorkommen. Die Ansage ordnet Takes zu, der Room Tone füllt Lücken und liefert das Rauschprofil. Perspektiven geben dem Spiel nahe und ferne Fassungen desselben Klangs, weil ein Spieler eine Quelle aus vielen Abständen hört; Wu nimmt Waffen deshalb für Spiele anders auf als für Filme. Wer die Wörter kennt, versteht Protokolle und Gespräche im Team. + resources: + - title: Sound On Sound · Glossar, Slate + url: https://www.soundonsound.com/glossary/slate + - title: A Sound Effect · Watson Wu, Weapon sound effects recording + url: https://www.asoundeffect.com/weapon-sound-effects-recording/ + + - id: rumpeln-im-take + type: bug + title: Jeder Take knarzt + prompt: | + So beschreibst du deinen Ablauf für einen Take. Beim Abhören reibt und knarzt es leise in jedem Take, und der Klang wirkt dumpfer als im Raum. Finde die zwei Schritte, die das verursachen. + lang: prose + code: | + Ich starte die Aufnahme und warte zwei Sekunden. + Ich sage: „Take drei, Schublade, zwanzig Zentimeter.“ + Ich warte zwei Sekunden. + Ich halte das Handy in der Hand. + Mein Daumen liegt auf der Unterkante des Handys. + Ich ziehe die Schublade auf und lasse den Klang drei Sekunden ausklingen. + Ich stoppe und höre den Take mit Kopfhörern an. + lines: [4, 5] + explanation: | + Ein Handy in der Hand überträgt jede Bewegung der Finger und Sehnen ins Gehäuse; das ist das Reiben und Knarzen, das NPR als „fingers sliding across the surface of the mic or tendons and bones creaking“ beschreibt. An der Unterkante sitzt bei vielen Geräten eine Mikrofonöffnung; ein Daumen darauf macht den Klang dumpf. Leg das Handy auf eine weiche Unterlage und halte die Öffnungen frei. Die übrigen Schritte sind richtig: Ansage, Stille, Ausklingen, Abhören. + resources: + - title: NPR Training · Which mic should I use? + url: https://www.npr.org/sections/npr-training/2025/05/28/g-s1-66934/which-mic-should-i-use-mics-part-1 + + - id: room-tone-passt-nicht + type: bug + title: Der Room Tone fällt auf + prompt: | + Dein Protokoll einer Session. In REAPER klingt der Room Tone halliger und dünner als die Pausen zwischen den Takes. Als Füller zwischen zwei Takes hört man ihn sofort. Finde die Zeile, die das erklärt. + lang: text + code: | + 2026-10-24 Küche, Fenster zu + Handy, App: Sprachmemos + Breites Spektrum, Mono + T1–T4 Schublade 20 cm + T5 Schublade 60 cm + T6 Room Tone 30 s, bei 2 m + Notiz: T2 Kühlschrank lief noch + lines: [6] + explanation: | + Room Tone muss dieselbe Position und denselben Pegel haben wie die Takes, sonst klingt er nach einem anderen Raum. NPR schreibt ausdrücklich, das Mikrofon an derselben Stelle zu lassen und den Pegel nicht zu ändern. In 2 m Abstand statt an der Stelle der nahen Takes hört das Handy mehr Raumhall und weniger Nahes, deshalb klingt T6 halliger. Die Notiz zu T2 ist kein Fehler; solche Notizen gehören ins Protokoll. + resources: + - title: NPR Training · Field recording checklist + url: https://training.npr.org/2017/06/30/get-great-sound-every-time-with-this-field-recording-checklist/ + + - id: leise-ist-nicht-sicher + type: explain + title: „Ich hebe es später an“ + prompt: | + Jemand aus deinem Team sagt diesen Satz. Erkläre in eigenen Worten, warum der Plan nicht aufgeht und was stattdessen hilft. + lang: prose + code: | + Im Hof ist es laut. Ich nehme deshalb ganz leise auf, dann ist weniger Lärm drauf, und ich hebe den Klang später in REAPER an. + points: + - Leiser aufnehmen ändert das Verhältnis von Klang zu Hintergrund nicht; beides wird gleich leiser. + - Beim Anheben kommt der Hintergrund mit, dazu das Rauschen der Elektronik. + - Das Verhältnis ändert nur der Abstand; jede Halbierung bringt bis zu 6 dB. + - Den Pegel stellt man so ein, dass der lauteste Moment nicht übersteuert, nicht so leise wie möglich. + explanation: | + Der Plan verwechselt Pegel und Verhältnis. Ein leiserer Pegel macht Klang und Hof gleichermaßen leiser; NPR schreibt dazu, man nehme dasselbe Verhältnis auf, nur leiser. Hebt man später an, kommt der Hof zurück und das Rauschen des Geräts dazu. Was hilft, ist Nähe: Bei halbem Abstand liegt der Klang bis zu 6 dB weiter vor dem Hintergrund. Den Pegel stellt man über einen Probe-Take mit dem lautesten Moment ein. + resources: + - title: NPR Training · Audio production FAQ + url: https://training.npr.org/2017/05/19/audio-production-faq-headphones-levels-mics/ + + - id: app-check + type: practice + title: Deine App mit einem Prüfmuster prüfen + prompt: | + Nimm mit deiner Aufnahme-App einen Klatscher als Prüfmuster auf, mit Ansage. Notiere in einer kurzen Liste, was du über Format, Filter, Pegelregelung und Kanäle herausfindest, und was die App nicht anzeigt. Hör die Probe mit Kopfhörern an und notiere, ab welchem Abstand der Klatscher nicht mehr übersteuert. + lang: prose + hints: + - Such in den Einstellungen der App nach Wörtern wie Format, Qualität, verlustfrei, Rauschunterdrückung, Sprachmodus, Mono, Stereo. + - Bei Sprachmemos stehen Filter im Kontrollzentrum, der Aufnahmemodus unter Einstellungen → Apps → Sprachmemos. + checklist: + - Du hast notiert, in welchem Format die App speichert, oder dass sie es nicht anzeigt. + - Du hast notiert, welche Filter oder Modi es gibt und wie sie bei der Probe standen. + - Die Probe beginnt mit einer Ansage, danach folgen mindestens zwei Sekunden Stille. + - Du hast den Abstand notiert, bei dem der Klatscher nicht übersteuert. + - Du hast eine offene Frage notiert, die du am PC an der Datei prüfst, etwa die Abtastrate. + explanation: | + Der Klatscher ist kurz und laut und damit das härteste Prüfmuster für Pegel und Filter. Was die App nicht anzeigt, prüfst du an der Datei, nicht an Annahmen. Die Liste ist der Anfang deines Protokolls: Gerät, App und Einstellung gehören in jede Session. Den Klatscher kannst du später als Prüfklick für [Audio zuordnen](/godot/audio) verwenden. + resources: + - title: Apple Support · Stereo-, Mono- oder 3D-Audio aufnehmen + url: https://support.apple.com/de-de/guide/iphone/iph4d2a39a3b/ios#iphc30ff1a19 + + - id: eigene-session + type: practice + title: Eine kleine Session mit Protokoll + prompt: | + Wähl einen Gegenstand in deiner Wohnung und nimm ihn auf: mindestens drei Takes nah, zwei weiter weg, jeder mit Ansage, danach 30 Sekunden Room Tone. Schreib das Protokoll in wenigen Zeilen auf. Welcher Gegenstand und welche Bewegung, entscheidest du. + lang: prose + hints: + - Datum, Ort, Gerät und App, Einstellung, Abstand je Take, Room Tone, Notizen. + checklist: + - Das Protokoll nennt Datum, Ort, Gerät, App und Einstellung. + - Jeder Take hat eine Nummer und einen Abstand, und die Ansage im Take sagt dasselbe. + - Der Room Tone ist 30 Sekunden lang und an derselben Stelle mit demselben Pegel aufgenommen wie die nahen Takes. + - Auf keinem Take ist eine verständliche Stimme Dritter. + - Die Originaldatei ist unverändert, und ihr Name folgt einem Schema für Sessions, das du notiert hast, etwa mit Datum, Ort und Gegenstand. + explanation: | + Die Session übt den ganzen Ablauf an einem Gegenstand, ohne Anspruch auf einen fertigen Klang. Was du aufnimmst, bleibt deine Wahl; geprüft wird, ob die Aufnahme später auffindbar und schneidbar ist. Das Material gehört in [REAPER als Werkbank](/sound/reaper-werkbank), wo du es schneidest und säuberst. + resources: + - title: NPR Training · Active sound + url: https://training.npr.org/2015/09/29/active-sound-how-to-find-it-record-it-and-use-it/ diff --git a/content/sound/modules/foley-umgebung.yaml b/content/sound/modules/foley-umgebung.yaml new file mode 100644 index 0000000..ede4386 --- /dev/null +++ b/content/sound/modules/foley-umgebung.yaml @@ -0,0 +1,509 @@ +id: foley-umgebung +title: Foley, Atmo und eigenes Material umbauen +level: 2 +minutes: 140 +summary: Wie du Foley zu einem Clip deiner eigenen Szene anlegst, eine Atmo aus nahtlosem Bett und zufälligen Spot-Klängen baust und eigene Aufnahmen mit den Bordmitteln von REAPER zu neuem Material umformst. +place: pc +goals: + - Du nimmst mit Godots Movie Maker einen Clip deiner Szene auf, holst ihn in REAPER, ins Videofenster oder als PNG-Folge mit WAV, und setzt Foley auf Frame-Nummern. + - Du trennst eine Atmo in Bett und Spot-Klänge und baust das Bett mit getauschten Hälften und Crossfade zu einem nahtlosen Loop, dessen Länge du vorher ausrechnest. + - Du lässt Spot-Klänge in Godot in zufälligen Pausen an zufälligen Orten spielen und begründest, warum die Pause erst nach dem Klang beginnt. + - Du formst eigenes Material mit Abspielrate, Pitch-Modi, Reverse und Faltung mit einer selbst aufgenommenen Impulsantwort um. + - Du findest die Ursache, wenn ein Loop im Spiel an der Naht klickt oder aussetzt. +resources: + - title: Shaun Gallagher · Foley für Mortal Kombat X (A Sound Effect) + url: https://www.asoundeffect.com/mortal-kombat-x-sound/ + - title: Mark Hollier · Eon Altar Ambience System (Game Developer) + url: https://www.gamedeveloper.com/audio/video-game-audio-under-the-hood-eon-altar-ambience-system + - title: Godot Docs · Creating movies, Command line usage + url: https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#command-line-usage + - title: REAPER User Guide 7.81 · Using REAPER with Video (S. 399) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=399 +lesson: | + Du hast eine Aktion deiner Figur in der Testszene, mit Treffer und eigenen Varianten aus [Layering und Varianten](/sound/layering-varianten). Zwischen den Treffern ist es still, als stünde die Figur im luftleeren Raum: kein Stoff, der sich bewegt, kein Ort, der klingt. Dafür sind zwei Handwerke da. **Foley** sind Geräusche, die zu einer Bewegung im Bild aufgenommen werden: Schritte, Kleidung, Dinge, die angefasst werden. Eine **Atmo** (Ambience) ist der Klang des Ortes, aus einem durchgehenden Bett und einzelnen Spot-Klängen. + + Aus der Musik kennst du Aufnahmen zum Klick und Loops auf Taktgrenzen. Im Spiel kommt hinzu: Foley hängt an Frames statt an Takten, und ein Bett läuft so lange, wie der Spieler an einem Ort bleibt, vielleicht zwanzig Minuten. Dieses Modul zeigt, wie du beides mit Handy, REAPER-Bordmitteln und Godot baust, und wie du eigenes Material zu neuem umformst. Welche Klänge dein Ort hat, entscheidest du. Seitenangaben beziehen sich auf die PDF-Seiten des [REAPER User Guide 7.81](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf). + + ## Foley im Spiel: zum Bild oder als Ereignis + + Shaun Gallagher übernahm den Foley für den Story-Modus von Mortal Kombat X. Er beschreibt die übliche Arbeit so: „footstep, cloth, and interaction foley is performed live to picture“, danach wird geschnitten. In Spielen ändert sich das Bild aber laufend, weil viele Leute gleichzeitig an Figuren und Umgebungen arbeiten: Schuhe, Böden, Kostüme. Das Team spielte Schritte deshalb über Sampler-Patches mit einzelnen Schritten per MIDI zum Bild ein; Timing und Lautstärke ließen sich danach Note für Note verschieben, und bei einem neuen Boden tauschte man nur das Patch. Kleidung und Interaktionen kamen aus eigenen Aufnahmen und Bibliotheken ([A Sound Effect](https://www.asoundeffect.com/mortal-kombat-x-sound/)). + + Für dein Spiel heißt das zweierlei: + + - **Als Ereignis.** Was der Spieler auslöst, spielt die Engine im passenden Frame, etwa Schritte über `frame_changed` ([Audio zuordnen](/godot/audio)). Hier nimmst du keine Darbietung auf, sondern Varianten: viele Takes, geschnitten, gesäubert, als Regionen gerendert. + - **Zum Bild.** Feste Abläufe, etwa ein Auftritt, eine Animation, die immer gleich läuft, oder ein Trailer, bekommen Foley zu einem Clip. + + Die Sampler-Methode geht mit Bordmitteln: ReaSamplOmatic5000 nimmt eine Aufnahme per Drag-and-drop oder mit *Import item from arrange* (S. 347). Spielen kannst du ohne Keyboard über das *Virtual MIDI Keyboard* (Alt+B, S. 72) oder indem du Noten im MIDI-Editor auf die Frame-Positionen zeichnest. Kontakt bleibt unter Linux ein offener Punkt; der Kurs braucht es nicht. + + ## Der eigene Clip: Videofenster oder PNG-Folge + + Einen Clip deiner Szene nimmt Godots **Movie Maker** auf. Er rendert nicht in Echtzeit, sondern Frame für Frame mit fester Bildrate, und schreibt den Ton mit; hören kannst du ihn während der Aufnahme nicht ([Creating movies](https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#command-line-usage)). Im Projektordner: + + ```text + godot --path . --write-movie clip.avi --fixed-fps 60 --quit-after 600 + ``` + + `--quit-after` zählt Frames, 600 sind bei 60 Frames pro Sekunde zehn Sekunden. Ein relativer Pfad landet im Projektordner. AVI enthält MJPEG-Video und unkomprimierten Ton. Beende die Aufnahme über `--quit-after` oder das Fensterkreuz; nach Strg+C fehlt der AVI-Datei laut Doku die Dauer, und Schnittprogramme öffnen sie dann womöglich nicht. Gemessen: Mit `--headless` stürzte Godot 4.7.2 im Movie Maker ab, starte also mit Fenster. Die Mischrate der Aufnahme steht standardmäßig auf 48 000 und muss laut Doku durch die Bildrate teilbar sein, sonst laufen Bild und Ton auseinander; 48 000 / 60 = 800 passt. Damit jede Aufnahme gleich abläuft, steuerst du die Eingaben mit dem Drehbuch der [Testszene](/combat/testszene-angriff) statt mit der Hand. + + In REAPER holst du die AVI mit *Insert → Media File* oder per Drag-and-drop, *View → Video Window* (Strg+Umschalt+V) zeigt das Bild (S. 399). Unter *Project Settings → Video* stellst du die Bildrate auf 60, die Zeitleiste per Rechtsklick auf *Absolute Frames* (S. 46, S. 408, S. 410). Für den Ton der AVI gibt es in den Eigenschaften der Videodatei *Ignore audio* (S. 409–410). + + Unter Linux ist das Videofenster der unsichere Teil. Der Guide nennt für Windows DirectShow und für macOS QuickTime (S. 399), zu Linux sagt er nichts Eigenes. Er beschreibt unter *Preferences → Media → Video* eine Rangfolge der Decoder mit ffmpeg und vlc und die Option *Show decoder information* (S. 409, S. 426). Ob REAPER 7.81 unter CachyOS die AVI aus Godot abspielt, ist ungeprüft. Bleibt das Fenster schwarz, zeigt *Show decoder information*, welcher Decoder es versucht hat; die Windows-Partition ist ein zweiter Weg. + + Es geht auch ohne Videofenster. Nimm den Clip als PNG-Folge auf, in einen Ordner außerhalb des Projekts, den du vorher anlegst; im Projektordner würde der Editor jede PNG importieren: + + ```text + godot --path . --write-movie ~/sound/clips/schlag/clip.png --fixed-fps 60 --quit-after 600 + ``` + + Godot schreibt `clip00000000.png`, `clip00000001.png` und so weiter, eine Datei pro Frame ab 0, und daneben `clip.wav` ([PNG](https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#png)). Die WAV öffnet REAPER wie jede Aufnahme, und Datei Nummer k beginnt in ihr bei k/60 Sekunden. Gemessen mit Godot 4.7.2: Eine Testszene schaltete im 21. Physik-Frame das Bild auf Weiß und startete einen Klick. Weiß war das Bild ab `clip00000020.png`, und der Klick begann in der WAV bei Sample 16 450, 450 Samples nach dem Beginn von Frame 20 (16 000 bei 48 kHz). Welches Ereignis in welchem Frame liegt, siehst du im Bildbetrachter an der Dateinummer oder im Log der Testszene. Lass die Szene an einem bekannten Ereignis einen Prüfklick spielen: An ihm prüfst du in der WAV, ob deine Zählung stimmt, bevor du Foley auf die Frames legst. + + **Aufnehmen zum Bild mit dem Handy:** Setz einen Marker zwei Sekunden vor die erste Bewegung. Starte die Handy-Aufnahme, spiel das Projekt ab und klatsche einmal, wenn der Cursor den Marker erreicht, dann spiel die Bewegungen mit. In REAPER legst du die Transiente des Klatschers auf den Marker. Die Ausrichtung ist so genau wie dein Klatschen; die einzelnen Ereignisse schiebst du danach auf ihre Frames, wie Gallagher seine MIDI-Noten. Halte die Takes kurz, denn zwei Geräte laufen nie genau gleich schnell. Wie du mit dem Handy aufnimmst, steht in [Feldaufnahme mit dem Handy](/sound/feldaufnahme). + + ## Atmo: Bett und Spot-Klänge + + Das **Bett** läuft durchgehend: der Grundton eines Ortes, als Loop. **Spot-Klänge** sind einzelne Ereignisse darüber, die zu unregelmäßigen Zeiten an verschiedenen Stellen kommen. Wie man das an veröffentlichten Spielen hört, steht in [Kritisch hören](/sound/hoeren-analysieren). + + Ronny Mraz beschreibt den ersten Schritt am Bett: auffällige Einzelgeräusche herausnehmen, denn wer „the same bird call every 15 seconds“ hört, erkennt den Loop. Die herausgenommenen Stücke hebt er auf, als Spot-Klänge ([Splice, 2021](https://splice.com/blog/audio-soundscape-for-video-games/)). Mark Hollier baute für Eon Altar je Zone ein Bett, mit Crossfade beim Wechsel, und darüber „One Shot Sweeteners“: eine zufällige Pause, in seiner Demo zwischen 5 und 10 Sekunden, dann eine zufällige Gruppe aus 5 bis 10 Klängen, daraus eine von 5 Varianten, an einem zufälligen Ort um den Hörer. Sein Problem danach: Klänge spielten übereinander. Seine Lösung: erst warten, bis der Klang zu Ende ist, dann die nächste Pause starten ([Game Developer, 2016](https://www.gamedeveloper.com/audio/video-game-audio-under-the-hood-eon-altar-ambience-system)). + + Dasselbe in Godot. Als `stream` trägst du einen `AudioStreamRandomizer` mit deinen Spot-Klängen ein: + + ```gdscript title=spot_sounds.gd + extends Node2D + ## Spielt Spot-Klänge einer Atmo in zufälligen Pausen an zufälligen Orten + ## um diesen Node. Die nächste Pause beginnt erst, wenn der Klang zu Ende ist. + ## Alle Zahlen sind Platzhalter. + + @export var stream: AudioStream ## z. B. ein AudioStreamRandomizer mit deinen Spot-Klängen + @export var min_pause := 4.0 ## Sekunden Stille, mindestens + @export var max_pause := 12.0 ## Sekunden Stille, höchstens + @export var radius := 160.0 ## Pixel um diesen Node + @export var bus := &"Atmo" ## muss im Audio-Panel existieren + + var _player := AudioStreamPlayer2D.new() + var _timer := Timer.new() + + + func _ready() -> void: + _player.stream = stream + _player.bus = bus + add_child(_player) + _timer.one_shot = true + add_child(_timer) + _timer.timeout.connect(_play_spot) + _player.finished.connect(_wait) + _wait() + + + func _wait() -> void: + _timer.start(randf_range(min_pause, max_pause)) + + + func _play_spot() -> void: + _player.position = Vector2.from_angle(randf() * TAU) * randf() * radius + _player.play() + ``` + + Gemessen in Godot 4.7.2 mit einem Prüfklang von 2 Sekunden und Pausen zwischen 1 und 3 Sekunden, 40 Sekunden lang: 9 Starts, 9-mal `finished`, kein Abbruch; zwischen zwei Starts lagen 3,39 bis 5,04 Sekunden. In einer Fassung, die die Pause schon beim Start des Klangs neu startet, statt bei `finished`, gab es 17 Starts und 6 Abbrüche: Der neue Klang schnitt den alten ab, weil ein Player standardmäßig nur eine Stimme hat (`max_polyphony`, siehe [Audio zuordnen](/godot/audio)). Das Bett selbst läuft auf einem eigenen `AudioStreamPlayer` ohne Ort, mit Loop aus dem Import-Dock. + + > [!PRAXIS] Was aus dem Bett fliegt, wird Spot-Klang + > Mraz und Hollier arbeiten mit derselben Trennung: Das Bett ist gleichmäßig und unauffällig, alles Erkennbare spielt die Engine einzeln, zufällig und mit Varianten. Mraz macht die Spot-Klänge aus genau den Stücken, die er aus dem Bett geschnitten hat. Ein Bett mit einem Vogelruf darin ist nach drei Durchläufen ein Loop; derselbe Ruf als Spot-Klang mit Varianten an wechselnden Orten bleibt Teil des Ortes. + + ## Der nahtlose Loop + + Ein Bett endet und beginnt im Spiel ohne Pause. Liegt der letzte Sample-Wert weit weg vom ersten, springt das Signal an der Naht, und das klickt bei jedem Durchlauf. Mraz' Weg: die Aufnahme etwa in der Mitte teilen, an einer ruhigen Stelle, die Hälften tauschen und am neuen Stoß mit einem Crossfade verbinden. An die äußeren Enden setzt er zusätzlich sehr kurze Fades gegen Klicks. Als Länge nennt er 10 bis 30 Sekunden als Startpunkt. Warum es funktioniert, zeigt ein kleines Modell mit einem Sinus, der nicht in ganzen Perioden endet: + + ```python run + import math + + rate = 1000 + werte = [math.sin(2 * math.pi * 3.3 * i / rate) for i in range(rate)] + + def sprung(a, b): + return round(abs(a - b), 3) + + print("Naht vorher:", sprung(werte[-1], werte[0])) + mitte = len(werte) // 2 + neu = werte[mitte:] + werte[:mitte] + print("Naht nachher:", sprung(neu[-1], neu[0])) + print("Stoß in der Mitte:", sprung(neu[mitte - 1], neu[mitte])) + print("größter Schritt sonst:", max(sprung(a, b) for a, b in zip(werte, werte[1:]))) + ``` + + Nach dem Tausch sind Ende und Anfang der Datei zwei Samples, die in der Aufnahme nebeneinanderlagen; der Sprung ist so klein wie jeder andere Schritt. Der alte Sprung sitzt jetzt in der Mitte, und dort kannst du ihn mit einem Crossfade überdecken, an der Naht eines Loops nicht. Weil an der Naht jetzt Nachbarn liegen, senkt ein Fade dort den Pegel kurz ab; ob du Mraz' kurze Fades brauchst, hörst du in Schleife. + + In REAPER teilst du mit S, schiebst die Hälften und lässt sie überlappen; mit *Auto Crossfade* (Alt+X) entsteht der Crossfade beim Überlappen, im *Crossfade Editor* stellst du Länge und Form ein (S. 145). Der Crossfade verkürzt den Loop um seine Länge: Aus 24 Sekunden mit 2 Sekunden Crossfade werden 22. Zum Abhören legst du eine Region genau über den Loop und spielst sie mit *Repeat* (R) in Schleife; der Region/Marker Manager kann eine Region beim Auswählen durchspielen und wiederholen (S. 46, S. 181). Gerendert wird ohne Tail, sonst hängt REAPER Zeit an. Liegt ein Hall auf dem Bett, braucht der Anfang des Loops die Fahne vom Ende; dafür gibt es *2nd pass render*, das den Bereich vor dem Rendern einmal abspielt (S. 415). Ausführlich steht das in [Eigenes Stück aufbereiten](/sound/loops-stems). + + In Godot stellst du im Import-Dock *Loop Mode* auf *Forward* und lässt *Loop End* auf −1, das Ende der Datei ([Edit > Loop Mode](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-loop-mode)). Gemessen in 4.7.2: Der Übergang im Spiel ist so glatt wie in der Datei, bis auf ein ausgelassenes Sample (Details im Kasten). Ein Klick in der Datei bleibt ein Klick. + + > [!DEEP] Was Godot 4.7.2 an der Naht tut + > Eine WAV mit 44 100 Samples, importiert mit *Forward* und Loop-Ende −1, hatte danach `loop_end` 44 099. Offline über `mix_audio()` gemischt, wiederholte Godot ab dem zweiten Durchlauf die Samples 1 bis 44 099; Sample 0 kam nur beim ersten Mal. Bei einem Sinus mit ganzen Perioden war der Schritt an der Naht deshalb 0,063 statt 0,031 wie im Inneren, ein ausgelassenes Sample. Setzt du `loop_end` per Code auf die Anzahl der Samples, also eins zu hoch, entstand an der Naht ein einzelner falscher Wert von 0,418 statt nahe 0, ein Klick. Lass das Loop-Ende im Import-Dock auf −1. + + Wie oft hört man den Loop? Bei 22 Sekunden Länge 2,7-mal pro Minute. Auffällige Stellen im Bett hört der Spieler also alle 22 Sekunden. Dagegen helfen ein längerer Loop, ein gleichmäßigeres Bett und Spot-Klänge, die das Ohr beschäftigen. + + ## Eigenes Material umbauen + + Aus wenigen Aufnahmen wird mehr Material, wenn du sie umformst. Alles hier sind Bordmittel: + + - **Abspielrate ohne Tonhöhenerhalt.** In den *Media Item Properties* (F2) setzt du *Playback rate* und schaltest *Preserve pitch* aus, oder du ziehst mit Alt an der Kante des Items (S. 134, S. 195). Tonhöhe und Länge ändern sich zusammen, wie bei einer Bandmaschine: halbe Rate, eine Oktave tiefer, doppelt so lang. + - **Tonhöhe und Länge getrennt.** *Pitch adjust* in Halbtönen, mit wählbarem Algorithmus: élastique Pro, Efficient oder Soloist, Rubber Band, SoundTouch für Dehnungen bis zum 1000-Fachen und Rrreeeaaa, laut Guide „useful if you want big stretches that do not preserve transients“ ([S. 193](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=193)). Welcher Modus zu deinem Material passt, hörst du. + - **Reverse.** Die Item-Eigenschaften haben eine Option zum Umkehren, *Reverse items as new take* legt die umgekehrte Fassung als neuen Take daneben und lässt das Original stehen (S. 135, S. 166). + - **Faltung mit eigener Impulsantwort.** ReaVerb ist ein Faltungshall: Er nutzt die Impulsantwort eines Raums, also die Aufnahme eines kurzen Signals in diesem Raum ([S. 331](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=331)). Mit *Add → File* lädst du eine Datei als Impulsantwort, weitere Module normalisieren, kürzen, dehnen oder kehren sie um (S. 332–333). Deine eigene: ein Klatscher im Raum, mit dem Handy aufgenommen, direkt vor der Transiente geschnitten, bis ins Rauschen ausklingen lassen, Fade am Ende. Ein Klatscher ist kein idealer Impuls, er färbt mit; für ein Spielgeräusch kann genau das gewollt sein. ReaVerb lädt jede Datei als Impulsantwort; auch ein Schlag auf Metall ergibt so einen „Raum“, wenn auch keinen, den es gibt. + + Für extreme Dehnungen gibt es PaulXStretch. Laut README läuft es unter Linux nur als eigenständiges Programm, das du selbst baust; als Plugin gibt es es für macOS, Windows und iOS ([PaulXStretch](https://github.com/essej/paulxstretch)). Emergence ist unter Linux ungeprüft. Die Bordmittel Rrreeeaaa und SoundTouch decken den Pflichtteil ab. Fremdes Material behält nach dem Umbau seine Lizenz; das hältst du in der Quellenliste fest ([Quellen und Lizenzen](/sound/quellen-lizenzen)). + + > [!INTERVIEW] + > „Wie baust du eine Ambience, die nach zwanzig Minuten nicht nervt?“ Gute Antwort, etwa als Sound Designer: Ich trenne Bett und Spot-Klänge. Aus dem Bett schneide ich alles Auffällige heraus, mache es mit getauschten Hälften und Crossfade nahtlos und halte es lang genug für die Verweildauer am Ort. Die herausgeschnittenen Stücke werden Spot-Klänge mit Varianten, die die Engine in zufälligen Pausen an wechselnden Orten spielt, erst nach dem Ende des vorigen. Dann höre ich im Spiel so lange zu, wie ein Spieler dort bleibt, und notiere, wann mir etwas zum ersten Mal auffällt. + + ## Häufige Fehler + + - **Ein auffälliges Ereignis im Bett.** Es verrät den Loop bei jedem Durchlauf. + - **Loop ohne getauschte Hälften.** An der Naht klickt es. + - **Tail beim Rendern eines Loops.** Die Datei wird länger als der Loop, im Spiel setzt das Bett kurz aus. + - **Loop-Ende per Code auf die Anzahl der Samples.** Godot liest einen Wert zu weit; im Import-Dock bleibt es bei −1. + - **Pause ab dem Start des Spot-Klangs.** Längere Klänge werden vom nächsten abgeschnitten. + - **Movie Maker headless oder mit Strg+C beendet.** Absturz oder eine AVI ohne Dauer. + - **PNG-Folge in den Projektordner geschrieben.** Der Editor importiert jede einzelne Datei. + - **Foley zum Bild für Ereignisse, die die Engine auslöst.** Dort brauchst du Varianten, keine Darbietung. + - **Umgebautes fremdes Material ohne Lizenzvermerk.** Die Lizenz gilt weiter. + + ## Zusammenfassung + + - Foley für Ereignisse sind Varianten, Foley für feste Abläufe entsteht zum Bild. + - Movie Maker nimmt einen Clip mit fester Bildrate auf; in REAPER Videofenster, 60 Frames pro Sekunde, Zeitleiste in Frames. Ob das Videofenster unter CachyOS läuft, ist ungeprüft; ohne Video trägt eine PNG-Folge mit WAV. + - Eine Atmo ist ein gleichmäßiges Bett und Spot-Klänge; Auffälliges aus dem Bett wird Spot-Klang. + - Spot-Klänge spielt die Engine zufällig nach Pause und Ort, die Pause beginnt nach `finished`. + - Nahtlos: Hälften tauschen, Crossfade in der Mitte, Region ohne Tail; der Crossfade kürzt den Loop. + - Godot 4.7.2 übernimmt den Übergang der Datei, bis auf ein ausgelassenes Sample, wenn das Loop-Ende im Import-Dock auf −1 steht. + - Umbauen mit Bordmitteln: Abspielrate, Pitch-Modi, Reverse, ReaVerb mit eigener Impulsantwort. +exercises: + - id: bett-oder-spot + type: choice + title: Was gehört ins Bett? + prompt: | + Du baust die Atmo eines Ortes aus einer zehnminütigen Aufnahme. Welche Teile gehören als Spot-Klänge in die Engine statt ins Bett? + options: + - text: Ein einzelner, gut erkennbarer Ruf, der einmal in der Aufnahme vorkommt + correct: true + why: Im Bett käme er bei jedem Durchlauf an derselben Stelle. Als Spot-Klang kommt er zufällig und an wechselnden Orten. + - text: Ein knackender Ast, der deutlich lauter ist als alles um ihn herum + correct: true + why: Was heraussticht, verrät den Loop. Aus dem Bett schneiden und als Spot-Klang mit Varianten verwenden. + - text: Ein gleichmäßiges Rauschen aus Wind und fernen Blättern + correct: false + why: Das ist das Bett. Es soll gleichmäßig sein, damit die Naht und die Wiederholung nicht auffallen. + - text: Der Grundton eines Raumes ohne erkennbare Ereignisse + correct: false + why: Auch das ist Bett, im Grunde ein Room Tone. Er füllt die Stille und trägt keine Information, die man wiedererkennt. + explanation: | + Die Trennung folgt einer Frage: Würde man es beim dritten Durchlauf wiedererkennen? Alles Erkennbare macht einen Loop hörbar und gehört deshalb in die Engine, die es zufällig verteilt. Das Bett bleibt gleichmäßig. Welche Klänge dein Ort hat, entscheidest du; die Trennung bleibt dieselbe. + resources: + - title: Ronny Mraz · How to create an audio soundscape for video games (Splice) + url: https://splice.com/blog/audio-soundscape-for-video-games/ + + - id: loop-ordnen + type: order + title: Ein Bett nahtlos machen + prompt: | + Bring die Schritte in die richtige Reihenfolge. + lang: prose + lines: | + Auffällige Einzelgeräusche aus der Aufnahme schneiden und für Spot-Klänge aufheben. + Die Aufnahme an einer ruhigen Stelle etwa in der Mitte teilen. + Die Hälften tauschen, die zweite kommt nach vorn. + Die Hälften am neuen Stoß überlappen lassen und mit einem Crossfade verbinden. + Eine Region genau über den Loop legen und in Schleife auf Klicks und Pegellöcher abhören. + Ohne Tail rendern und in Godot mit Loop-Modus Forward importieren. + explanation: | + Erst kommt heraus, was den Loop verraten würde, dann wird getauscht. Nach dem Tausch liegen an Anfang und Ende der Datei zwei Stellen, die in der Aufnahme nebeneinanderlagen; der alte Stoß sitzt in der Mitte und bekommt den Crossfade. Abhören in Schleife zeigt, ob es noch klickt, bevor du renderst. Ohne Tail bleibt die Datei so lang wie der Loop. + resources: + - title: REAPER User Guide 7.81 · Crossfades (S. 145) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=145 + + - id: loop-laenge + type: output + title: Wie lang wird der Loop? + prompt: | + Eine Aufnahme von 24 Sekunden wird mit getauschten Hälften und 2 Sekunden Crossfade zum Loop. Was gibt das Programm aus? + lang: python + code: | + laenge = 24.0 # Sekunden Aufnahme, Platzhalter + xfade = 2.0 # Sekunden Crossfade am Stoß + loop = laenge - xfade + print(loop) + print(round(loop * 48000)) + print(round(60 / loop, 2)) + expected: | + 22.0 + 1056000 + 2.73 + explanation: | + Im Crossfade liegen beide Hälften übereinander, also wird der Loop um die Länge des Crossfades kürzer: 22 Sekunden, bei 48 kHz 1 056 000 Samples. Pro Minute läuft er 2,73-mal durch. Ein auffälliges Ereignis im Bett hört der Spieler damit alle 22 Sekunden. Die Länge der Region in REAPER sollte genau diese Zahl zeigen. + resources: + - title: Ronny Mraz · How to create an audio soundscape for video games (Splice) + url: https://splice.com/blog/audio-soundscape-for-video-games/ + + - id: spots-pro-minute + type: output + title: Wie oft kommt ein Spot-Klang? + prompt: | + Pausen gleichverteilt zwischen 4 und 12 Sekunden, Spot-Klänge im Mittel 1,5 Sekunden lang (Platzhalter). Die letzte Zeile rechnet die fehlerhafte Fassung, die die Pause schon beim Start des Klangs beginnt. Was gibt das Programm aus? + lang: python + code: | + min_pause, max_pause = 4.0, 12.0 + laenge = 1.5 + abstand = (min_pause + max_pause) / 2 + laenge + print(abstand) + print(round(60 / abstand, 1)) + print(round(60 / ((min_pause + max_pause) / 2), 1)) + expected: | + 9.5 + 6.3 + 7.5 + explanation: | + Beginnt die Pause erst nach `finished`, liegen zwischen zwei Starts im Mittel 8 Sekunden Pause plus 1,5 Sekunden Klang, etwa 6,3 Spot-Klänge pro Minute. Die fehlerhafte Fassung zählt die Pause ab dem Start; dann kommen 7,5 pro Minute, und jeder Klang, der länger ist als die nächste Pause, wird abgeschnitten. In der Messung mit 2-Sekunden-Klängen und Pausen von 1 bis 3 Sekunden waren das 6 Abbrüche in 40 Sekunden. + resources: + - title: Godot Docs · AudioStreamPlayer2D, Signal finished + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-signal-finished + + - id: abspielrate + type: output + title: Halbe Rate, wie tief? + prompt: | + Abspielrate ohne Tonhöhenerhalt, wie *Playback rate* in REAPER mit *Preserve pitch* aus oder `pitch_scale` in Godot: Was gibt das Programm aus? + lang: python + code: | + import math + + for rate in (0.5, 1.06, 2.0): + halbtoene = 12 * math.log2(rate) + print(rate, round(halbtoene, 2), "Halbtöne, Länge mal", round(1 / rate, 2)) + expected: | + 0.5 -12.0 Halbtöne, Länge mal 2.0 + 1.06 1.01 Halbtöne, Länge mal 0.94 + 2.0 12.0 Halbtöne, Länge mal 0.5 + explanation: | + Ohne Tonhöhenerhalt hängen Tonhöhe und Länge zusammen: Die halbe Rate klingt eine Oktave tiefer und dauert doppelt so lang, die doppelte eine Oktave höher bei halber Länge. 1,06 ist etwa ein Halbton höher und macht den Klang um 6 % kürzer. In Godot gilt dasselbe: `pitch_scale` ändert laut Doku „pitch and tempo“, und auch die zufällige Tonhöhe eines `AudioStreamRandomizer` ist ein solcher Faktor. Eine Fahne, die gerade in ein Zeitfenster passt, wird mit tieferer Zufallstonhöhe länger. In REAPER schaltest du *Preserve pitch* ein oder nimmst *Pitch adjust* mit einem der Algorithmen, wenn du nur eins von beiden ändern willst. + resources: + - title: REAPER User Guide 7.81 · Pitch and Time Manipulation (S. 193) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=193 + - title: Godot Docs · AudioStreamPlayer.pitch_scale + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-property-pitch-scale + + - id: movie-maker-befehl + type: command + title: Zehn Sekunden Clip für REAPER + prompt: | + Nimm mit Movie Maker zehn Sekunden deiner Szene bei 60 Frames pro Sekunde als `clip.avi` auf. Godot soll danach selbst beenden. + context: Du bist im Projektordner der Testszene. Godot startest du mit `godot`. + answers: + - godot --write-movie clip.avi --fixed-fps 60 --quit-after 600 + - godot --write-movie clip.avi --quit-after 600 --fixed-fps 60 + - godot --fixed-fps 60 --write-movie clip.avi --quit-after 600 + - godot --fixed-fps 60 --quit-after 600 --write-movie clip.avi + - godot --quit-after 600 --write-movie clip.avi --fixed-fps 60 + - godot --quit-after 600 --fixed-fps 60 --write-movie clip.avi + - godot --path . --write-movie clip.avi --fixed-fps 60 --quit-after 600 + - godot --write-movie clip.avi --path . --fixed-fps 60 --quit-after 600 + - godot --write-movie clip.avi --fixed-fps 60 --path . --quit-after 600 + - godot --write-movie clip.avi --fixed-fps 60 --quit-after 600 --path . + - godot --path . --write-movie clip.avi --quit-after 600 --fixed-fps 60 + - godot --write-movie clip.avi --path . --quit-after 600 --fixed-fps 60 + - godot --write-movie clip.avi --quit-after 600 --path . --fixed-fps 60 + - godot --write-movie clip.avi --quit-after 600 --fixed-fps 60 --path . + - godot --path . --fixed-fps 60 --write-movie clip.avi --quit-after 600 + - godot --fixed-fps 60 --path . --write-movie clip.avi --quit-after 600 + - godot --fixed-fps 60 --write-movie clip.avi --path . --quit-after 600 + - godot --fixed-fps 60 --write-movie clip.avi --quit-after 600 --path . + - godot --path . --fixed-fps 60 --quit-after 600 --write-movie clip.avi + - godot --fixed-fps 60 --path . --quit-after 600 --write-movie clip.avi + - godot --fixed-fps 60 --quit-after 600 --path . --write-movie clip.avi + - godot --fixed-fps 60 --quit-after 600 --write-movie clip.avi --path . + - godot --path . --quit-after 600 --write-movie clip.avi --fixed-fps 60 + - godot --quit-after 600 --path . --write-movie clip.avi --fixed-fps 60 + - godot --quit-after 600 --write-movie clip.avi --path . --fixed-fps 60 + - godot --quit-after 600 --write-movie clip.avi --fixed-fps 60 --path . + - godot --path . --quit-after 600 --fixed-fps 60 --write-movie clip.avi + - godot --quit-after 600 --path . --fixed-fps 60 --write-movie clip.avi + - godot --quit-after 600 --fixed-fps 60 --path . --write-movie clip.avi + - godot --quit-after 600 --fixed-fps 60 --write-movie clip.avi --path . + explanation: | + `--write-movie` schaltet Movie Maker ein, die Endung `.avi` wählt AVI mit MJPEG und unkomprimiertem Ton. `--fixed-fps 60` legt die Bildrate fest, `--quit-after 600` beendet nach 600 Frames, also nach zehn Sekunden. Beenden über `--quit-after` ist wichtig, denn nach Strg+C fehlt der Datei die Dauer. Ohne `--headless` starten; damit stürzte Godot 4.7.2 im Movie Maker ab. + resources: + - title: Godot Docs · Creating movies, Quitting Movie Maker mode + url: https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#quitting-movie-maker-mode + + - id: spot-luecken + type: gap + title: Spot-Klänge mit Pause nach dem Klang + prompt: | + Ergänze das Skript: Die Pause ist zufällig zwischen `min_pause` und `max_pause`, der Ort liegt in einem Kreis um den Node, und die nächste Pause beginnt, wenn der Klang zu Ende ist. + lang: gdscript + code: | + func _ready() -> void: + _player.stream = stream + add_child(_player) + _timer.⟦one_shot⟧ = true + add_child(_timer) + _timer.timeout.connect(_play_spot) + _player.⟦finished⟧.connect(_wait) + _wait() + + + func _wait() -> void: + _timer.start(⟦randf_range⟧(min_pause, max_pause)) + + + func _play_spot() -> void: + _player.position = Vector2.⟦from_angle⟧(randf() * TAU) * randf() * radius + _player.play() + gaps: + - label: Timer läuft einmal + hint: Die Eigenschaft, die einen Timer nach einem Ablauf stehen lässt. + - label: Signal am Ende + hint: Das Signal, das ein Player sendet, wenn der Klang zu Ende ist. + - label: Zufällige Zahl + hint: Eine Fließkommazahl zwischen zwei Grenzen. + - label: Richtung aus Winkel + hint: Ein Einheitsvektor aus einem Winkel im Bogenmaß. + explanation: | + `one_shot` hält den Timer nach jedem Ablauf an; neu gestartet wird er nur in `_wait()`. Weil `_wait()` an `finished` hängt, beginnt die Pause erst nach dem Klang, wie in Holliers Lösung. `randf_range` liefert die Pause, `Vector2.from_angle` eine Richtung, die mit `randf() * radius` eine Entfernung bekommt. Ein Klang, der in Schleife läuft, sendet kein `finished`; Spot-Klänge dürfen deshalb nicht loopen. + resources: + - title: Godot Docs · Timer.start + url: https://docs.godotengine.org/en/stable/classes/class_timer.html#class-timer-method-start + - title: Godot Docs · Vector2.from_angle + url: https://docs.godotengine.org/en/stable/classes/class_vector2.html#class-vector2-method-from-angle + + - id: loop-setzt-aus + type: bug + title: Das Bett setzt immer wieder aus + prompt: | + Das Bett ist in REAPER nahtlos, die Region 22 Sekunden lang. Im Spiel setzt es nach 22 Sekunden kurz aus und danach bei jedem Durchlauf wieder. Die Datei im Exportordner ist 23 Sekunden lang. Welche Zeile ist schuld? + lang: text + code: | + Region bett_ort_loop, 0:00.000 bis 0:22.000 + Source Region render matrix + Tail an, 1000 ms + File name $region + Format WAV, 24 bit PCM + Godot Loop Mode Forward, Loop End −1 + lines: [3] + explanation: | + Mit Tail hängt REAPER an jede Region Zeit an, hier eine Sekunde. Die Datei ist dann 23 Sekunden lang, und Godot loopt über die ganze Datei, also auch über die angehängte Sekunde, in der das Bett schon zu Ende ist. Für Loops rendert man ohne Tail; braucht der Anfang eine Hallfahne vom Ende, ist *2nd pass render* der Weg. + resources: + - title: REAPER User Guide 7.81 · Render-Dialog mit Tail (S. 413) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=413 + - title: REAPER User Guide 7.81 · 2nd pass render (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 + + - id: spots-abgeschnitten + type: bug + title: Lange Spot-Klänge brechen ab + prompt: | + Kurze Spot-Klänge laufen gut. Lange brechen manchmal mitten ab, und die Abstände zwischen den Klängen wirken kürzer als die eingestellten Pausen plus Klang. Finde die Zeile und korrigiere sie. + lang: gdscript + code: | + func _ready() -> void: + _player.stream = stream + add_child(_player) + _timer.one_shot = true + add_child(_timer) + _timer.timeout.connect(_play_spot) + _timer.timeout.connect(_wait) + _wait() + lines: [7] + fix: + - line: 7 + answers: + - _player.finished.connect(_wait) + - _player.finished.connect(self._wait) + - _player.finished.connect(Callable(self, "_wait")) + - _player.connect("finished", _wait) + - _player.connect("finished", self._wait) + - _player.connect(&"finished", _wait) + explanation: | + Die Zeile startet die nächste Pause schon in dem Moment, in dem der Klang beginnt. Ist der Klang länger als die neue Pause, startet der nächste, während der alte noch läuft, und weil der Player nur eine Stimme hat, schneidet er ihn ab. Gemessen in Godot 4.7.2: 6 Abbrüche in 40 Sekunden statt keinem. Mit `finished` beginnt die Pause nach dem Klang, so wie Hollier es für Eon Altar gelöst hat. + resources: + - title: Mark Hollier · Eon Altar Ambience System (Game Developer) + url: https://www.gamedeveloper.com/audio/video-game-audio-under-the-hood-eon-altar-ambience-system + + - id: naht-erklaeren + type: explain + title: Warum das Tauschen die Naht versteckt + prompt: | + Erkläre an dieser Ausgabe des Modells aus der Lektion, warum getauschte Hälften einen Loop nahtlos machen und wohin der Sprung wandert. + lang: text + code: | + Naht vorher: 0.957 + Naht nachher: 0.012 + Stoß in der Mitte: 0.957 + größter Schritt sonst: 0.021 + points: + - Vorher springt das Signal an der Naht vom letzten zum ersten Sample weit, das klickt bei jedem Durchlauf. + - Nach dem Tausch sind Ende und Anfang zwei Samples, die in der Aufnahme nebeneinanderlagen; der Schritt ist so klein wie jeder andere. + - Der alte Sprung sitzt jetzt in der Mitte der Datei. + - In der Mitte lässt er sich mit einem Crossfade überdecken, an der Naht eines Loops nicht, weil dort nichts überlappt. + explanation: | + Der Loop-Punkt ist der einzige Ort, an dem du nicht überblenden kannst: Danach kommt der Anfang derselben Datei. Tauschst du die Hälften, liegen dort zwei Samples, die in der Aufnahme direkt aufeinander folgten, und der Übergang ist so glatt wie die Aufnahme. Den Sprung hast du damit nicht beseitigt, sondern in die Mitte verlegt. Dort überlappen die Hälften, und ein Crossfade macht ihn unhörbar. Im echten Material wählst du die Schnittstelle an einer ruhigen Stelle, damit der Crossfade nichts Auffälliges verdoppelt. + + - id: ir-erklaeren + type: explain + title: Ein Raum aus einem Klatscher + prompt: | + Erkläre, was diese Kette tut und was du beim Ergebnis erwarten musst. + lang: text + code: | + Aufnahme Handy, Flur, ein Klatscher + Schnitt direkt vor der Transiente, + Ausklang bis ins Rauschen, Fade am Ende + Render ir_flur_klatsch_01.wav + ReaVerb Add → File: ir_flur_klatsch_01.wav + Normalize + Spur Schritt-Varianten, ReaVerb als Track FX + points: + - ReaVerb faltet das Signal mit der Datei; die Datei wirkt als Impulsantwort, also als Abdruck des Flurs. + - Der Schnitt direkt vor der Transiente verhindert eine Verzögerung des Halls, der Fade am Ende einen harten Abbruch. + - Ein Klatscher ist kein idealer Impuls; sein eigener Klang färbt den Hall mit. + - Das Rauschen im Ausklang wird mit gefaltet, deshalb lohnt ein kurzer Ausklang oder eine gesäuberte Aufnahme. + explanation: | + Faltung heißt: Jedes Sample des Signals löst eine Kopie der Impulsantwort aus, mit seinem Wert skaliert, und alle Kopien werden addiert. Ist die Impulsantwort die Aufnahme eines kurzen Knalls in einem Raum, klingt das Ergebnis, als stünde die Quelle in diesem Raum. Ein Klatscher ist kein perfekter Knall, er hat eine eigene Klangfarbe, die mitkommt; ob sie stört oder gerade passt, hörst du. Stille vor der Transiente verzögert den ganzen Hall, Rauschen im Ausklang wird zu einem rauschenden Hall. Normalize hebt die Impulsantwort auf 0 dBFS, damit der Pegel planbar bleibt. + resources: + - title: REAPER User Guide 7.81 · ReaVerb (S. 331) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=331 + + - id: foley-zum-clip + type: practice + title: Foley zu deinem Clip + prompt: | + Nimm mit Movie Maker einen Clip einer festen Aktion deiner Szene auf, hol ihn in REAPER und lege Foley dazu: eigene Aufnahmen mit dem Handy, zum Bild eingespielt oder wild aufgenommen und auf die Frames geschoben. Spielt das Videofenster bei dir nicht, nimm den Weg ohne Bild aus der Lektion: PNG-Folge, WAV und Prüfklick. Welche Geräusche, entscheidest du. + lang: prose + checklist: + - Der Clip ist mit `--fixed-fps` und `--quit-after` aufgenommen und läuft im Videofenster, oder du hast ihn als PNG-Folge mit WAV aufgenommen und notiert, warum das Videofenster nicht ging. + - Die Zeitleiste zeigt Frames, und die Bildrate in den Projekteinstellungen entspricht der des Clips. + - Ohne Videofenster liegt der Prüfklick in der WAV in dem Frame, den du für sein Ereignis notiert hast. + - Für jedes Foley-Ereignis hast du notiert, auf welchem Frame es liegt. + - Jede Aufnahme hat eine Ansage oder einen Klatscher, und die Rohdateien liegen unverändert in deinem Rohordner. + - Mindestens eine Bewegung hast du in zwei Varianten aufgenommen und notiert, welche du nimmst und warum. + explanation: | + Die Übung verbindet Bild, Aufnahme und Schnitt in einem Ablauf, den du später für jede feste Sequenz wiederholst. Die Frame-Nummern machen dein Foley änderbar: Ändert sich die Animation, weißt du, welches Ereignis wohin wandern muss. Ob die Geräusche gut sind, entscheidest du beim Hören im Spiel, nicht diese Liste. + resources: + - title: Godot Docs · Creating movies + url: https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html + + - id: eigene-atmo + type: practice + title: Eine Atmo für einen Ort + prompt: | + Bau die Atmo für einen Ort deiner Wahl: ein Bett als nahtlosen Loop aus eigener Aufnahme und Spot-Klänge, die `spot_sounds.gd` in Godot zufällig spielt. Hör sie im Spiel mindestens zehn Minuten und notiere, was dir wann auffällt. Ort, Klänge und Pausen entscheidest du. + lang: prose + checklist: + - Das Bett ist eine Region ohne Tail, ihre Länge steht in deiner Notiz, und in Godot steht *Loop Mode* auf *Forward* mit Loop-Ende −1. + - In Schleife abgehört klickt die Naht nicht, weder in REAPER noch im Spiel. + - Mindestens ein auffälliges Stück aus der Aufnahme ist aus dem Bett geschnitten und als Spot-Klang mit Varianten verwendet. + - Die Spot-Klänge spielen über `spot_sounds.gd` auf einem eigenen Bus, und kein Klang bricht ab. + - Deine Notiz nennt für die zehn Minuten Hören mindestens einen Zeitpunkt, an dem dir eine Wiederholung auffiel, oder hält fest, dass keine auffiel. + - Fremdes Material, falls verwendet, steht mit Lizenz in deiner Quellenliste. + explanation: | + Ob eine Atmo trägt, zeigt erst langes Hören im Spiel. Die Notiz mit Zeitpunkten macht daraus eine Messung: Fällt dir nach drei Minuten etwas auf, weißt du, was du aus dem Bett nehmen oder als Spot-Klang variieren musst. Bus und Lizenz sind Ordnung, die du in [Mischung](/sound/mischung-stille) und in der Werkstatt brauchst. + resources: + - title: Godot Docs · Importing audio samples, Loop Mode + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-loop-mode diff --git a/content/sound/modules/godot-musik.yaml b/content/sound/modules/godot-musik.yaml new file mode 100644 index 0000000..a2ed755 --- /dev/null +++ b/content/sound/modules/godot-musik.yaml @@ -0,0 +1,813 @@ +id: godot-musik +title: "Musik in Godot: AudioStreamInteractive und Synchronized" +level: 3 +minutes: 160 +summary: Wie du in Godot 4.7 eigene Musik über eine Übergangstabelle zwischen Zuständen wechselst, Schichten ein- und ausblendest und mit Prüfloops misst, wann ein Wechsel zu hören ist, statt es zu vermuten. +place: pc +goals: + - Du baust eine Übergangstabelle aus Clips, Zeitpunkt, Ziel-Position, Fade-Modus und Übergangsstück und sagst vorher, wann ein Wechsel hörbar wird. + - Du schaltest über das Playback um, nicht über den Stream, und verhinderst mit einem Nachlauf, dass die Musik flattert oder neu beginnt. + - Du kennst die gemessenen Abweichungen von der Doku in 4.7.2, etwa die Einblendung nach dem Übergangsstück und `FADE_IN` bei Loops. + - Du blendest Schichten eines `AudioStreamSynchronized` per Tween ein und aus und weißt, wie sich eine Kurve in dB von einer linearen unterscheidet. + - Du prüfst Übergänge in der Combat-Testszene mit Log und Mitschnitt und liest die Wechselzeit aus der WAV-Datei ab. +resources: + - title: Godot Docs · AudioStreamInteractive · add_transition + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + - title: Godot Docs · AudioStreamPlaybackInteractive + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplaybackinteractive.html#class-audiostreamplaybackinteractive-method-switch-to-clip-by-name + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html#class-audiostreamsynchronized-method-set-sync-stream-volume + - title: Godot Docs · Creating movies (Movie Maker) + url: https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#command-line-usage + - title: Winifred Phillips · Horizontal Resequencing and Dynamic Transitions (GDC 2021) + url: https://www.gamedeveloper.com/audio/horizontal-resequencing-and-dynamic-transitions-for-game-music-composers-from-spyder-to-sackboy-gdc-2021- +lesson: | + In der Combat-Testszene beginnt der Dummy in Frame 200 seinen Telegraph. Dein Skript fragt im selben Frame den Musik-Zustand „gefahr“ an. Zu hören ist er erst bei 4,0 Sekunden, also rund Frame 240. Lässt du den Zustand enden, sobald der Angriff vorbei ist, passiert in einem echten Lauf etwas anderes: Bei den ersten beiden Telegraphs wechselt die Musik gar nicht. Beides ist kein Fehler in Godot. Die Übergangstabelle tut genau, was in ihr steht, und dein Code fragt im falschen Rhythmus an. + + Dieses Modul baut die Tabelle mit `AudioStreamInteractive`, misst mit Prüfloops, wann ein Wechsel hörbar wird, und schreibt den Code, der richtig anfragt. Es baut auf [Audio zuordnen](/godot/audio) auf (Player, Busse, Dezibel), auf [Eigenes Stück aufbereiten](/sound/loops-stems) (Import-Felder `bpm`, `beat_count`, `bar_beats`) und auf deinem Zustandsdiagramm aus [Musik für Zustände](/sound/musik-zustaende). Die Zustände „ruhe“ und „gefahr“, das Tempo und alle Längen sind Platzhalter der Testszene. Welche Zustände dein Spiel hat und wie deine Musik sie trägt, entscheidest du. + + ## Der Prüfaufbau: Prüfloops, Offline-Messung, Mitschnitt + + Gemessen wird mit **Prüfloops**, technischen Signalen statt Musik: 120 BPM, 4/4, vier Takte, auf der Eins ein hoher Klick, sonst ein tiefer. Damit du beide Zustände im Kopfhörer und in der Auswertung auseinanderhältst, spielt Zustand A nur links und Zustand B nur rechts. Das Übergangsstück klickt auf beiden Seiten. Wie du so einen Loop mit REAPERs Click Source renderst, steht in [Eigenes Stück aufbereiten](/sound/loops-stems). Für die Messungen in diesem Modul hat dieses Skript dieselben Muster erzeugt: + + ```python + import math, struct, wave + + RATE, BPM, TAKTE = 44100, 120, 4 # Platzhalter + SCHLAG = round(RATE * 60 / BPM) # 22050 Samples + + + def klick(freq): # 30 ms, schnell abklingend + return [0.5 * min(1, i / 44) * math.exp(-i / 353) * math.sin(2 * math.pi * freq * i / RATE) + for i in range(int(RATE * 0.03))] + + + def pruefloop(datei, hoch, tief, links=True, rechts=True, takte=TAKTE): + werte = [0.0] * (SCHLAG * 4 * takte) + for s in range(4 * takte): + for i, v in enumerate(klick(hoch if s % 4 == 0 else tief)): + werte[s * SCHLAG + i] += v + with wave.open(datei, "wb") as w: + w.setnchannels(2), w.setsampwidth(2), w.setframerate(RATE) + w.writeframes(b"".join(struct.pack(" void: + var musik: AudioStreamInteractive = load("res://musik.tres") + var pb := musik.instantiate_playback() as AudioStreamPlaybackInteractive + pb.start(0.0) + var frames := pb.mix_audio(1.0, int(5.30 * RATE)) # bis zur Anfrage + pb.switch_to_clip_by_name(&"gefahr") + frames.append_array(pb.mix_audio(1.0, int(6.70 * RATE))) + var data := PackedByteArray() + data.resize(frames.size() * 4) + for i in frames.size(): + data.encode_s16(i * 4, int(frames[i].x * 32767)) + data.encode_s16(i * 4 + 2, int(frames[i].y * 32767)) + var wav := AudioStreamWAV.new() + wav.format = AudioStreamWAV.FORMAT_16_BITS + wav.stereo = true + wav.mix_rate = RATE + wav.data = data + wav.save_to_wav("res://out/messung.wav") + quit() + ``` + + Im laufenden Spiel schneidest du den Master-Bus mit einem `AudioEffectCapture` oder `AudioEffectRecord` mit ([AudioEffectRecord](https://docs.godotengine.org/en/stable/classes/class_audioeffectrecord.html#class-audioeffectrecord-method-get-recording)). Frame-genau wird es mit dem Movie Maker: `godot --path . --write-movie film.png --fixed-fps 60` schreibt Bilder und daneben eine WAV-Datei ([Creating movies](https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#png)). Sie hat 48 kHz (`editor/movie_writer/mix_rate`), also 800 Samples pro Bild. Der Movie Maker braucht ein Fenster: Mit `--headless` bricht Godot 4.7.2 mit Signal 11 ab. + + Ausgewertet wird mit einem kurzen Skript. Es sucht in jedem Kanal den Beginn jedes Klicks und zählt Nulldurchgänge, um hoch und tief zu unterscheiden: + + ```python + import struct, sys, wave + + with wave.open(sys.argv[1]) as w: # 16 Bit, Stereo + rate, n = w.getframerate(), w.getnframes() + werte = struct.unpack(f"<{2 * n}h", w.readframes(n)) + + klicks = [] + for kanal, name in ((0, "A"), (1, "B")): # A links, B rechts + x = werte[kanal::2] + i = 0 + while i < n: + if abs(x[i]) > 100: # Klick beginnt (etwa -50 dBFS) + stueck = x[i:i + rate // 100] # 10 ms + wechsel = sum((a < 0) != (b < 0) for a, b in zip(stueck, stueck[1:])) + klicks.append((i / rate, name, "hoch" if wechsel * 50 > 1500 else "tief")) + i += rate // 10 # 100 ms weiter + else: + i += 1 + + for zeit, name, hoehe in sorted(klicks): + print(f"{zeit:7.3f} s {name} {hoehe}") + ``` + + Für den Messlauf oben mit „nächster Takt“ gibt es aus (Ausschnitt): + + ```text + 4.500 s A tief + 5.000 s A tief + 5.499 s A tief + 5.997 s B hoch + 6.495 s B tief + ``` + + Angefragt bei 5,30 s, gewechselt auf der Eins bei 6,0 s. Die 3 ms davor sind gemessen: In den Messläufen wechselte Godot bis zu 124 Samples vor dem Raster, musikalisch bedeutungslos. Das Übergangsstück erscheint in dieser Ausgabe als „A hoch“ und „B hoch“ zur selben Zeit. + + ## Die Übergangstabelle: Clips, Regeln und was ohne Regel passiert + + Ein `AudioStreamInteractive` ist eine Resource. Im Inspektor legst du Clips an, jeder mit Namen und Stream, und wählst den Startclip. Der Knopf *Übergänge bearbeiten* (Edit Transitions) öffnet eine Tabelle „von/nach“, in der du je Paar den Übergang einschaltest (Use Transition) und *Übergang von* (Transition From), *Übergang nach* (Transition To), *Fade-Modus* (Fade Mode), *Fade-Beats* (Fade Beats), *Filler-Clip* (Filler Clip) und *Voriges halten* (Hold Previous) setzt. In Code heißt dasselbe [`add_transition()`](https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition). So speichert Godot die Resource der Testszene, zwei Regeln, gekürzt: + + ```ini title=musik.tres + [resource] + clip_count = 3 + clip_0/name = &"ruhe" + clip_0/stream = ExtResource("1_h5apq") + clip_1/name = &"gefahr" + clip_1/stream = ExtResource("2_vybc6") + clip_2/name = &"bruecke" + clip_2/stream = ExtResource("3_c7rmj") + _transitions = { + Vector2i(0, 1): { + "fade_beats": 1.0, + "fade_mode": 0, + "from_time": 2, + "to_time": 1 + }, + Vector2i(1, 0): { + "fade_beats": 2.0, + "fade_mode": 2, + "from_time": 2, + "to_time": 1 + } + } + ``` + + `Vector2i(0, 1)` ist „von Clip 0 nach Clip 1“, `from_time` 2 heißt „nächster Takt“, `to_time` 1 „Clip-Anfang“, `fade_mode` 2 „Fade-Out“. Clip 2, das Übergangsstück, steht schon in der Liste, aber keine der beiden Regeln benutzt ihn; in der Übung am Ende bekommt die Regel zurück nach „ruhe“ das Übergangsstück. Statt eines Clips darfst du *Beliebiger Clip* (Any Clip) setzen, in Code `CLIP_ANY`. Godot sucht in dieser Reihenfolge: genaues Paar, von diesem Clip zu beliebig, von beliebig zu diesem Clip, beliebig zu beliebig ([Quelltext 4.7.2](https://github.com/godotengine/godot/blob/4.7.2-stable/modules/interactive_music/audio_stream_interactive.cpp)). Findet es nichts, nimmt es eine leere Regel: nächster Schlag, Clip-Anfang, Fade-Modus automatisch, ein Schlag. Gemessen: Ohne jede Regel wechselte die Musik bei 5,5 s, und A klang über einen Schlag aus. + + ## Wann der Wechsel greift: sofort, Schlag, Takt, Ende + + Dieselbe Anfrage bei 5,30 s, vier Regeln, je ein Messlauf, ohne Blende: + + | Übergang ab | Wechsel hörbar | + | --- | --- | + | `IMMEDIATE` | 5,30 s | + | `NEXT_BEAT` | 5,50 s | + | `NEXT_BAR` | 6,00 s | + | `END` | 8,00 s | + + Godot rechnet nicht mit einer Uhr des Spiels, sondern mit der Position im laufenden Clip. Es nimmt sie modulo Schlag oder Takt und wartet den Rest ab; „Ende“ ist `beat_count` Schläge oder, ohne `beat_count`, die Dateilänge. Das Modell dazu stimmt mit allen Messungen dieses Moduls überein, solange der laufende Clip bei 0 s begonnen hat: + + ```python run + def wechsel(anfrage, bpm, bar_beats, art, beat_count=16): + schlag = 60 / bpm + loop = beat_count * schlag + pos = anfrage % loop # Position im Clip, er schleift + if art == "sofort": + warte = 0 + elif art == "schlag": + warte = schlag - pos % schlag + elif art == "takt": + warte = schlag * bar_beats - pos % (schlag * bar_beats) + else: # "ende" + warte = loop - pos + return round(anfrage + warte, 2) + + + for art in ["sofort", "schlag", "takt", "ende"]: + print(art, wechsel(5.30, 120, 4, art)) + print("genau auf dem Schlag:", wechsel(5.50, 120, 4, "schlag")) + ``` + + Drei Folgen davon sind wichtig. + + - **Auf dem Schlag ist schon vorbei.** Eine Anfrage genau bei 5,50 s wartet auf den folgenden Schlag, gemessen 6,00 s. + - **Das Raster gehört dem Clip.** Startet B mit „sofort“ und „Clip-Anfang“ bei 5,30 s, liegen seine Takte bei 7,30, 9,30 und so weiter. Eine Anfrage zurück nach A „nächster Takt“ bei 7,0 s wechselte gemessen bei 7,30 s. Mit „gleiche Position“ (`TRANSITION_TO_TIME_SAME_POSITION`) springt B an die Stelle von A, und das Raster bleibt, wo es war. + - **Ohne `bpm` gibt es keinen Takt.** Steht `bpm` auf 0, wechselt „nächster Schlag“ und „nächster Takt“ sofort, ohne Fehlermeldung. Zusätzlich zählt `fade_beats` dann in Sekunden: 2 „Schläge“ Crossfade dauerten gemessen 2 Sekunden statt einer. + + Im laufenden Spiel kommt eine kleine Verzögerung dazu: Godot nimmt die Anfrage erst beim nächsten Mischblock an. Für „nächster Schlag“ und „nächster Takt“ spielt das meist keine Rolle; kommt die Anfrage wenige Millisekunden vor der Grenze, kann sie auf die nächste rutschen. Für „sofort“ sind es einige Millisekunden plus die Latenz des Treibers ([`get_output_latency()`](https://docs.godotengine.org/en/stable/classes/class_audioserver.html#class-audioserver-method-get-output-latency)). + + ## Blenden und Übergangsstücke: was die Fade-Modi tun + + Für Blenden taugen Klicks wenig. Die Messung lief mit Dauertönen, A links, B rechts, „sofort“ bei 5,30 s und `fade_beats` 2, also eine Sekunde bei 120 BPM: + + | Fade-Modus | alter Clip A | neuer Clip B | + | --- | --- | --- | + | `FADE_DISABLED` | sofort aus | sofort voll | + | `FADE_IN` | sofort aus | 1 s Einblendung | + | `FADE_OUT` | 1 s Ausblendung | sofort voll | + | `FADE_CROSS` | 1 s Ausblendung | 1 s Einblendung | + | `FADE_AUTOMATIC` | mit Clip-Anfang wie `FADE_OUT`, mit gleicher Position wie `FADE_CROSS` | | + + Die Blende ist linear in der Amplitude. Alle 100 ms gemessen fällt A um −0,4, −1,3, −2,4, −3,7, −5,1, −6,9, −9,1, −12,0, −16,4 und −26,0 dB. In der Mitte steht er also schon bei −6 dB, am Ende stürzt er ab. Aus der Musikproduktion kennst du das als lineare Fade-Kurve; eine andere Form bietet `AudioStreamInteractive` nicht. Brauchst du eine, render sie in die Datei. + + > [!WARNING] Doku und Messung weichen ab + > Die Doku beschreibt `FADE_IN` mit „let the current clip finish“ ([FadeMode](https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#enum-audiostreaminteractive-fademode)). Mit einem Loop als altem Clip stimmt das nicht: Er bricht am Wechselpunkt ab, mit einer Blende von einer Millisekunde. Ausspielen darf er nur, wenn er keinen Loop hat und die Regel „Clip-Ende“ heißt. Und die Import-Seite der Doku nennt `bpm`, `beat_count` und `bar_beats` noch „currently unused“. Gemessen sind sie die Grundlage jedes Takt-Wechsels. Vertrau bei Musik-Übergängen der Messung. + + Ein **Übergangsstück** (Filler) trägst du in der Übergangsregel als *Filler-Clip* ein. So lief „nächster Takt“ mit Übergangsstück von einem Takt und `FADE_DISABLED`, gemessen: + + ```text + 5,30 s Anfrage B + 6,00 s A hört auf, Übergangsstück beginnt voll + 8,00 s Übergangsstück endet nach beat_count 4, B beginnt + 8,00 s B blendet ein: −25 dB, −16 dB, −12 dB … + 9,00 s B voll + ``` + + Die Einblendung hast du nicht bestellt. Nach einem Übergangsstück blendet Godot 4.7.2 den Zielclip mit `FADE_DISABLED`, `FADE_OUT` und, bei „Clip-Anfang“, `FADE_AUTOMATIC` immer über eine Sekunde ein, gemessen bei 120 und bei 90 BPM, unabhängig von `fade_beats`. Mit `FADE_IN` und `FADE_CROSS` dauert sie `fade_beats` Schläge; `FADE_AUTOMATIC` mit „gleicher Position“ wird laut Quelltext zu `FADE_CROSS`. Im Quelltext steht an dieser Stelle der Kommentar „start at full volume, as filler is meant as a transition“. Gewollt ist die Einblendung also vermutlich nicht; miss sie nach jedem Godot-Update neu. Soll B nach dem Übergangsstück hart einsetzen, nimm `FADE_IN` mit sehr kleinem `fade_beats`: Mit 0,05 Schlägen, 25 ms bei 120 BPM, war B nach 25 ms voll da. `FADE_IN` schneidet A aber am Takt ab, das Ende von A gehört dann ins Übergangsstück. Das Übergangsstück selbst blendet Godot nie, auch das renderst du in die Datei. + + ### Stinger: einmal hin und zurück + + Ein **Stinger** ist ein kurzes Musikstück, das ein Ereignis markiert. Meist liegt er über der laufenden Musik, dann ist er ein eigener `AudioStreamPlayer`. Er kann die Musik auch kurz ersetzen, danach kehrt sie zurück. Dafür gibt es in `AudioStreamInteractive` zwei Wege. *Voriges halten* (Hold Previous, `hold_previous`) mit dem Auto-Advance-Modus `AUTO_ADVANCE_RETURN_TO_HOLD` kehrte im Messlauf nur zurück, wenn die Regel mit *Voriges halten* schon vor dem Stinger gegriffen hatte, etwa bei A nach B, und der Stinger danach kam. Zurück ging es dann zu A, an die Stelle, an der A verlassen wurde, nicht zu B, das der Stinger ersetzt hatte. Stand *Voriges halten* auf der Regel A nach Stinger selbst, blieb es nach dem Stinger still. Laut [Quelltext](https://github.com/godotengine/godot/blob/4.7.2-stable/modules/interactive_music/audio_stream_interactive.cpp) merkt sich Godot den gehaltenen Clip erst nach der Prüfung auf `AUTO_ADVANCE_RETURN_TO_HOLD`. Miss diesen Weg nach jedem Godot-Update neu. Zuverlässig lief der zweite Weg: Der Stinger bekommt `AUTO_ADVANCE_ENABLED` mit A als nächstem Clip, und die Regel Stinger nach A heißt „Clip-Ende“ und „vorige Position“. Gemessen ging A an der Stelle weiter, an der der Stinger es abgelöst hatte. + + ## Umschalten aus dem Code: Playback, Nachlauf, keine Doppelanfrage + + Umgeschaltet wird nicht am Stream, sondern an seinem Playback, das der Player erst nach `play()` hat ([`get_stream_playback()`](https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-method-get-stream-playback)). Die drei typischen Fehler und was Godot 4.7.2 dazu ausgibt: + + ```text + get_stream_playback() vor play(): + ERROR: Player is inactive. Call play() before requesting get_stream_playback(). + switch_to_clip_by_name() auf dem Stream: + SCRIPT ERROR: Invalid call. Nonexistent function 'switch_to_clip_by_name' in base 'AudioStreamInteractive'. + unbekannter Clip-Name: + ERROR: Clip not found: Kampf + ``` + + Den Stream-Fehler meldet Godot erst zur Laufzeit, auch wenn die Variable als `AudioStreamInteractive` typisiert ist. Statt der Methode geht auch die Eigenschaft des Players: `player.set(&"parameters/switch_to_clip", &"ruhe")` wechselte gemessen genauso. `get_current_clip_index()` springt erst, wenn der neue Clip gemischt wird, nicht bei der Anfrage. Damit schreibt das Log beides: wann du gefragt hast und wann gewechselt wurde. + + Bevor ein Spielereignis umschaltet, prüf die Tabelle per Taste. Dieses Testgerüst hängt am Musik-Player; die Aktion `musik_wechsel` legst du in der Eingabeliste an, der Name ist ein Platzhalter: + + ```gdscript title=musik_taste.gd + extends AudioStreamPlayer + + ## Testgerüst: schaltet mit der Aktion „musik_wechsel“ zwischen zwei Clips um. + + var _target := &"ruhe" + + + func _ready() -> void: + play() + + + func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed(&"musik_wechsel"): # Platzhalter-Aktion + _target = &"ruhe" if _target == &"gefahr" else &"gefahr" + get_stream_playback().switch_to_clip_by_name(_target) + HitLog.add("Musik: Anfrage %s" % _target) + ``` + + Gedrückt in Frame 30 und 150; die Zeilen `spielt` schreibt ein zweites Gerüst, das `get_current_clip_index()` abfragt: + + ```text + 1 Musik: spielt ruhe + 30 Musik: Anfrage gefahr + 121 Musik: spielt gefahr + 150 Musik: Anfrage ruhe + 244 Musik: spielt ruhe + ``` + + Der erste Wechsel kommt an der Eins bei 2,0 s. Der zweite kommt erst bei 4,0 s, obwohl die Taste bei 2,5 s gedrückt wurde: „gefahr“ hat bei 2,0 s begonnen, und sein Takt zählt. + + > [!MERKE] + > Frag nie den Clip an, der schon läuft. Godot fasst das als neue Anfrage auf: Der laufende Clip verstummt bis zum Wechselpunkt und beginnt dort von vorn. Gemessen fehlte nach einer Anfrage bei 5,30 s der Klick bei 5,5 s, und bei 6,0 s kam die Eins von Takt 1 statt Takt 4. Wer jeden Frame anfragt, bekommt Stille. + + Dasselbe passiert mit einer zurückgenommenen Anfrage. Fragst du B an und vor dem nächsten Takt wieder A, verwirft Godot das wartende B und startet A am Takt neu (gemessen: B bei 5,30 s, A bei 5,60 s, ab 6,0 s A von vorn). Deshalb braucht die Musikregie zwei Regeln: nur anfragen, wenn sich das Ziel ändert, und einen Zustand erst verlassen, wenn sein Signal eine Zeit lang ununterbrochen fehlt. Diese Wartezeit heißt **Nachlauf**. So sieht die Musikregie für die Testszene aus: + + ```gdscript title=music_director.gd + extends Node + + ## Musikregie der Testszene: zwei Platzhalter-Zustände, „ruhe“ und „gefahr“. + ## Gefahr, solange ein Gegner im Telegraph oder aktiv ist. Zurück erst nach hold_frames ruhigen Frames. + + @export var music: AudioStreamPlayer + @export var enemies: Array[Node] = [] + @export_range(0, 600) var hold_frames := 120 ## Nachlauf in Physik-Frames ohne Gefahr + + var _target := &"ruhe" + var _calm := 0 + var _clip := -1 + + + func _ready() -> void: + music.play() + + + func _physics_process(_delta: float) -> void: + var danger := enemies.any(_is_dangerous) + _calm = 0 if danger else _calm + 1 + if danger: + _request(&"gefahr") + elif _calm >= hold_frames: + _request(&"ruhe") + _log_switch() + + + func _is_dangerous(enemy: Node) -> bool: + return enemy.state == enemy.State.TELEGRAPH or enemy.state == enemy.State.ACTIVE + + + func _request(clip: StringName) -> void: + if clip == _target: + return # nie den Clip anfragen, der schon gilt + _target = clip + _playback().switch_to_clip_by_name(clip) + HitLog.add("Musik: Anfrage %s" % clip) + + + func _log_switch() -> void: + var now := _playback().get_current_clip_index() + if now != _clip: + _clip = now + HitLog.add("Musik: spielt %s" % music.stream.get_clip_name(now)) + + + func _playback() -> AudioStreamPlaybackInteractive: + return music.get_stream_playback() + ``` + + In der Szene hängt ein `AudioStreamPlayer` „Musik“ mit `musik.tres` auf dem Bus `Musik`, daneben ein `Node` „Musikregie“ mit diesem Skript; `music` und `enemies` setzt du im Inspektor. Der Dummy hat einen Angriff (Telegraph 20, aktiv 4, Erholung 30) und `wait_frames` 200. Ein echter Lauf in Echtzeit mit `hold_frames` 120, Log von `HitLog`: + + ```text + 200 Dummy WAIT -> TELEGRAPH + 200 Musik: Anfrage gefahr + 220 Dummy TELEGRAPH -> ACTIVE + 224 Dummy ACTIVE -> RECOVERY + 244 Musik: spielt gefahr + 254 Dummy RECOVERY -> WAIT + 343 Musik: Anfrage ruhe + 361 Musik: spielt ruhe + ``` + + Im Mitschnitt kommt B bei 4,00 s und A bei 6,00 s, jeweils auf der Eins. Die Zeile `spielt` steht einige Frames nach dem Takt, weil Godot den Index beim Mischen umstellt und das Spiel ihn im nächsten Physik-Frame liest. Für das Hören zählt der Mitschnitt. Derselbe Lauf mit `hold_frames` 0, nur die Musik-Zeilen: + + ```text + 200 Musik: Anfrage gefahr + 224 Musik: Anfrage ruhe + 454 Musik: Anfrage gefahr + 478 Musik: Anfrage ruhe + 708 Musik: Anfrage gefahr + 725 Musik: spielt gefahr + 732 Musik: Anfrage ruhe + ``` + + Die Gefahr dauert 24 Frames, der Weg zum nächsten Takt bis zu 120. Ob die Musik wechselt, hängt dann davon ab, wo im Takt der Telegraph beginnt. Und zweimal startete „ruhe“ von vorn: Mit einem Prüfloop, der die Taktnummer in der Tonhöhe der Eins trägt, begann „ruhe“ bei 4,0 s und bei 8,0 s jeweils mit Takt 1. Welchen Nachlauf dein Spiel braucht, entscheidest du am Diagramm aus [Musik für Zustände](/sound/musik-zustaende); 120 Frames sind ein Platzhalter. + + > [!INTERVIEW] + > „Unsere Kampfmusik setzt manchmal Sekunden zu spät ein und manchmal gar nicht. Woran liegt es?“ Gute Antwort, etwa als Technical Sound Designer: Ich lasse ein Log mit Frame der Anfrage und Frame des tatsächlichen Wechsels laufen und schneide den Ton mit. Wartet ein Takt-Wechsel bis zur Eins, ist „zu spät“ das Raster, kein Fehler, und die dringende Information gehört in ein Geräusch. Kommt der Wechsel gar nicht, prüfe ich, ob der Zustand kürzer ist als der Weg zum Takt und ob der Code die Anfrage zurücknimmt oder den laufenden Clip erneut anfragt. Dann setze ich einen Nachlauf und frage nur bei Änderungen an. + + ## Vertikal: Schichten mit AudioStreamSynchronized + + `AudioStreamSynchronized` spielt bis zu 32 Streams gleichzeitig ab derselben Position, jeden mit eigener Lautstärke in dB ([`set_sync_stream_volume()`](https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html#class-audiostreamsynchronized-method-set-sync-stream-volume)). Das ist die vertikale Schichtung. Winifred Phillips grenzt sie von Stems für einen Mix ab: Die Schichten sind Dateien, „that are not always meant to play simultaneously“ ([Pure Vertical Layering](https://www.gamedeveloper.com/game-platforms/pure-vertical-layering-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). Gleich lang müssen sie trotzdem sein, sonst wandern sie auseinander (siehe [Eigenes Stück aufbereiten](/sound/loops-stems)). + + Die Lautstärke liest Godot bei jedem Mischblock neu. Gemessen liefen beide Wege, ohne dass die Wiedergabe neu startete: + + ```gdscript + var tw := create_tween() + tw.tween_method(func(db: float): layers.set_sync_stream_volume(1, db), -60.0, 0.0, 1.0) + # oder über den Pfad der Eigenschaft: + create_tween().tween_property(layers, ^"stream_1/volume", 0.0, 1.0) + ``` + + Der Inspektor lässt −60 bis +12 dB zu. −60 dB ist nicht stumm, gemessen −60,6 dB unter dem vollen Pegel; im leisen Abschnitt eines Kopfhörermixes kann das zu hören sein. Und die Kurve entscheidet, wie die Blende klingt. Dieselbe Sekunde, einmal in dB linear, einmal in der Amplitude linear (`linear_to_db()` auf 0 bis 1), gemessen: + + | nach | dB linear | Amplitude linear | + | --- | --- | --- | + | 0,1 s | −51,5 dB | −16,9 dB | + | 0,5 s | −27,3 dB | −5,2 dB | + | 0,9 s | −3,3 dB | −0,4 dB | + + In dB linear bleibt die Schicht fast die ganze Sekunde leise und kommt am Ende; linear in der Amplitude ist sie nach einer Zehntelsekunde da. Welche Kurve zu deiner Musik passt, hörst du. Die Lautstärke ist eine Eigenschaft der Resource, nicht des Players; laut [Quelltext](https://github.com/godotengine/godot/blob/4.7.2-stable/modules/interactive_music/audio_stream_synchronized.cpp) liest jede Wiedergabe sie dort. Spielen zwei Player dieselbe `.tres`, ändert ein Tween also beide. + + Ein `AudioStreamSynchronized` kann selbst ein Clip in einem `AudioStreamInteractive` sein. Es übernimmt Tempo und Taktart seiner Streams; ein Wechsel „nächster Takt“ aus so einem Clip kam gemessen auf der Eins. So kombinierst du beide Achsen. + + > [!PRAXIS] Viele Segmente, ein Raster + > Phillips beschreibt die horizontale Neuordnung als Segmente mit Markern, die „may be positioned on each individual beat, at the beginning of each measure“ ([GDC 2021, Teil 2](https://www.gamedeveloper.com/audio/horizontal-resequencing-and-dynamic-transitions-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). In Godot sind das „nächster Schlag“ und „nächster Takt“. Für Spyder hatte jedes Level über dreißig Segmente in zufälliger Reihenfolge. Die Boss-Musik von Sackboy dagegen besteht aus drei Segmenten, deren Übergänge man nicht hören soll. Beides sind Entscheidungen der Komposition, die Technik folgt ihnen. Für zufällige Folgen gibt es in Godot `AudioStreamPlaylist`: `shuffle`, `loop` (Standard an) und `fade_time` (Standard 0,3 s). Mit `bpm` und `beat_count` ging sie gemessen nach dieser Zahl Schläge zum nächsten Stream, sonst nach der Dateilänge ([AudioStreamPlaylist](https://docs.godotengine.org/en/stable/classes/class_audiostreamplaylist.html#class-audiostreamplaylist-property-fade-time)). + + ## Häufige Fehler + + - **Umschalten am Stream.** `switch_to_clip_by_name()` gehört dem Playback; am Stream gibt es nur einen Laufzeitfehler. + - **`bpm` 0.** Takt- und Schlagwechsel kommen sofort, `fade_beats` zählt in Sekunden. + - **Jeden Frame anfragen.** Die Musik verstummt. Nur bei Änderung anfragen. + - **Anfrage zurücknehmen.** Der laufende Clip beginnt am Wechselpunkt von vorn. Nachlauf setzen. + - **„sofort“ mit Clip-Anfang, danach Takt-Regeln.** Das Raster verschiebt sich um die Anfragezeit. Gleiche Position nehmen oder auf den Takt wechseln. + - **Einblendung nach dem Übergangsstück übersehen.** Mit `FADE_DISABLED` blendet B eine Sekunde ein. + - **`FADE_IN` für einen Loop, der ausklingen soll.** Er bricht ab. + - **Schicht auf −60 dB für „aus“.** Das ist leise, nicht stumm. + + ## Zusammenfassung + + - `AudioStreamInteractive` wechselt nach einer Tabelle; ohne passende Regel: nächster Schlag, Ausblendung über einen Schlag. + - Schlag und Takt rechnet Godot aus der Position im laufenden Clip. Auf dem Schlag heißt: nächster Schlag. „sofort“ mit Clip-Anfang verschiebt das Raster. + - `bpm`, `beat_count` und `bar_beats` aus dem Import sind Pflicht für Takt-Wechsel, trotz anderslautender Import-Doku. + - Blenden sind linear in der Amplitude. `FADE_IN` schneidet einen Loop ab; nach einem Übergangsstück blendet das Ziel ohne `FADE_IN`/`FADE_CROSS` eine Sekunde ein (gemessen mit „Clip-Anfang“). + - Umschalten über `get_stream_playback()`, nur bei Änderung und mit Nachlauf. Der laufende Clip, erneut angefragt, beginnt von vorn. + - `AudioStreamSynchronized` blendet Schichten live; Kurve in dB und linear klingen verschieden, −60 dB ist nicht stumm. + - Gemessen wird mit Prüfloops: offline mit `mix_audio()`, im Spiel mit Mitschnitt, frame-genau mit dem Movie Maker. +exercises: + - id: fade-modi + type: choice + title: Wer hört wann auf? + prompt: | + Alter Clip A ist ein Loop, die Regel heißt „sofort“, `fade_beats` 2 bei 120 BPM. Welche Aussagen entsprechen den Messungen mit Godot 4.7.2? Mehrere Antworten richtig. + options: + - text: Mit `FADE_OUT` klingt A eine Sekunde lang aus, B setzt sofort voll ein. + correct: true + why: So gemessen. Die Ausblendung dauert `fade_beats` Schläge, hier zwei zu je 0,5 s. + - text: Mit `FADE_IN` spielt A zu Ende, während B einblendet. + correct: false + why: Das steht so in der Doku, gemessen bricht ein Loop aber am Wechselpunkt ab. Nur ein Clip ohne Loop mit der Regel „Clip-Ende“ darf ausspielen. + - text: "`FADE_AUTOMATIC` mit „gleicher Position“ verhält sich wie ein Crossfade." + correct: true + why: Mit „Clip-Anfang“ wird daraus `FADE_OUT`, mit „gleicher Position“ `FADE_CROSS`. Gemessen und im Quelltext so festgelegt. + - text: Nach der halben Blende steht der ausblendende Clip bei −3 dB. + correct: false + why: Die Blende ist linear in der Amplitude, nach der halben Zeit misst er etwa −6 dB, nicht −3 dB wie bei einer Kurve gleicher Leistung. + - text: Mit `FADE_DISABLED` endet A sofort und B setzt sofort voll ein. + correct: true + why: Ein harter Schnitt, mit einer Mini-Blende von einer Millisekunde gegen Klicks. + explanation: | + Die Fade-Modi beschreiben zwei Dinge auf einmal: was mit dem alten Clip passiert und was mit dem neuen. Gemessen ist das eindeutig, die Doku-Zeile zu `FADE_IN` aber nicht. Für Loops ist sie falsch. Weil alle Blenden linear in der Amplitude laufen, klingt ein Crossfade in der Mitte dünner als ein Crossfade gleicher Leistung aus REAPER. Ob dich das stört, hörst du mit deiner Musik. + resources: + - title: Godot Docs · AudioStreamInteractive · FadeMode + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#enum-audiostreaminteractive-fademode + + - id: schicht-kurve + type: choice + title: Eine Schicht in einer Sekunde einblenden + prompt: | + Schicht 1 eines `AudioStreamSynchronized` soll von −60 dB auf 0 dB kommen. Welche Aussagen stimmen nach den Messungen? Mehrere Antworten richtig. + options: + - text: Ein Tween auf `set_sync_stream_volume()` ändert die Lautstärke, während die Musik weiterläuft. + correct: true + why: Godot liest die Lautstärke bei jedem Mischblock neu. Gemessen startete nichts neu. + - text: Linear in dB getweent ist die Schicht nach der halben Zeit fast so laut wie am Ende. + correct: false + why: Gemessen lag sie nach 0,5 s noch bei −27 dB. Erst kurz vor Schluss wird sie laut. + - text: Linear in der Amplitude ist sie nach 0,1 s schon bei rund −17 dB. + correct: true + why: Ein Zehntel der Amplitude sind −20 dB; gemessen −16,9 dB im Fenster ab 0,1 s. + - text: Bei −60 dB ist die Schicht stumm. + correct: false + why: −60 dB ist ein Tausendstel der Amplitude. In einem leisen Abschnitt kann man das hören. Für „aus“ schaltest du die Schicht ab oder gehst im Code tiefer. + explanation: | + Die Lautstärke einer Schicht ist eine Zahl in dB, die Godot bei jedem Block liest. Wie sich eine Blende anfühlt, hängt an der Kurve, mit der du diese Zahl bewegst. Linear in dB klingt eher wie ein später Einsatz, linear in der Amplitude wie ein schneller. Keine ist richtig; du wählst nach dem Hören. Prüf außerdem, ob −60 dB in deinem leisesten Abschnitt verschwindet. + resources: + - title: Godot Docs · AudioStreamSynchronized · set_sync_stream_volume + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html#class-audiostreamsynchronized-method-set-sync-stream-volume + - title: Godot Docs · Tween.tween_method + url: https://docs.godotengine.org/en/stable/classes/class_tween.html#class-tween-method-tween-method + + - id: filler-ablauf + type: order + title: Ein Wechsel über ein Übergangsstück + prompt: | + Regel A nach B: „nächster Takt“, „Clip-Anfang“, `FADE_DISABLED`, Übergangsstück F mit `beat_count` 4, 120 BPM. Bring die Ereignisse in die gemessene Reihenfolge. + lang: prose + lines: | + Der Code fragt B an, A spielt weiter + Auf der nächsten Eins hört A auf und F beginnt voll + get_current_clip_index meldet den Filler-Clip + Nach vier Schlägen endet F und B beginnt + B blendet über eine Sekunde ein + B spielt mit voller Lautstärke + explanation: | + Die Anfrage wartet bis zum Takt. Dort übernimmt das Übergangsstück, ungeblendet und in voller Lautstärke, und der Index springt auf F, denn das Playback meldet immer, was gerade gemischt wird. F läuft `beat_count` Schläge, danach beginnt B. Dass B einblendet, obwohl die Regel keine Blende hat, ist das gemessene Verhalten von 4.7.2: Mit `FADE_DISABLED`, `FADE_OUT` und, bei „Clip-Anfang“, `FADE_AUTOMATIC` dauert die Einblendung nach einem Übergangsstück immer eine Sekunde. + resources: + - title: Godot Docs · AudioStreamInteractive · add_transition + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + + - id: wechselzeit-90 + type: output + title: Wann wechselt es bei 90 BPM? + prompt: | + Das Modell aus der Lektion, Prüfloop A mit 90 BPM, 4/4, `beat_count` 16. Die ersten drei Anfragen kommen bei 7,10 s, die letzte bei 12,00 s, nachdem A einmal geschleift hat. Was gibt das Programm aus? Alle Werte sind gegen Messläufe in Godot 4.7.2 geprüft. + lang: python + code: | + def wechsel(anfrage, bpm, bar_beats, art, beat_count=16): + schlag = 60 / bpm + loop = beat_count * schlag + pos = anfrage % loop + if art == "schlag": + warte = schlag - pos % schlag + elif art == "takt": + warte = schlag * bar_beats - pos % (schlag * bar_beats) + else: + warte = loop - pos + return round(anfrage + warte, 2) + + + print(wechsel(7.10, 90, 4, "schlag")) + print(wechsel(7.10, 90, 4, "takt")) + print(wechsel(7.10, 90, 4, "ende")) + print(wechsel(12.00, 90, 4, "takt")) + expected: | + 7.33 + 8.0 + 10.67 + 13.33 + hints: + - Ein Schlag dauert bei 90 BPM 0,667 s, ein Takt 2,667 s, der Loop 10,667 s. + - Bei 12,00 s ist A schon wieder bei Position 1,333 s. + explanation: | + Bei 7,10 s liegt A im elften Schlag, der nächste beginnt bei 7,33 s. Takte beginnen bei 0, 2,67, 5,33 und 8,0 s, also 8,0. „Ende“ wartet auf 16 Schläge, 10,67 s. Bei 12,00 s ist A bei Position 1,33 s im zweiten Durchlauf; der nächste Takt im Clip liegt bei 2,67 s, also 13,33 s. Die Messläufe ergaben 7,331, 7,998, 10,664 und 13,332 s, jeweils wenige Millisekunden vor dem Raster. + resources: + - title: Godot Docs · AudioStreamInteractive · TransitionFromTime + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#enum-audiostreaminteractive-transitionfromtime + + - id: raster-verschoben + type: output + title: Wo liegt jetzt der Takt? + prompt: | + 120 BPM, 4/4. A läuft seit 0 s. Bei 5,30 s fragt der Code B an, Regel „sofort“ und „Clip-Anfang“. Bei 7,00 s fragt er A an, Regel B nach A „nächster Takt“. Bei welcher Zeit in Sekunden wird A hörbar? Antworte mit der Zahl, zwei Nachkommastellen. + lang: text + code: | + 5.30 Anfrage B sofort, Clip-Anfang + 7.00 Anfrage A nächster Takt + ???? A hörbar + expected: | + 7.30 + accept: + - "7,30" + - "7.3" + - "7,3" + hints: + - Die Takte von B zählen ab dem Moment, in dem B begonnen hat. + explanation: | + B startet bei 5,30 s an seinem Anfang. Seine Takte liegen also bei 5,30, 7,30, 9,30 s. Bei 7,00 s ist B bei Position 1,70 s, der nächste Takt im Clip kommt 0,30 s später. Gemessen wechselte A bei 7,30 s. Mit der globalen Rechnung „nächste Eins bei 8,00 s“ lägst du daneben. Wer das Raster behalten will, nimmt für den ersten Wechsel „gleiche Position“ oder wechselt auf den Takt. + resources: + - title: Godot Docs · AudioStreamInteractive · TransitionToTime + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#enum-audiostreaminteractive-transitiontotime + + - id: filler-voll + type: output + title: Ab wann ist B voll da? + prompt: | + 120 BPM, 4/4. Regel A nach B: „nächster Takt“, `FADE_DISABLED`, `fade_beats` 1, Übergangsstück F mit `beat_count` 4. Die Anfrage kommt bei 5,30 s. Ab welcher Zeit spielt B mit voller Lautstärke? Antworte in Sekunden mit einer Nachkommastelle. + lang: text + code: | + 5.3 Anfrage B + ??? B voll + expected: | + 9.0 + accept: + - "9,0" + - "9" + hints: + - Erst die nächste Eins, dann vier Schläge Übergangsstück. + - Die Einblendung nach dem Übergangsstück hängt hier nicht an `fade_beats`. + explanation: | + Die nächste Eins ist 6,0 s, dort beginnt F. Vier Schläge später, bei 8,0 s, beginnt B und blendet ein. Mit `FADE_DISABLED` dauert diese Einblendung in 4.7.2 immer eine Sekunde, gemessen bei 120 und 90 BPM. B ist also ab 9,0 s voll da. Mit `FADE_IN` und `fade_beats` 1 wäre es 8,5 s, mit 0,05 Schlägen 25 ms nach 8,0 s. + resources: + - title: Godot Docs · AudioStreamInteractive · add_transition + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + + - id: tabelle-im-code + type: gap + title: Zwei Regeln und ein Wechsel + prompt: | + Ergänze: Von „ruhe“ (Clip 0) nach „gefahr“ (Clip 1) auf den nächsten Takt, ohne Blende. Von jedem Clip zurück nach „ruhe“ auf den nächsten Takt mit Ausblendung über zwei Schläge. Umgeschaltet wird am Playback des Players `music`. + lang: gdscript + code: | + var m := AudioStreamInteractive.new() + m.clip_count = 2 + m.set_clip_name(0, &"ruhe") + m.set_clip_name(1, &"gefahr") + m.add_transition(0, 1, AudioStreamInteractive.⟦TRANSITION_FROM_TIME_NEXT_BAR⟧, AudioStreamInteractive.TRANSITION_TO_TIME_START, AudioStreamInteractive.FADE_DISABLED, 1.0) + m.add_transition(AudioStreamInteractive.⟦CLIP_ANY⟧, 0, AudioStreamInteractive.TRANSITION_FROM_TIME_NEXT_BAR, AudioStreamInteractive.TRANSITION_TO_TIME_START, AudioStreamInteractive.⟦FADE_OUT⟧, ⟦2.0⟧) + music.stream = m + music.play() + music.⟦get_stream_playback()⟧.switch_to_clip_by_name(&"gefahr") + gaps: + - label: Zeitpunkt + hint: Die Konstante für „nächster Takt“. + - label: Von jedem Clip + hint: Eine Konstante mit dem Wert −1. + - label: Blende + hint: Nur der alte Clip blendet aus. + accept: ["FADE_AUTOMATIC"] + - label: Länge + hint: In Schlägen, als Kommazahl. + accept: ["2"] + - label: Playback + hint: Die Methode des Players, die nach `play()` die laufende Wiedergabe liefert. + explanation: | + `CLIP_ANY` als Quelle heißt „von jedem Clip“; Godot nimmt diese Regel, wenn es kein genaues Paar gibt. `FADE_OUT` lässt den alten Clip ausklingen, der neue beginnt voll. `FADE_AUTOMATIC` ist hier gleichwertig, weil das Ziel „Clip-Anfang“ ist. Umgeschaltet wird am Playback, das `get_stream_playback()` nach `play()` liefert. Im echten Code bekommen die Clips noch ihre Streams mit `set_clip_stream()`. + resources: + - title: Godot Docs · AudioStreamInteractive · add_transition + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + + - id: regie-luecken + type: gap + title: Nur bei Änderung, erst nach dem Nachlauf + prompt: | + Ergänze die Musikregie: Ruhige Frames zählen hoch und bei Gefahr auf 0 zurück, „ruhe“ erst nach `hold_frames`, und der Clip, der schon gilt, wird nicht noch einmal angefragt. + lang: gdscript + code: | + func _physics_process(_delta: float) -> void: + var danger := enemies.any(_is_dangerous) + _calm = 0 if danger else ⟦_calm + 1⟧ + if danger: + _request(&"gefahr") + elif _calm ⟦>=⟧ hold_frames: + _request(&"ruhe") + + + func _request(clip: StringName) -> void: + if ⟦clip == _target⟧: + return + _target = clip + _playback().switch_to_clip_by_name(clip) + gaps: + - label: Zähler + hint: Einen Frame mehr. + accept: ["1 + _calm"] + - label: Vergleich + hint: Ab genau `hold_frames` ruhigen Frames. + accept: [">"] + - label: Schutz + hint: Vergleiche mit dem zuletzt angefragten Ziel. + accept: ["_target == clip"] + explanation: | + Der Zähler macht aus „gerade keine Gefahr“ einen Nachlauf: „ruhe“ kommt erst, wenn `hold_frames` Frames lang ununterbrochen keine Gefahr war. `>` statt `>=` verschiebt ihn nur um einen Frame, beides ist eine Wahl. Der Schutz in `_request` ist kein Stil, sondern Pflicht: Ein zweites `switch_to_clip_by_name()` für den Clip, der schon gilt, lässt ihn verstummen und am Wechselpunkt neu beginnen. Die Lösung vergleicht mit `_target`, also mit dem zuletzt angefragten Clip, nicht mit dem, der gerade hörbar ist. + resources: + - title: Godot Docs · AudioStreamPlaybackInteractive · switch_to_clip_by_name + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplaybackinteractive.html#class-audiostreamplaybackinteractive-method-switch-to-clip-by-name + + - id: wechsel-am-stream + type: bug + title: Der Telegraph kommt, die Musik nicht + prompt: | + Beim Telegraph soll die Musik wechseln. Stattdessen steht im Ausgabefenster `Invalid call. Nonexistent function 'switch_to_clip_by_name' in base 'AudioStreamInteractive'.`, und die Musik spielt weiter „ruhe“. Finde die Zeile und korrigiere sie. + lang: gdscript + code: | + @onready var music: AudioStreamPlayer = $Musik + + + func _ready() -> void: + music.play() + + + func _on_dummy_telegraph() -> void: + var stream: AudioStreamInteractive = music.stream + stream.switch_to_clip_by_name(&"gefahr") + lines: [10] + fix: + - line: 10 + answers: + - music.get_stream_playback().switch_to_clip_by_name(&"gefahr") + - music.get_stream_playback().switch_to_clip_by_name("gefahr") + - (music.get_stream_playback() as AudioStreamPlaybackInteractive).switch_to_clip_by_name(&"gefahr") + - (music.get_stream_playback() as AudioStreamPlaybackInteractive).switch_to_clip_by_name("gefahr") + - music.set(&"parameters/switch_to_clip", &"gefahr") + - music.set(&"parameters/switch_to_clip", "gefahr") + - music.set("parameters/switch_to_clip", &"gefahr") + - music.set("parameters/switch_to_clip", "gefahr") + - music["parameters/switch_to_clip"] = &"gefahr" + - music["parameters/switch_to_clip"] = "gefahr" + explanation: | + Der Stream ist die Beschreibung, das Playback die laufende Wiedergabe. Nur das Playback kennt `switch_to_clip_by_name()`. Die typisierte Variable hilft hier nicht: Godot 4.7.2 meldet den Fehler erst zur Laufzeit. Die Eigenschaft `parameters/switch_to_clip` des Players führt zum selben Ziel, mit `set()` oder in eckigen Klammern; alle diese Schreibweisen wechselten in Godot 4.7.2 gemessen den Clip. Zeile 9 ist danach überflüssig. + resources: + - title: Godot Docs · AudioStreamPlayer · get_stream_playback + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-method-get-stream-playback + + - id: bpm-null + type: bug + title: Der Takt-Wechsel kommt sofort + prompt: | + Die Regel heißt „nächster Takt“, mit Übergangsstück. Im Mitschnitt wechselt der Zustand aber sofort bei der Anfrage, mitten im Takt, und eine Ausblendung über zwei Schläge dauert zwei Sekunden statt einer. Die Datei ist ein Loop mit vier Takten bei 120 BPM. Finde die Zeile in ihrer Import-Datei und korrigiere sie. + lang: ini + code: | + [params] + + loop=true + loop_offset=0 + bpm=0 + beat_count=16 + bar_beats=4 + lines: [5] + fix: + - line: 5 + answers: + - bpm=120 + - bpm=120.0 + explanation: | + Ohne `bpm` kennt Godot keinen Schlag. „nächster Schlag“ und „nächster Takt“ werden dann zu „sofort“, ohne Fehlermeldung, und `fade_beats` zählt in Sekunden. Beides ist gemessen. Nach einem Import steht `bpm` auf 0, auch wenn die Datei ein BPM-Tag trägt. Trag den Wert im Import-Dock ein; die Zeile in der `.import`-Datei ist dann das Ergebnis. + resources: + - title: Godot Docs · AudioStreamOggVorbis.bpm + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamoggvorbis.html#class-audiostreamoggvorbis-property-bpm + + - id: log-ohne-mindestdauer + type: explain + title: Warum die Musik zweimal nicht wechselt + prompt: | + Ein echter Lauf der Testszene mit `hold_frames` 0, 120 BPM, Regeln „nächster Takt“. Der Dummy ist jeweils 24 Frames lang im Telegraph oder aktiv. Erkläre, warum „gefahr“ erst beim dritten Mal zu hören ist und was ein Nachlauf daran ändert. + lang: text + code: | + 200 Musik: Anfrage gefahr + 224 Musik: Anfrage ruhe + 454 Musik: Anfrage gefahr + 478 Musik: Anfrage ruhe + 708 Musik: Anfrage gefahr + 725 Musik: spielt gefahr + 732 Musik: Anfrage ruhe + points: + - Ein Takt dauert 2 s, also 120 Frames; eine Anfrage wartet bis zu 120 Frames auf die Eins. + - Bei Frame 200 und 454 kommt „ruhe“ nach 24 Frames, bevor die nächste Eins erreicht ist; Godot verwirft das wartende „gefahr“. + - Die zurückgenommene Anfrage startet „ruhe“ an der Eins von vorn. + - Bei Frame 708 liegt die Eins nur 12 Frames entfernt, deshalb wechselt es dort. + - Dauern Gefahr und Nachlauf zusammen mindestens einen Takt, bleibt „gefahr“ angefragt, bis es erklungen ist; der Wechsel hängt nicht mehr von der Lage im Takt ab. + explanation: | + Die Anfragen kommen im Rhythmus des Gegners, die Wechsel im Rhythmus der Musik. Frame 200 ist 3,33 s, die nächste Eins 4,0 s, also Frame 240. Schon bei Frame 224 kommt „ruhe“, und Godot verwirft das wartende „gefahr“. Weil „ruhe“ der laufende Clip ist, beginnt er an der Eins von vorn. Bei Frame 454 ist es dasselbe. Frame 708 ist 11,8 s, die Eins bei 12,0 s kommt nach 12 Frames, also vor der Rücknahme in Frame 732. Ob die Musik wechselt, entscheidet damit der Zufall der Lage im Takt. Ein Nachlauf, im Log mit `hold_frames` 120 gemessen, hält die Anfrage lange genug, damit jeder Telegraph die Musik erreicht: 24 Frames Gefahr plus 120 Frames Nachlauf sind mehr als ein Takt von 120 Frames. Wie lang er sein soll, ist eine Designfrage. + resources: + - title: Godot Docs · AudioStreamPlaybackInteractive · get_current_clip_index + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplaybackinteractive.html#class-audiostreamplaybackinteractive-method-get-current-clip-index + + - id: testszene-musik + type: practice + title: Prüfloops und Musikregie in der Testszene + prompt: | + Bau die Musik in deine Kopie der [Combat-Testszene](/combat/testszene-angriff) ein, mit Prüfloops, nicht mit Musik: + + 1. Erzeuge drei Prüfloops (A nur links, B nur rechts, Übergangsstück beidseitig) und importiere sie als Ogg mit `loop`, `bpm`, `beat_count` und `bar_beats`. + 2. Leg einen Bus `Musik` an, eine Resource `musik.tres` mit drei Clips und zwei Regeln, einen `AudioStreamPlayer` „Musik“ und die Musikregie. Eine der beiden Regeln bekommt das Übergangsstück als *Filler-Clip* (Filler Clip). + 3. Prüf beide Regeln zuerst per Taste mit `musik_taste.gd`. + 4. Gib dem Dummy einen Angriff und eine Wartezeit, sodass er alle paar Sekunden telegraphiert und keine Anfrage in das Übergangsstück fällt, und schalte mit der Musikregie um. + 5. Starte die Szene und lies im Log, wann angefragt und wann gewechselt wurde. + + Die Musikregie ist Code und darf aus der Lektion stammen; die Werte sind Platzhalter. + lang: gdscript + solution: | + # music_director.gd wie in der Lektion. Im Inspektor: + # Musik (AudioStreamPlayer): stream = musik.tres, bus = Musik + # Musikregie (Node): music = ../Musik, enemies = [../Dummy], hold_frames = 120 + # Dummy: attacks = [AttackData mit windup 20, active 4, recovery 30], wait_frames = 400 + # + # musik.tres: + # Clip 0 ruhe = zustand_a.ogg (loop, bpm 120, beat_count 16, bar_beats 4) + # Clip 1 gefahr = zustand_b.ogg (loop, bpm 120, beat_count 16, bar_beats 4) + # Clip 2 bruecke = uebergang.ogg (ohne loop, bpm 120, beat_count 4) + # ruhe -> gefahr: Next Bar, Clip Start, Fade Mode Disabled + # gefahr -> ruhe: Next Bar, Clip Start, Fade Mode Fade-Out, Fade Beats 2, + # Filler Clip bruecke + # + # Erwartetes Log (Echtzeitlauf, Godot 4.7.2): + # 400 Dummy WAIT -> TELEGRAPH + # 400 Musik: Anfrage gefahr + # 485 Musik: spielt gefahr + # 543 Musik: Anfrage ruhe + # 602 Musik: spielt bruecke + # 725 Musik: spielt ruhe + # Im Mitschnitt: B ab 8,0 s, Übergangsstück von 10,0 bis 12,0 s, + # dann A ab 12,0 s mit einer Einblendung bis 13,0 s. + # + # wait_frames 400 ist ein Platzhalter. Mit 200 fiel die nächste Anfrage + # „gefahr“ (Frame 454) in das Übergangsstück. Gemessen ging es danach + # bei 8,0 s direkt zu „gefahr“, „ruhe“ erklang nicht. + checklist: + - Die Prüfloops liegen als Ogg vor; in ihren `.import`-Dateien stehen `bpm`, `beat_count` und `bar_beats` passend zur Datei. + - Im Kopfhörer kommt A von links, B von rechts und das Übergangsstück von beiden Seiten. + - Jede Regel hast du einmal per Taste ausgelöst, bevor die Musikregie sie benutzt. + - Das Log zeigt zu jeder Anfrage eine spätere Zeile `spielt` mit dem Frame des Wechsels. + - Die Musikregie fragt keinen Clip an, der schon gilt; im Log steht keine Anfrage zweimal hintereinander. + - Mit `hold_frames` 0 und 120 hast du je einen Lauf gemacht und die Logs verglichen. + explanation: | + Die Prüfloops machen Fehler hörbar, die in echter Musik als „klingt komisch“ durchgehen: ein Wechsel neben der Eins, ein Neustart, eine Einblendung. Das Log trennt Anfrage und Wechsel. So erkennst du, ob der Code zu spät fragt oder die Tabelle wartet. Erst wenn beides mit Prüfloops stimmt, setzt du deine Musik ein; dann suchst du nur noch musikalische Fragen. + resources: + - title: Godot Docs · AudioStreamInteractive + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + + - id: uebergangs-protokoll + type: practice + title: Prüfprotokoll für deine Übergänge + prompt: | + Prüf mit deinem eigenen Stück oder mit Prüfloops mindestens drei Übergänge deiner Tabelle. Halte für jeden fest: Regel (von, nach, Zeitpunkt, Ziel, Fade-Modus, Übergangsstück), Anfrage (Frame oder Sekunde), deine Vorhersage mit dem Modell aus der Lektion, gemessener Wechsel aus dem Mitschnitt oder Movie Maker und die Abweichung. Schreib dazu, was du hörst, ohne es zu bewerten, und welche Frage für dich offen bleibt. + lang: prose + checklist: + - Zu jedem Übergang steht die vollständige Regel mit allen sechs Angaben. + - Zu jedem Übergang steht eine Vorhersage, aufgeschrieben vor dem Lauf. + - Die gemessene Wechselzeit stammt aus einer WAV-Datei, nicht aus dem Log allein. + - Bei jeder Abweichung über 50 ms hast du notiert, ob sie aus Raster, Übergangsstück, Einblendung oder Code kommt. + - Mindestens ein Übergang ist mit Übergangsstück oder Stinger geprüft. + - Zu mindestens einem Übergang steht eine offene Frage, die du mit einem weiteren Lauf klären kannst. + explanation: | + Ein Übergang ist eine Behauptung: „Bei dieser Anfrage wechselt die Musik dann und so.“ Das Protokoll prüft sie. Die Vorhersage vor dem Lauf zwingt dich, die Regel zu verstehen; die Messung zeigt, ob Godot sie so umsetzt. Abweichungen sind die interessanten Zeilen. In 4.7.2 kommen sie meist aus der Einblendung nach dem Übergangsstück, aus einem verschobenen Raster oder aus dem Code. Was musikalisch richtig ist, entscheidest du danach. + resources: + - title: Godot Docs · Creating movies (Movie Maker) + url: https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#command-line-usage diff --git a/content/sound/modules/hoeren-analysieren.yaml b/content/sound/modules/hoeren-analysieren.yaml new file mode 100644 index 0000000..73eb8bd --- /dev/null +++ b/content/sound/modules/hoeren-analysieren.yaml @@ -0,0 +1,466 @@ +id: hoeren-analysieren +title: "Kritisch hören: was ein Klang im Spiel leistet" +level: 1 +minutes: 90 +summary: Wie du mit Kopfhörern am Handy hörst, welche Information ein Klang im Spiel trägt – Rückmeldung, Warnung, Ort außerhalb des Bilds, Orientierung, Stimmung, Kontinuität –, wie du ihn mit Begriffen von Jørgensen, Chion und Schafer einordnest und als Hörprotokoll festhältst. +place: unterwegs +goals: + - Du ordnest einen Klang einer oder mehreren Aufgaben zu – Rückmeldung, Warnung, Information außerhalb des Bilds, Orientierung, Stimmung, Kontinuität – und nennst die wichtigste zuerst. + - Du unterscheidest diegetische, extradiegetische und transdiegetische Klänge nach Kristine Jørgensen und begründest Grenzfälle mit ihrer Funktion. + - Du hörst denselben Clip nacheinander kausal, semantisch und reduziert, wie Michel Chion die drei Hörweisen unterscheidet. + - Du überträgst Schafers Grundton, Signal und Klangmarke auf eine Spielszene. + - Du führst ein Hörprotokoll eines 30–60-Sekunden-Clips mit Zeit, Klang, Quelle, Aufgabe, Priorität und Stille und wertest es aus. +resources: + - title: Wwise Tour 2016 · Blizzard Overwatch (1 of 7) – Audio in Overwatch + url: https://www.youtube.com/watch?v=fGDOgn2WXLs + - title: Kristine Jørgensen · On Transdiegetic Sounds in Computer Games (2007) + url: https://hdl.handle.net/1956/5855 + - title: Handbook for Acoustic Ecology (SFU) · Keynote + url: https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Keynote.html + - title: Michel Chion · Audio-Vision (Columbia University Press) + url: https://cup.columbia.edu/book/audio-vision-sound-on-screen/9780231185899/ + - title: Karen Collins · Game Sound (MIT Press, DOI) + url: https://doi.org/10.7551/mitpress/7909.001.0001 +lesson: | + Du sitzt im Bus, Kopfhörer auf, und spielst einen Clip von 40 Sekunden aus einem Action-Spiel in der Draufsicht ab. Beim ersten Durchgang schaust du hin. Beim zweiten schließt du die Augen und zählst, was du trotzdem weißt: dass rechts jemand läuft, dass gleich ein Angriff kommt, dass der Kampf vorbei ist. Diese zweite Liste ist das Thema dieses Moduls. + + Jeff Kaplan, Game Director von Overwatch, gab dem Audio-Team früh ein Ziel: Er wollte das Spiel nach Gehör spielen können. Scott Lawlor, Audio Director des Projekts, gibt es so wieder: „he wants so much information encoded in the sound that he could practically close his eyes or turn off the monitor and know what's going on at any given time“ ([Wwise Tour 2016, Teil 1](https://www.youtube.com/watch?v=fGDOgn2WXLs), ab etwa 24:30). Aus der Musik kennst du kritisches Hören: Frequenzen, Dynamik, Raum, Fehler im Mix. Im Spiel kommt eine Frage hinzu, die ein Stück nicht stellt: Was sagt dieser Klang dem Spieler, und was tut der Spieler deshalb? + + Dieses Modul gibt dir Begriffe und eine Methode dafür, das Hörprotokoll. Es bewertet keine Klänge. Ob dir etwas gefällt, notierst du getrennt. Im Protokoll steht, was ein Klang leistet. In deinem Spiel wirst du dieselben Fragen an deine eigenen Klänge stellen; welche das sind, entscheidest du. + + ## Sechs Aufgaben eines Klangs im Spiel + + Khris Brown nennt in einem GDC-Vortrag von 2017 als Zweck von Sounddesign „providing the information you need to know to interact optimally with the world“ ([Audio Choreography for Battle Design](https://www.youtube.com/watch?v=q6Vs3d6CrUc), ab etwa 1:42). Für die Analyse zerlegst du diese Information in sechs Aufgaben: + + | Aufgabe | Frage an den Klang | + | --- | --- | + | Rückmeldung | Hat meine Eingabe gewirkt? | + | Warnung | Was passiert gleich? | + | Außerhalb des Bilds | Was ist da, das ich nicht sehe? | + | Orientierung | Wo bin ich, wohin geht es? | + | Stimmung | Wie fühlt sich dieser Ort an? | + | Kontinuität | Was verbindet zwei Momente? | + + **Rückmeldung** bestätigt eine Eingabe. Brown fordert „accurate immediate reinforcing feedback“ (ab etwa 10:40). Im Kampf heißt das: Der Ton gehört in den Frame, in dem der Treffer gemeldet wird, sonst klingt auch ein Schlag ins Leere wie ein Treffer ([Trefferfeedback](/combat/trefferfeedback)). + + **Warnung** kündigt an, was gleich passiert. In Overwatch ruft McCree (heute Cassidy) „It's high noon“, bevor seine stärkste Fähigkeit trifft. Laut Team galt: „as soon as you hear that line you had to react and hide“ ([Teil 4, Pinpoint Accuracy](https://www.youtube.com/watch?v=F2N8bbzhHd8), ab etwa 9:51). + + **Information außerhalb des Bilds** ist das, was nur der Ton liefert. Der Sound Supervisor von Overwatch klagte beim Spielen immer wieder, er sei sicher, dass jemand bei ihm sei, und finde ihn nicht. Die Aufnahme zeigte: Der Gegner lief ein Stockwerk höher (Teil 4, ab etwa 1:52). In einer Draufsicht ist der Bildrand diese Grenze. Was dahinter liegt, kann nur klingen. + + **Orientierung** sagt, wo du bist. Im Vortrag zu Hyper Light Drifter beschreiben Rich Vreeland und Akash Thakkar zwei Arten von Atmo: allgemeine wie Wind, den man überall hört, und Klänge an Fackeln und Lampen, die lauter und leiser werden, während man vorbeigeht ([GDC 2017](https://www.youtube.com/watch?v=k1b9xKRC74E), ab etwa 49:45). Die zweite Art macht Orte unterscheidbar. Im Level-Design-Kurs steht dazu der Satz „Auch Klang kann ein Hinweis sein“ ([Ausgestaltung](/level-design/ausgestaltung)). Hier hörst du, wie das klingt. + + **Stimmung** färbt, wie sich ein Ort anfühlt. Brown beschreibt für BioShock einzelne Klänge wie Knarren und Wasser und darunter „this bed of the actual pressure“, den Druck des Ozeans auf dem Bauwerk (ab etwa 3:28). + + **Kontinuität** verbindet, was das Bild trennt: zwei Räume, einen Kampf und die Ruhe danach. Hyper Light Drifter hatte dafür mehrere Fassungen desselben Stücks, „that kind of crossfade between each other“ (ab etwa 48:53). + + Ein Klang hat selten nur eine Aufgabe. Die Schritte eines Gegners hinter dir sind Warnung und Information außerhalb des Bilds zugleich. Notier im Protokoll die wichtigste zuerst. + + ## Quelle in der Spielwelt oder nicht: diegetisch, extradiegetisch, transdiegetisch + + Die Filmtheorie unterscheidet Klänge nach ihrer Quelle. **Diegetisch** ist, was in der erzählten Welt entsteht und was die Figuren hören könnten: Schritte, Stimmen, ein Schwert. **Extradiegetisch** (auch nicht diegetisch) ist, was keine Quelle in dieser Welt hat, etwa die Filmmusik. Kristine Jørgensen zeigt, warum diese Trennung im Spiel nicht reicht ([On Transdiegetic Sounds in Computer Games](https://hdl.handle.net/1956/5855), 2007). Ihr Beispiel: In Oblivion wechselt beim Ritt durch den Wald die Musik zu einer aggressiveren Melodie. Der Spieler weiß, dass ein Gegner kommt, und kann ausweichen oder kämpfen. Beim Hai-Thema in Jaws kann der Zuschauer nur zusehen. Jørgensen nennt Musik, die keine Quelle in der Spielwelt hat und trotzdem über Ereignisse darin informiert, **transdiegetisch**. + + Sie unterscheidet zwei Richtungen: + + - **Extern transdiegetisch**: keine Quelle in der Welt, aber Information für das Handeln darin. Ihr Beispiel ist die Musik in Hitman Contracts, die dramatisch wird, sobald die Figur in einen Kampf gerät. + - **Intern transdiegetisch**: eine Quelle in der Welt, die sich an den Spieler wendet. In Warcraft III antwortet ein Arbeiter „More work?“, wenn der Spieler ihn auswählt. In World of Warcraft sagt die eigene Figur „Not enough mana!“, und nur ihr Spieler hört es. + + Daneben bleiben Klänge, die eindeutig sind: Die Hintergrundmusik von Warcraft III reagiert laut Jørgensen nicht auf das Spiel und wirkt nur als Stimmung, sie ist extradiegetisch. Für Jørgensen ist das Transdiegetische vor allem eine Funktion eines Klangs, kein dritter Raum zwischen den beiden anderen. Das ist für die Analyse der nützliche Teil. Zur Frage „Wo ist die Quelle?“ kommt eine zweite: „Wem sagt der Klang etwas, und kann der Spieler damit handeln?“ Einen breiten Überblick über Geschichte, Theorie und Praxis von Spielton gibt Karen Collins' Lehrbuch [Game Sound](https://doi.org/10.7551/mitpress/7909.001.0001) (MIT Press, 2008). + + Diegetisch heißt nicht realistisch. Ein Treffer-Ton kann stilisiert sein und trotzdem aus der Welt kommen. Umgekehrt kann eine Musik, die nur der Spieler hört, die wichtigste Warnung im Clip sein. + + ## Drei Arten zu hören: kausal, semantisch, reduziert + + Michel Chion unterscheidet in [Audio-Vision](https://cup.columbia.edu/book/audio-vision-sound-on-screen/9780231185899/) im Kapitel „The Three Listening Modes“ drei Arten, auf einen Klang zu hören. **Kausal** hörst du, wenn du nach der Ursache fragst: Wer oder was macht das? **Semantisch** hörst du, wenn du einen Code liest, etwa Sprache oder ein Signal mit vereinbarter Bedeutung. **Reduziert** hörst du, wenn du auf den Klang selbst achtest, unabhängig von Ursache und Bedeutung: Hüllkurve, Lage, Dichte, Raum. + + Im Spiel ist semantisches Hören oft gelernt. Das Overwatch-Team nennt das Ziel „Pavlovian response“: „when you hear a sound you can react as quickly as possible“ (Teil 1, ab etwa 25:46). Tomas Neumann erzählt, dass von der Ansage „Justice rains from above“ der Anfang genügt, und man weiß, was zu tun ist ([Teil 6, Pavlovian Response](https://www.youtube.com/watch?v=ojlqmkoLvHg), ab etwa 2:00). Ein Spieler, der das Spiel kennt, hört also anderes als du beim ersten Durchgang. + + Reduziert hörst du als Produzent ohnehin. Gerade deshalb kommt dieser Durchgang zuletzt. Wer zuerst auf Transienten und Hall achtet, notiert Klangeigenschaften und übersieht, was der Klang dem Spieler sagt. Für einen Clip heißt das drei Durchgänge: + + ```text + Durchgang 1, kausal: + Was erzeugt den Klang? + Durchgang 2, semantisch: + Was bedeutet er im Spiel? + Durchgang 3, reduziert: + Wie klingt er? Hüllkurve, + Lage, Dichte, Raum + ``` + + Chion schreibt über Film. Seine Hörweisen sind ein Werkzeug, mit dem du dieselbe Stelle dreimal anders hörst, kein Gesetz über Spiele. + + ## Klanglandschaft: Grundton, Signal, Klangmarke + + Der Komponist R. Murray Schafer hat den Begriff Soundscape geprägt. Sein Verlag fasst die Aufgabe, die er seinen Lesern stellt, so zusammen: „Our task, he maintains, is to listen, analyze, and make distinctions“ ([The Soundscape](https://www.innertraditions.com/books/the-soundscape)). Das Handbook for Acoustic Ecology, herausgegeben von Barry Truax und ursprünglich vom World Soundscape Project der Simon Fraser University veröffentlicht, definiert die drei Begriffe, die du für Spiele brauchst: + + - **Grundton** (keynote): Klänge, die so beständig zu hören sind, dass sie einen Hintergrund bilden, „against which other sounds are perceived“. Oft nimmt man sie nicht bewusst wahr ([Keynote](https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Keynote.html)). + - **Signal** (sound signal): „any sound or message which is meant to be listened to“. Signale stehen immer im Verhältnis zu ihrem Grundton, wie Figur und Grund im Bild (Eintrag Sound Signal). + - **Klangmarke** (soundmark): abgeleitet von landmark, ein Klang, der für einen Ort einzigartig ist oder dort besonders beachtet wird (Eintrag Soundmark). + + Auf eine Spielszene übertragen: + + | Begriff | Im Spiel | Frage | + | --- | --- | --- | + | Grundton | Bett der Atmo | Was hört man immer? | + | Signal | Schritt, Warnung | Worauf reagiert man? | + | Klangmarke | Klang eines Orts | Woran erkennt man ihn? | + + Atmo heißt in diesem Kurs ein Bett aus Grundton plus einzelne Spot-Klänge. Der Wind in Hyper Light Drifter ist Grundton, die Fackeln sind Spot-Klänge mit Ort. Ob ein Spot-Klang zur Klangmarke wird, hängt daran, ob er einen Ort unverwechselbar macht. Für Orientierung zählen die Klangmarken, nicht der Grundton. + + Das Handbuch kennt noch ein Begriffspaar, das dir im Modul zur Mischung wieder begegnet. In einer Hi-Fi-Umgebung sind alle Klänge klar zu hören, „without being crowded or masked“. In einer Lo-Fi-Umgebung sind die Signale „overcrowded, resulting in masking and lack of clarity“ (Einträge Hi-Fi und Lo-Fi). Schafer bewertet damit Umgebungen. Für die Analyse nimmst du die Begriffe als Beschreibung: Kann man in diesem Moment ein Signal noch heraushören? + + ## Priorität und Stille: was der Spieler nicht verpassen darf + + Nicht jeder Klang ist gleich wichtig. Das Overwatch-Team stellte für den Mix eine Frage: „what is your greatest threat“ ([Teil 3, A Clear Mix](https://www.youtube.com/watch?v=2M5cWHswQsM), ab etwa 3:23). Wer auf dich zielt, ist wichtiger als jemand, der näher steht, aber wegschaut. Die Genauigkeit der Richtung ging dabei vor Ästhetik. Ein breites Stereobild „may not sound cinematic“, heißt es im Vortrag, aber „it's better for the game to say this person's right there“ (Teil 4, ab etwa 11:27). Sakurai ordnet die Lautstärke in Smash nach Wichtigkeit: KO zuerst, Schritte zuletzt ([Trefferfeedback](/combat/trefferfeedback), Abschnitt Ton). Wie man solche Reihenfolgen baut, ist Stoff von [Mischung: Musik, Geräusche und Stille](/sound/mischung-stille). + + Für dein Protokoll reicht eine Ziffer: 1 heißt, der Spieler darf den Klang nicht verpassen, 2 hilft ihm, 3 ist Hintergrund. Du schätzt die Priorität. Ob das Spiel sie auch so setzt, hörst du daran, was in dichten Momenten noch durchkommt. + + Zur Priorität gehört ihr Gegenteil: Stellen, an denen es leise wird. In Overwatch dämpft das Spiel alles, wenn du dich in eine Nische zurückziehst. „Everything would just get quiet and muffled and you would hear clearly one person walk in the room“ (Teil 4, ab etwa 7:19). Die Stille dort ist eine Information: Hier bist du sicher, und was du jetzt noch hörst, zählt. Stille im Protokoll heißt deshalb selten digitale Null. Notier, was wegfällt und was bleibt. + + ## Das Hörprotokoll: 30 bis 60 Sekunden mit Kopfhörern + + Das Hörprotokoll hält fest, was du hörst, in Zeilen, die man vergleichen kann. Es passt aufs Handy, wenn du kurze Kürzel nimmst. So gehst du vor: + + 1. Wähl einen Clip von 30 bis 60 Sekunden, am besten Spielszenen ohne Kommentar. Längere Clips werden ungenau. + 2. Stell die Lautstärke mit Kopfhörern einmal ein und lass sie, bis du fertig bist. + 3. Hör einmal ohne Notizen, mit Bild. + 4. Hör mit geschlossenen Augen und notier, was du ohne Bild weißt. + 5. Trag jeden Klang mit Zeit, Quelle, Aufgabe und Priorität ein, ein Klang pro Zeile. Hör dafür kausal, dann semantisch. + 6. Notier die Stellen, an denen es still wird, und was dort bleibt. + 7. Hör ein letztes Mal reduziert und ergänze Klangeigenschaften, wo sie dir auffallen. + 8. Werte aus: Welche Information kommt nur über den Ton? + + Ein Protokoll in dieser Form, für einen ausgedachten Clip: + + ```text + Zeit Klang Q Aufg. P + 0:03 Schritte Fig. B Rück 3 + 0:05 Wind - Stim 3 + 0:11 Schritte re. A Warn 1 + 0:12 Musik dichter - Warn 1 + 0:14 Hieb B Rück 2 + 0:15 Treffer B Rück 1 + 0:18 Stille 1 s - Kont - + bleibt: Wind + ``` + + Q ist die Quelle: B im Bild, A außerhalb des Bilds, - ohne Quelle in der Spielwelt. Aufg. ist die wichtigste Aufgabe als Kürzel: Rück, Warn, Auß, Ort, Stim und Kont stehen für die sechs Aufgaben aus der Tabelle oben. P ist die Priorität. Wo du unsicher bist, schreib ein Fragezeichen dazu. Ein Fragezeichen ist ein Befund, keine Lücke. + + Die Auswertung beantwortet drei Fragen. Welche Klänge mit Priorität 1 haben keine Quelle im Bild? Das ist die Information, die nur der Ton liefert, im Beispiel die Schritte rechts und die dichtere Musik. Wie viel Vorlauf gibt eine Warnung? Rechne ihn in Frames, wie im [Combat-Kurs](/combat/testszene-ausweichen), wo der Telegraph des Trainingsgegners in den Beispielläufen 20 Frames dauert. Und wo wird es still, und was kommt danach? + + > [!MERKE] + > Notier, was ein Klang dem Spieler sagt, nicht, ob er dir gefällt. Geschmack bekommt eine eigene Zeile. + + > [!PRAXIS] Vom Clip zum Befund + > Im Overwatch-Studio liefen Aufnahmen mit. Fühlte sich ein Tod unfair an, schickten Kollegen dem Audio-Team das Video. Das Team sah es sich an, baute die Situation nach und änderte die Werte, damit der Spieler eine Chance zum Reagieren hatte (Teil 3, ab etwa 11:19). Ein Auslöser war die Rückmeldung aus dem Design-Team, jemand sei von hinten herangelaufen und habe geschossen. Danach bekamen die Schritte die höchsten Prioritäten im Mix (ab etwa 11:05). Im Kern ist das ein Hörprotokoll mit einer Frage: Was hätte der Spieler hören müssen, und wann? + + > [!TIP] Kopfhörer und Lautstärke + > Im Bus ist es laut. Dreh nicht nach, wenn eine Stelle leise wird, sonst misst du deine Hand statt des Mixes. Leise Stellen notierst du als leise. + + > [!INTERVIEW] + > „Wie analysierst du den Ton eines Spiels, das du nicht kennst?“ Gute Antwort, etwa für eine Stelle als Sound Designer oder Audio Designer: Ich nehme einen kurzen Clip und höre ihn mehrmals mit Kopfhörern, einmal mit geschlossenen Augen. Jeden Klang trage ich mit Zeit, Quelle im Bild oder außerhalb, Aufgabe und Priorität ein. Dann frage ich, welche Information nur über den Ton kommt, wie viel Vorlauf die Warnungen haben und wo es still wird. Was mir gefällt, notiere ich getrennt, denn ein schöner Klang kann trotzdem eine Warnung verdecken. + + ## Häufige Fehler + + - **Geschmack statt Aufgabe.** „Klingt fett“ sagt nichts darüber, was der Spieler erfährt. Aufgabe und Geschmack in getrennte Spalten. + - **Gewusstes statt Gehörtes.** Wer das Spiel kennt, trägt ein, was er erwartet. Mit geschlossenen Augen hören und nur eintragen, was im Clip zu hören ist. + - **Nur die lauten Klänge.** Grundton und Stille fehlen im Protokoll, obwohl sie den Rahmen für jedes Signal bilden. + - **Diegetisch mit realistisch verwechselt.** Die Frage ist, woher der Klang kommt und wem er etwas sagt, nicht, ob er echt klingt. + - **Lautstärke nachgeregelt.** Danach stimmen die Verhältnisse im Protokoll nicht mehr. + - **Zu lange Clips.** Fünf Minuten ergeben eine Liste ohne Zeitangaben. 30 bis 60 Sekunden genügen für eine Frage. + - **Reduziert zuerst gehört.** Dann stehen Hall und Transienten im Protokoll, aber nicht, was der Klang bedeutet. + + ## Zusammenfassung + + - Ein Klang im Spiel hat Aufgaben: Rückmeldung, Warnung, Information außerhalb des Bilds, Orientierung, Stimmung, Kontinuität. Oft mehrere zugleich. + - Diegetisch heißt, die Quelle liegt in der Spielwelt. Transdiegetisch ist ein Klang nach Jørgensen, wenn er über die Grenze hinweg informiert, etwa Musik, die einen Gegner ankündigt. + - Chions drei Hörweisen ergeben drei Durchgänge: kausal, semantisch, reduziert. Reduziert zuletzt. + - Schafers Grundton, Signal und Klangmarke beschreiben Bett, Reaktion und Ort. + - Priorität fragt, was der Spieler nicht verpassen darf. Overwatch fragt nach der größten Bedrohung. + - Stille ist selten Null. Notier, was wegfällt und was bleibt. + - Das Hörprotokoll hat eine Zeile pro Klang: Zeit, Klang, Quelle, Aufgabe, Priorität. Die Auswertung sucht Information, die nur der Ton liefert. +exercises: + - id: aufgaben-der-schritte + type: choice + title: Schritte von rechts + prompt: | + Im Clip ist nur deine Figur im Bild. Von rechts, jenseits des Bildrands, kommen Schritte näher. Eine Sekunde später erscheint ein Gegner und greift an. Welche Aufgaben erfüllen die Schritte? Mehrere Antworten richtig. + options: + - text: Warnung + correct: true + why: Sie kündigen den Angriff eine Sekunde vorher an. + - text: Information außerhalb des Bilds + correct: true + why: Der Gegner ist noch nicht zu sehen. Nur der Ton sagt, dass und wo er kommt. + - text: Rückmeldung auf deine Eingabe + correct: false + why: Du hast nichts gedrückt. Rückmeldung bestätigt eine eigene Eingabe, etwa einen Treffer. + - text: Kontinuität zwischen zwei Räumen + correct: false + why: Im Clip wechselt kein Raum. Die Schritte verbinden nichts, sie kündigen an. + explanation: | + Ein Klang hat oft mehrere Aufgaben. Diese Schritte warnen und liefern zugleich, was das Bild nicht zeigt. Im Protokoll trägst du die wichtigste zuerst ein, hier die Warnung, und notierst die Quelle als „außerhalb“. So taucht der Klang in der Auswertung bei der Information auf, die nur der Ton liefert. Overwatch hat solche Schritte so gemischt, dass ein Spieler sie hört, bevor der Gegner schießt. + resources: + - title: Wwise Tour 2016 · Overwatch (3 of 7) – A Clear Mix + url: https://www.youtube.com/watch?v=2M5cWHswQsM + + - id: transdiegetisch-einordnen + type: choice + title: Welche Klänge überschreiten die Grenze? + prompt: | + Welche Beispiele aus Kristine Jørgensens Aufsatz sind dort transdiegetisch? Mehrere Antworten richtig. + options: + - text: In Hitman Contracts wird die Musik dramatisch, sobald die Figur in einen Kampf gerät. + correct: true + why: Extern transdiegetisch. Die Musik hat keine Quelle in der Welt, gibt dem Spieler aber Information für sein Handeln. + - text: In Warcraft III antwortet ein Arbeiter „More work?“, wenn der Spieler ihn auswählt. + correct: true + why: Intern transdiegetisch. Die Figur ist Teil der Welt, spricht aber den Spieler an. + - text: In World of Warcraft sagt die eigene Figur „Not enough mana!“, und nur ihr Spieler hört es. + correct: true + why: Intern transdiegetisch. Andere Spieler hören den Satz nicht, er richtet sich an den Spieler. + - text: Die Hintergrundmusik von Warcraft III, die nicht auf das Spielgeschehen reagiert. + correct: false + why: Laut Jørgensen wirkt sie nur als Stimmung. Sie ist extradiegetisch. + - text: Ein Schwert, das im Bild auf einen Schild trifft. + correct: false + why: Die Quelle liegt in der Welt, die Figuren könnten es hören. Das ist diegetisch. + explanation: | + Jørgensen fragt nach der Quelle und nach der Funktion. Extern transdiegetisch ist ein Klang ohne Quelle in der Welt, der trotzdem über sie informiert. Intern transdiegetisch ist ein Klang aus der Welt, der sich an den Spieler wendet. Musik, die nur färbt, und Klänge, die nur aus der Welt kommen, bleiben in ihren alten Kategorien. Für dein Protokoll heißt das: Eine Musik ohne Quelle kann eine Warnung mit Priorität 1 sein. + resources: + - title: Kristine Jørgensen · On Transdiegetic Sounds in Computer Games + url: https://hdl.handle.net/1956/5855 + + - id: protokoll-ablauf + type: order + title: Ein Hörprotokoll Schritt für Schritt + prompt: | + Bring die Schritte des Hörprotokolls in die Reihenfolge aus der Lektion. + lang: prose + lines: | + Einen Clip von 30 bis 60 Sekunden wählen und die Lautstärke einmal festlegen + Einmal ohne Notizen hören, mit Bild + Mit geschlossenen Augen hören und notieren, was du ohne Bild weißt + Jeden Klang mit Zeit, Quelle, Aufgabe und Priorität eintragen + Notieren, wo es still wird und was dort bleibt + Ein letztes Mal reduziert hören und Klangeigenschaften ergänzen + Auswerten, welche Information nur über den Ton kommt + explanation: | + Die Lautstärke steht vor allem anderen fest, sonst stimmen die Verhältnisse nicht. Der erste Durchgang ohne Notizen gibt dir den Überblick, der zweite mit geschlossenen Augen zeigt, was der Ton allein trägt. Dann folgen die Zeilen, kausal und semantisch gehört, und die Stille. Reduziertes Hören kommt zuletzt, damit Klangeigenschaften nicht die Frage nach der Bedeutung verdrängen. Die Auswertung braucht alle Zeilen und steht deshalb am Ende. + + - id: nur-ueber-den-ton + type: output + title: Was nur der Ton sagt + prompt: | + Ein Protokoll als Liste, die Zeit in Sekunden. Die Quelle ist `B` im Bild, `A` außerhalb des Bilds, `-` ohne Quelle in der Spielwelt. Priorität 1 ist die höchste. Was gibt das Programm aus? + lang: python + code: | + # Zeit (s), Klang, Quelle, Prio + liste = [ + (3, "Schritte Fig.", "B", 3), + (7, "Wind", "-", 3), + (11, "Schritte re.", "A", 1), + (12, "Musik dichter", "-", 1), + (14, "Hieb", "B", 2), + (15, "Treffer", "B", 1), + (19, "Rufen li.", "A", 2), + ] + for zeit, klang, q, prio in liste: + if q != "B" and prio == 1: + print(zeit, klang) + expected: | + 11 Schritte re. + 12 Musik dichter + hints: + - "Gesucht sind Zeilen, deren Quelle `q` nicht `B` ist und deren Priorität 1 ist." + explanation: | + Der Filter ist die erste Frage der Auswertung: Welche wichtige Information zeigt das Bild nicht? Die Schritte rechts kommen von außerhalb, die dichtere Musik hat gar keine Quelle in der Welt. Beide kündigen den Angriff an. Der Treffer hat auch Priorität 1, ist aber im Bild zu sehen, und das Rufen links ist nur Priorität 2. In einem echten Protokoll sind das oft nur zwei, drei Zeilen. Gerade sie zeigen, wofür der Ton im Spiel gebraucht wird. + + - id: vorlauf-in-frames + type: output + title: Wie viel Vorlauf gibt die Warnung? + prompt: | + Aus einem Protokoll: drei Ankündigungen und die Ereignisse, die sie ankündigen, in Sekunden. Das Programm rechnet den Vorlauf in Frames bei 60 Frames pro Sekunde. Was gibt es aus? + lang: python + code: | + FPS = 60 + # Klang, Ankündigung, Ereignis (s) + paare = [ + ("Ruf", 11.20, 11.90), + ("Zischen", 23.50, 24.00), + ("Musik", 40.00, 43.25), + ] + for name, an, ereignis in paare: + frames = (ereignis - an) * FPS + print(name, round(frames)) + expected: | + Ruf 42 + Zischen 30 + Musik 195 + hints: + - "0,7 Sekunden sind 0,7 × 60 Frames." + explanation: | + 0,7 s sind 42 Frames, 0,5 s sind 30, 3,25 s sind 195. Der Trainingsgegner im Combat-Kurs hat einen Telegraph von 20 Frames; alle drei Ankündigungen geben hier mehr Vorlauf. In Frames rechnest du, damit du Ton und Spielmechanik in derselben Einheit vergleichen kannst. Zeiten, die du im Clip abliest, sind nur auf etwa eine Zehntelsekunde genau; das reicht für einen Vergleich. + resources: + - title: Godot Docs · ProjectSettings, physics_ticks_per_second + url: https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-physics-common-physics-ticks-per-second + + - id: begriffe-luecken + type: gap + title: Sechs Begriffe, ein Absatz + prompt: | + Ergänze die Begriffe aus der Lektion. + lang: prose + code: | + Ein Klang, der so beständig zu hören ist, dass er einen Hintergrund bildet, heißt bei Schafer ⟦Grundton⟧. Ein Klang, auf den man hören soll, ist ein ⟦Signal⟧. Ein Klang, der einen Ort unverwechselbar macht, ist eine ⟦Klangmarke⟧. Wer fragt, was einen Klang erzeugt, hört ⟦kausal⟧. Wer seine Bedeutung nach einem Code liest, hört ⟦semantisch⟧. Wer nur auf seine Eigenschaften achtet, hört ⟦reduziert⟧. + gaps: + - label: Hintergrund + hint: Im Englischen keynote, wie der Grundton einer Tonart. + accept: ["Keynote", "Keynote Sound"] + - label: Zum Hinhören + hint: Englisch sound signal. + accept: ["Sound Signal", "Signalklang"] + - label: Ort + hint: Abgeleitet von landmark. + accept: ["Soundmark"] + - label: Ursache + hint: Chions erste Hörweise, von „Ursache“. + accept: ["causal"] + - label: Code + hint: Wie Sprache oder Morsezeichen. + accept: ["semantic"] + - label: Eigenschaften + hint: Der Klang wird auf sich selbst zurückgeführt. + accept: ["reduced"] + explanation: | + Schafers Begriffe beschreiben eine Klanglandschaft: Bett, Signale und Orte. Chions Hörweisen beschreiben, wie du hörst. Beide zusammen ergeben die Spalten deines Protokolls: Die Quelle findest du kausal, die Aufgabe semantisch, Klangeigenschaften reduziert. Ob ein Klang Grundton oder Signal ist, hängt an der Szene, nicht am Klang selbst. + resources: + - title: Handbook for Acoustic Ecology · Sound Signal + url: https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Sound_Signal.html + - title: Handbook for Acoustic Ecology · Soundmark + url: https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Soundmark.html + + - id: quelle-falsch + type: bug + title: Null Klänge von außerhalb + prompt: | + Zum Clip: Im Bild ist nur deine Figur. Der Gegner taucht erst bei 0:24 im Bild auf. Deine Auswertung sagt trotzdem: „Kein Klang mit Quelle außerhalb des Bilds.“ Finde die Zeile, die das verursacht, und korrigiere sie. + lang: text + code: | + Zeit Klang Q Aufg. P + 0:04 Schritte Fig. B Rück 3 + 0:09 Wind - Stim 3 + 0:21 Schritte re. B Warn 1 + 0:24 Gegner Ruf B Warn 2 + 0:25 Hieb B Rück 2 + lines: [4] + fix: + - line: 4 + answers: + - 0:21 Schritte re. A Warn 1 + explanation: | + Bei 0:21 ist der Gegner noch nicht zu sehen. Seine Schritte kommen also von außerhalb des Bilds, Quelle `A`. Wer das Spiel kennt oder den Clip schon gesehen hat, weiß, wer da läuft, und trägt aus Versehen `B` ein. Dann fehlt in der Auswertung die Information, die nur der Ton liefert. Deshalb der Durchgang mit geschlossenen Augen: Er trennt Gehörtes von Gewusstem. + resources: + - title: Wwise Tour 2016 · Overwatch (4 of 7) – Pinpoint Accuracy + url: https://www.youtube.com/watch?v=F2N8bbzhHd8 + + - id: klangmarke-falsch + type: bug + title: Die Orte klingen gleich + prompt: | + Du willst mit diesen Notizen festhalten, woran man drei Orte eines Clips am Klang unterscheidet. Beim Nachlesen merkst du, dass eine Zeile dabei nicht hilft, weil sie einen Klang falsch einordnet. Finde sie. + lang: prose + code: | + Der Wind ist in allen drei Gebieten zu hören. Er ist die Klangmarke des Spiels. + Der Brunnen ist nur auf dem Platz zu hören. Er ist die Klangmarke des Platzes. + Das Brummen der Maschine ist nur in der Halle zu hören. Es ist ihre Klangmarke. + Die Schritte des Gegners sind ein Signal. Auf sie reagiert man. + lines: [1] + explanation: | + Ein Klang, der überall zu hören ist, ist ein Grundton, kein Ort. Er bildet den Hintergrund, vor dem Signale und Klangmarken auffallen. Eine Klangmarke macht einen bestimmten Ort unverwechselbar, wie der Brunnen den Platz. Für Orientierung zählen deshalb die Klänge, die es nur an einem Ort gibt. Der Wind sagt dir, dass du im Spiel bist, nicht wo. + resources: + - title: Handbook for Acoustic Ecology · Keynote + url: https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Keynote.html + + - id: high-noon-erklaeren + type: explain + title: Eine Ansage, die durch Wände geht + prompt: | + Erkläre in eigenen Worten, warum das Overwatch-Team diese Ausnahmen gemacht hat. + lang: prose + code: | + Schritte und Waffenfeuer haben verschiedene Abstandskurven: + Die Waffe hört man von weiter weg, die Schritte erst in der Nähe. + Die Ansage „It's high noon“ wird von Wänden kaum gedämpft. + Das Team hat Dämpfungswerte bei Ansagen der stärksten Fähigkeiten bewusst weggelassen. + points: + - Die Ansage ist eine Warnung, auf die jeder sofort reagieren muss, auch hinter einer Wand. + - Priorität geht hier vor physikalischer Wahrscheinlichkeit. + - Die gelernte Bedeutung wirkt nur, wenn die Ansage zuverlässig ankommt. + - Verschiedene Abstandskurven geben zusätzliche Information, etwa ob ein Gegner nah oder nur in Hörweite ist. + explanation: | + McCrees Ansage kündigt einen Angriff an, der jeden im Blickfeld treffen kann. Würde eine Wand sie dämpfen, verpasste ausgerechnet der Spieler die Warnung, der in Gefahr ist. Das Team hat deshalb bei solchen Ansagen auf die Dämpfung verzichtet und Priorität über Realismus gestellt. Weil die Ansage gelernt ist, reicht oft der Anfang, und man sucht Deckung. Die verschiedenen Kurven für Schritte und Waffen sind dieselbe Idee im Kleinen: Hört man nur die Waffe, ist der Gegner weit weg; hört man die Schritte, ist er nah und kommt vielleicht um die Ecke. + resources: + - title: Wwise Tour 2016 · Overwatch (4 of 7) – Pinpoint Accuracy + url: https://www.youtube.com/watch?v=F2N8bbzhHd8 + - title: Wwise Tour 2016 · Overwatch (6 of 7) – Pavlovian Response + url: https://www.youtube.com/watch?v=ojlqmkoLvHg + + - id: film-gegen-spiel + type: explain + title: Dieselbe Musik, andere Wirkung + prompt: | + Erkläre mit Jørgensens Begriffen, warum eine Musik ohne Quelle im Film extradiegetisch bleibt, im Spiel aber transdiegetisch werden kann. + lang: prose + code: | + Film: Das Hai-Thema setzt ein. Die Figur im Wasser ahnt nichts. + Spiel: Die Musik wird aggressiver. Der Spieler weicht dem Gegner aus, den er noch nicht sieht. + points: + - Im Film kann der Zuschauer die Information nicht in Handlung umsetzen, im Spiel kann der Spieler es. + - Die Musik hat in beiden Fällen keine Quelle in der erzählten Welt. + - Im Spiel beeinflusst sie, was in der Spielwelt passiert, und ist deshalb extern transdiegetisch. + - Transdiegetisch ist eine Funktion des Klangs, kein eigener Ort. + explanation: | + Der Zuschauer von Jaws hört das Hai-Thema, aber er kann nichts tun; die Musik bleibt Kommentar für ihn. Der Spieler von Oblivion hört die aggressivere Melodie und handelt: Er weicht aus oder bleibt und kämpft. Die Quelle ist in beiden Fällen dieselbe, nämlich keine in der Welt. Was sich ändert, ist die Funktion: Im Spiel trägt die Musik Information, die in die Welt zurückwirkt. Jørgensen nennt das extern transdiegetisch und betont, dass es um diese Funktion geht. + resources: + - title: Kristine Jørgensen · On Transdiegetic Sounds in Computer Games + url: https://hdl.handle.net/1956/5855 + + - id: drei-durchgaenge + type: practice + title: Ein Clip, drei Hörweisen + prompt: | + Nimm einen Clip von 30 Sekunden aus einem Spiel deiner Wahl, Spielszenen ohne Kommentar. Hör ihn dreimal mit Kopfhörern: kausal, dann semantisch, dann reduziert. Schreib nach jedem Durchgang zwei, drei Stichpunkte. Notier am Ende, welcher Durchgang dir etwas gezeigt hat, das die anderen nicht zeigten. + lang: prose + checklist: + - Du hast notiert, welchen Clip du gehört hast und welche 30 Sekunden. + - Zu jedem der drei Durchgänge stehen eigene Stichpunkte. + - Im semantischen Durchgang hast du mindestens eine Bedeutung notiert, die man im Spiel lernen muss. + - Im reduzierten Durchgang hast du Eigenschaften beschrieben, keine Ursachen. + - Du hast eine Beobachtung notiert, die nur einer der drei Durchgänge geliefert hat. + hints: + - Im kausalen Durchgang darfst du raten. Schreib dann ein Fragezeichen dazu. + explanation: | + Die drei Durchgänge trennen, was beim normalen Hören zusammenfällt. Kausal findest du die Quellen, semantisch die Bedeutungen, reduziert die Eigenschaften, mit denen ein Klang seine Aufgabe erfüllt. Oft zeigt der semantische Durchgang am meisten, weil man dort merkt, was ein Spieler erst lernen muss. Der reduzierte bereitet vor, was du in [Klangformung](/sound/klangformung) selbst gestaltest. + resources: + - title: Michel Chion · Audio-Vision (Columbia University Press) + url: https://cup.columbia.edu/book/audio-vision-sound-on-screen/9780231185899/ + + - id: eigenes-hoerprotokoll + type: practice + title: Dein erstes Hörprotokoll + prompt: | + Schreib ein Hörprotokoll für einen Clip von 30 bis 60 Sekunden aus einem Spiel deiner Wahl. Halte dich an die Schritte und Spalten aus der Lektion, mit kurzen Kürzeln, damit es aufs Handy passt. Schreib darunter drei Sätze Auswertung. + lang: prose + checklist: + - Du hast Spiel, Clip und Zeitbereich notiert. + - Das Protokoll hat mindestens acht Zeilen mit Zeit, Klang, Quelle, Aufgabe und Priorität. + - Mindestens eine Zeile hat eine Quelle außerhalb des Bilds oder ohne Quelle in der Spielwelt, oder du hast notiert, dass es keine gibt. + - Du hast mindestens eine Stelle notiert, an der es still oder leiser wird, und was dort bleibt. + - Die Auswertung nennt die Information, die nur der Ton liefert. + - Unsichere Einträge tragen ein Fragezeichen. + - Bewertungen wie „gut“ oder „zu laut“ stehen nicht in den Spalten. + explanation: | + Das Protokoll ist dein Werkzeug für alle weiteren Module. Mit ihm planst du in [Vom Ereignis zur Cue-Liste](/sound/klangplan), welche Klänge dein Spiel braucht, und prüfst in [Mischung](/sound/mischung-stille), ob ein Signal durchkommt. Ein erstes Protokoll ist oft zu grob oder zu lang. Vergleich es nach ein paar Tagen mit einem zweiten Durchgang desselben Clips: Was du dann anders einträgst, zeigt, was du dazugelernt hast. + resources: + - title: Wwise Tour 2016 · Overwatch (1 of 7) – Audio in Overwatch + url: https://www.youtube.com/watch?v=fGDOgn2WXLs diff --git a/content/sound/modules/klangformung.yaml b/content/sound/modules/klangformung.yaml new file mode 100644 index 0000000..e0918c6 --- /dev/null +++ b/content/sound/modules/klangformung.yaml @@ -0,0 +1,515 @@ +id: klangformung +title: "Klangformung: Hüllkurve, Frequenz, Dynamik und Raum für kurze Spielgeräusche" +level: 2 +minutes: 110 +summary: Wie Hüllkurve, Frequenzbänder, Dynamik und Raum bestimmen, wann ein kurzer Klang im Spiel passiert, wie schwer er wirkt und wo er sitzt – mit Rechnungen in Frames, Samples und Dezibel und dem gemessenen Verhalten von Godot 4.7.2 bei Stereo-Dateien und Hall-Bereichen. +place: unterwegs +goals: + - Du bestimmst, wann ein Klang im Spiel hörbar „passiert“, und rechnest Stille vor der Transiente in Samples und Frames um. + - Du vergleichst die Länge eines Klangs mit Aktionsdauer und Wiederholrate und erkennst eine Hallfahne, die ins nächste Ereignis ragt. + - Du ordnest den Frequenzbändern eines kurzen Klangs Rollen zu und nennst mit Hyper Light Drifter und einer Quelle zu Handy-Lautsprechern, was das für Schichten und Bass heißt. + - Du rechnest mit Schwelle und Ratio aus, wie ein Kompressor die Spitzen von Varianten angleicht, und erklärst, wie eine langsame Attack des Kompressors die Transiente betont. + - Du entscheidest begründet zwischen trockenem Klang mit Hall im Spiel und eingebackenem Hall und richtest in Godot 4.7.2 einen Hall-Bereich mit `area_mask` ein. + - Du begründest mit einer Messung in Godot 4.7.2, warum Klänge mit Ort mono sein sollten und was die Draufsicht für links, rechts, oben und unten heißt. +resources: + - title: Godot Docs · Audio effects, Compressor + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_effects.html#compressor + - title: Godot Docs · Importing audio samples, Best practices + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices + - title: Godot Docs · AudioStreamPlayer2D · area_mask + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-property-area-mask + - title: REAPER Effects Guide · ReaComp (PDF, S. 9) + url: https://www.reaper.fm/userguide/REAPEREffectsGuide2021.pdf#page=9 + - title: Akash Thakkar, Rich Vreeland · The Sound and Music of Hyper Light Drifter (GDC 2017) + url: https://www.youtube.com/watch?v=k1b9xKRC74E +lesson: | + Du legst einen eigenen Trefferklang in die [Testszene: Angriff](/combat/testszene-angriff). Laut Log meldet der Dummy den Treffer in Frame 21, und genau dort startet der Ton. Trotzdem wirkt er spät und verwaschen, und wenn der Dummy rechts steht, kommt er von links. In der Datei stehen 40 ms Stille vor dem ersten Ausschlag, die Hallfahne läuft 1,2 Sekunden, und die Datei ist stereo, aufgenommen mit dem Handy links neben der Quelle. + + Hüllkurve, EQ, Kompressor und Hall kennst du aus der Musik. Im Spiel kommt hinzu: Ein kurzer Klang muss in einem bestimmten Frame ankommen, er wiederholt sich in einem Takt, den das Spiel vorgibt, er steht neben vielen anderen, und er hat einen Ort auf dem Bildschirm. Dieses Modul geht die vier Werkzeuge unter diesen Bedingungen durch. Wie dein Klang klingt, entscheidest du; alle Zahlen hier sind Platzhalter oder Messwerte. + + ## Hüllkurve: wann der Klang passiert + + Der hörbare Moment eines kurzen Klangs ist seine **Transiente**, der steile Anstieg am Anfang. Alles davor ist Wartezeit. Bei 60 Frames pro Sekunde dauert ein Frame 16,7 ms, bei 48 kHz sind das 800 Samples. 40 ms Stille am Dateianfang verschieben den hörbaren Treffer also um 2,4 Frames. Der Ton startet im richtigen Frame und klingt im falschen. Wie du die Stille beim Import oder Export abschneidest, steht in [Audio zuordnen](/godot/audio). + + Für den Treffer gilt die Regel aus [Trefferfeedback](/combat/trefferfeedback): Der Ton gehört in den Frame, in dem der Treffer gemeldet wird. Das setzt voraus, dass die Transiente am Anfang der Datei sitzt. Masahiro Sakurai beschreibt in [Strong Attack, Light Reverb](https://www.youtube.com/watch?v=-nWjT49-cds) zwei Regeln für starke Treffer: Sie bekommen einen kurzen Hall mit der Spitze am Anfang (0:56), und ihr Hauptton endet im Hitstop (2:13). Die Testszene hält bei einem Treffer 4 Frames an, das sind 67 ms. Wie lang dein Hauptton ist, entscheidest du; die Rechnung sagt dir, wie er zum Stillstand passt. + + Die Länge zählt auch nach hinten. In der Testszene kann der Spieler mit Treffer alle 29 Frames angreifen. Ein Klang, der länger ist, liegt beim nächsten Angriff noch in der Luft. Das Modell rechnet Hüllkurve und Abstand in Frames; die Zeiten sind Platzhalter: + + ```python run + import math + + FPS = 60 + RATE = 48000 + + + def frames(ms): + return ms * FPS / 1000 + + + # Platzhalter-Klang, Zeiten in ms + stille, attack = 40, 2 + koerper, fahne = 120, 1200 + gesamt = stille + attack + gesamt += koerper + fahne + + t = frames(stille + attack) + print("Transiente nach", + f"{t:.1f} Frames") + smp = stille * RATE // 1000 + print(f"Stille: {smp} Samples") + lang = frames(gesamt) + print(f"Länge: {lang:.1f} Frames") + + # Frames zwischen zwei Treffern + ABSTAND = 29 + n = math.ceil(lang / ABSTAND) + print("gleichzeitig:", n) + ``` + + Bis zu drei Kopien desselben Klangs klingen gleichzeitig, jede mit ihrer Fahne. Das verwischt den nächsten Treffer. Spielt der Player mit `max_polyphony` 1, dem Standard, schneidet jedes neue `play()` den laufenden Ton ab ([Audio zuordnen](/godot/audio)); dann hörst du statt der Überlagerung einen Abbruch. Beides ist eine Folge der Länge, nicht der Engine. Der **Release**, das Ausklingen nach dem Körper, ist im Spiel deshalb eine Frage der Wiederholrate: Wie oft kommt das Ereignis, und was soll dazwischen zu hören sein? + + ## Frequenz: Gewicht, Identität, Detail + + Dieses Modul ist kein EQ-Kurs. Für kurze Spielgeräusche hilft ein grobes Arbeitsmodell mit drei Rollen: Unten sitzt das **Gewicht**, also wie schwer etwas wirkt. In der Mitte sitzt die **Identität**, also woran man erkennt, was es ist. Oben sitzt das **Detail**, das den Klang nah und scharf macht. Wo genau die Grenzen liegen, hängt am Klang; das Modell sagt dir nur, welche Frage du beim Hören stellst. + + Akash Thakkar und Rich Vreeland bauten für Hyper Light Drifter einen Schuss der Diamond Shotgun aus 9 Schichten, manche Sounds aus bis zu etwa 20 ([GDC 2017](https://www.youtube.com/watch?v=k1b9xKRC74E), ab etwa 30:16). Die Schichten verteilen sie auf hohe, mittlere und tiefe Frequenzen, die Wucht kommt aus dem Bass (Fragerunde, 55:46 und 57:01). Das ist das Modell oben, als Arbeitsweise: Jede Schicht übernimmt eine Rolle, statt dass alle denselben Bereich füllen. Wie du Schichten baust, zeigt [Layering und Varianten](/sound/layering-varianten). + + Wo ein Klang gehört wird, ändert die Rechnung. Koukoi Games schreibt in einem Blogbeitrag über sein Handyspiel Crashing Season: „The bass sounds simply cannot be properly replayed through basic mobile device speakers, often resulting in a tinny, treble-heavy sound“ ([Game Developer](https://www.gamedeveloper.com/audio/making-good-sounding-audio-for-mobile-games)). Sein Ausweg ist Sättigung, die Obertöne des Basses hörbar macht. Ein Klang, der sein Gewicht nur ganz unten trägt, verliert es auf kleinen Lautsprechern. Ob dein Spiel dort läuft, entscheidest du; prüfen kannst du es mit dem Lautsprecher deines Handys. + + Wenn viele Klänge gleichzeitig spielen, verdecken sie sich gegenseitig in denselben Bändern. Diese **Maskierung** behandelt [Mischung: Musik, Geräusche und Stille](/sound/mischung-stille). + + > [!PRAXIS] Schichten nach Rollen, nicht nach Menge + > Die 9 Schichten der Diamond Shotgun sind kein Ziel, sondern ein Beispiel dafür, wie ein Studio einen Klang aus Teilen mit klaren Aufgaben baut. Thakkar und Vreeland nennen die Verteilung auf hoch, mittel und tief und den Bass als Quelle der Wucht. Schreib deshalb in die Cue-Liste, welche Schichten ein Klang hat und welche Rolle jede spielt (siehe [Vom Ereignis zur Cue-Liste](/sound/klangplan)). + + ## Dynamik: gleich laute Varianten, geformte Transienten + + In einem Stück formst du Dynamik über die Zeit: Strophe, Refrain, Break. Im Spiel bestimmt der Spieler, wann was kommt, und eine Zufallsauswahl, welche Variante spielt. Jede Variante steht für sich. Ist eine davon deutlich lauter, wirkt ein zufälliger Treffer wichtiger als die anderen. Sakurai ordnet die Lautstärke in [Strike a Balance with Sound Effects](https://www.youtube.com/watch?v=qKjD5qbLUeI) nach Wichtigkeit: KO zuerst, dann Treffer und Spezialangriffe, Schritte zuletzt (1:01 bis 3:14). Lautstärke trägt im Spiel Information; Varianten desselben Ereignisses sollen deshalb dieselbe tragen. + + Ein Kompressor gleicht die Spitzen an. Schwelle, Ratio und Attack kennst du aus der Musik; im Spiel zählt, wie nah die Spitzen der Varianten danach beieinanderliegen. Als statische Kennlinie, ohne Attack und Release: + + ```python run + def komp(pegel, schwelle, ratio): + if pegel <= schwelle: + return pegel + ueber = pegel - schwelle + ueber /= ratio + return schwelle + ueber + + + # Spitzen in dBFS, Platzhalter + for v in [-3.0, -6.0, -9.0]: + aus = komp(v, -12.0, 4.0) + print(v, "->", round(aus, 2)) + ``` + + Aus 6 dB Unterschied werden 1,5 dB. Das gilt nur oberhalb der Schwelle; was darunter liegt, bleibt, wie es ist. Spitzen sind außerdem nicht Lautheit. Ob Varianten gleich laut wirken, hörst du und misst es mit einem Lautheitsmesser, wie in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel). + + Die Attack-Zeit des Kompressors formt die Transiente. Das ist nicht die Attack in der Hüllkurve des Klangs: Eine weiche Hüllkurven-Attack schiebt die Spitze nach hinten, eine langsame Kompressor-Attack lässt sie durch. Die Godot-Doku nennt die Folge für Spielgeräusche: Ein Kompressor „can accentuate transients by using a slower attack, which lets louder parts go through before they are compressed. This can emphasize the "punchiness" of sound effects“ ([Compressor](https://docs.godotengine.org/en/stable/tutorials/audio/audio_effects.html#compressor)). Eine schnelle Kompressor-Attack oder ein Limiter drückt die Transiente dagegen flach, und mit ihr den hörbaren Moment des Treffers. + + ## Raum: trocken liefern oder Hall einbacken + + Hall in der Datei ist endgültig. Hall im Spiel lässt sich mit dem Ort ändern. Die Godot-Doku rät bei den Import-Empfehlungen zum zweiten Weg: Das Bus-System „saves SFX artists the need to add reverb to the sound effects, reducing their size greatly and ensuring correct trimming“ ([Best practices](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices)). Kürzer heißt hier auch: weniger Fahne, die ins nächste Ereignis ragt. + + | | trocken + Bus | eingebacken | + | --- | --- | --- | + | Ort | wechselt mit dem Bereich | fest | + | Datei | kurz | länger | + | Ende | sauber beschnitten, Fahne vom Bus | Fahne muss in der Datei auslaufen | + | Entscheidung | im Spiel | in REAPER | + + Eingebacken hat seinen Platz, wenn der Raum zum Klang gehört statt zum Ort. Sakurais kurzer Hall auf starken Angriffen ist so ein Fall: Er hängt am Angriff, nicht am Ort. Welchen Weg ein Klang nimmt, entscheidest du pro Cue. + + In Godot 2D lenkt ein `Area2D` Klänge auf einen eigenen Bus, wenn `audio_bus_override` an ist; `audio_bus_name` nennt den Bus ([Area2D](https://docs.godotengine.org/en/stable/classes/class_area2d.html#class-area2d-property-audio-bus-override)). Den Bus legst du im Audio-Panel an und gibst ihm dort mit *Effekt hinzufügen* ein `AudioEffectReverb` ([Audio zuordnen](/godot/audio) zeigt das Panel). Welche Bereiche einen Player erreichen, bestimmt sein `area_mask`: laut Klassenreferenz die Area2D-Ebenen, die „affect the sound for reverb and audio bus effects“ ([area_mask](https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-property-area-mask)). Der Standard ist in Godot 4.7.2 `0`, gemessen und so in der Klassenreferenz. Gemessen mit einem 1-kHz-Ton, `Area2D` auf Ebene 1, Bus `Hall` zur Erkennung auf −20 dB: + + ```text + area_mask 0 (Standard) + Pegel Master 0.2500 + Bus Hall stumm + area_mask 1 + Pegel Master 0.0250 + Ton läuft über Hall + ``` + + Mit dem Standard wirkt kein Hall-Bereich, ohne Meldung. Die drei Einstellungen gehen im Inspektor oder im Code. Der Code, mit dem Bus `Hall` als Platzhalter, in 4.7.2 geprüft: + + ```gdscript + func _ready() -> void: + var area: Area2D = $HallArea + area.audio_bus_override = true + area.audio_bus_name = &"Hall" + $Treffer.area_mask = 1 + ``` + + ## Mono, Stereo und die Draufsicht + + Ein Klang mit Ort im Spiel kommt aus einem `AudioStreamPlayer2D`. Die Godot-Doku schreibt: „Many sound effects can generally be converted to mono as opposed to stereo“, und bietet dafür beim WAV-Import „Force > Mono“ an, das beide Kanäle zusammenmischt ([Best practices](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices)). Was passiert, wenn du es nicht tust, zeigt eine Messung mit Godot 4.7.2: ein 1-kHz-Ton als Mono-Datei und als Stereo-Datei mit Signal nur im linken Kanal, Bild 320 × 180, Hörer in der Bildmitte, Spitzenpegel links und rechts: + + | Datei, Position | links | rechts | + | --- | --- | --- | + | Mono, Mitte | 0.2500 | 0.2500 | + | Mono, 150 px rechts | 0.1156 | 0.3469 | + | Mono, 150 px oben | 0.2312 | 0.2312 | + | Stereo nur L, Mitte | 0.2500 | 0 | + | Stereo nur L, 150 px rechts | 0.1156 | 0 | + + Godot faltet die Stereo-Datei nicht zu Mono. Es gewichtet jeden Kanal mit dem Faktor seiner Seite: links bei 150 px rechts mit demselben Faktor wie bei der Mono-Datei, rechts ebenso. Was nur links aufgenommen ist, bleibt links, auch wenn die Quelle rechts steht. So kam der Trefferklang aus dem Einstieg von links. Mono-Dateien dagegen verteilen sich nach der Position. Der Ausweg ist Mono für alles mit Ort: beim Export aus REAPER oder mit „Force > Mono“. Stereo bleibt für Bett und Musik, die über einen `AudioStreamPlayer` ohne Ort laufen. Die Messung zeigt noch etwas: Derselbe Mono-Ton erreicht im `AudioStreamPlayer` 0.5 je Kanal, im `AudioStreamPlayer2D` in der Bildmitte 0.25, also 6 dB weniger. + + Für die **Draufsicht** zeigt die Tabelle eine zweite Folge. Eine Quelle 150 px über der Bildmitte ist auf beiden Seiten gleich laut, nur um den Abstand leiser. Godot unterscheidet oben und unten nicht über die Seite, sondern nur über die Lautstärke. Bewegt sich deine Figur in alle Richtungen, kommen Gegner auch von oben und unten; dort hörst du, wie weit sie weg sind, aber nicht, ob oben oder unten. Was du damit machst, entscheidest du. Reichweite und Panning-Stärke stehen in [Audio zuordnen](/godot/audio). + + > [!WARNING] Hall-Bereich ohne Wirkung + > `area_mask` steht in Godot 4.7.2 standardmäßig auf `0`. Dann erreicht kein `Area2D` den Player, und dein Hall-Bus bleibt stumm. Setz die Ebene deines Bereichs im Player, im Inspektor oder im Code. + + > [!INTERVIEW] + > „Unser Trefferklang wird im richtigen Frame ausgelöst und wirkt trotzdem spät. Woran kann es liegen?“ Gute Antwort, etwa als Technical Sound Designer: Erst die Datei. Ich messe, wie viel Stille vor der Transiente liegt, und rechne sie in Frames um; 40 ms sind bei 60 FPS mehr als zwei Frames. Dann die Hüllkurve: Eine weiche Attack in der Hüllkurve des Klangs oder ein Kompressor mit schneller Attack nimmt der Transiente die Spitze. Dann die Länge: Eine lange Fahne verwischt den nächsten Treffer oder wird von `max_polyphony` abgeschnitten. Erst danach prüfe ich im Log, ob der Auslöse-Frame stimmt. + + ## Häufige Fehler + + - **Stille vor der Transiente.** Jede Millisekunde ist Verzögerung; 800 Samples sind ein Frame bei 48 kHz und 60 FPS. + - **Fahne länger als die Wiederholrate.** Kopien überlagern sich oder werden abgeschnitten. Länge gegen den kürzesten Abstand rechnen. + - **Gewicht nur ganz unten.** Auf kleinen Lautsprechern verschwindet es. + - **Varianten ungleich laut.** Eine zufällige Variante wirkt wichtiger als die anderen. Angleichen, dann hören. + - **Schnelle Kompressor-Attack auf dem Treffer.** Der Kompressor drückt den Moment flach, an dem der Treffer hörbar wird. + - **Hall in jede Datei gebacken.** Der Raum wandert nicht mit dem Ort, und die Datei wird lang. + - **Stereo-Datei mit Ort.** Godot 4.7.2 faltet sie nicht zu Mono; einseitiger Inhalt bleibt auf seiner Seite. + - **`area_mask` vergessen.** Der Standard 0 lässt jeden Hall-Bereich wirkungslos. + + ## Zusammenfassung + + - Der hörbare Moment ist die Transiente. Stille davor ist Verzögerung: 40 ms sind 2,4 Frames bei 60 FPS. + - Länge gegen Aktion und Wiederholrate rechnen. Was länger ist als der Abstand zweier Auslösungen, überlagert sich oder wird abgeschnitten. + - Unten Gewicht, Mitte Identität, oben Detail. Hyper Light Drifter verteilt Schichten nach Bändern; auf Handy-Lautsprechern fehlt der tiefe Bass. + - Varianten tragen dieselbe Information und sollen gleich laut wirken. Ein Kompressor gleicht Spitzen oberhalb der Schwelle an, seine langsame Attack betont die Transiente. + - Trocken liefern und im Spiel verhallen spart Länge und folgt dem Ort; eingebacken passt, wenn der Raum zum Klang gehört. + - In Godot 2D lenkt ein `Area2D` mit `audio_bus_override` auf einen Hall-Bus, aber nur, wenn `area_mask` des Players die Ebene enthält; der Standard ist 0. + - Klänge mit Ort mono: Godot 4.7.2 gewichtet Stereo-Kanäle nur, statt sie zu mischen. Stereo bleibt für Bett und Musik. + - In der Draufsicht hört man links und rechts als Seite, oben und unten nur als Abstand. +exercises: + - id: wann-passiert-es + type: choice + title: Wann ist der Treffer zu hören? + prompt: | + Welche Aussagen über Hüllkurve und Timing eines kurzen Spielgeräuschs stimmen? Mehrere Antworten richtig. + options: + - text: 40 ms Stille vor der Transiente verschieben den hörbaren Treffer bei 60 FPS um mehr als zwei Frames. + correct: true + why: 40 ms sind 2,4 Frames. Der Ton startet im richtigen Frame, hörbar wird er später. + - text: Stille am Dateianfang ist egal, weil Godot den Ton im richtigen Frame startet. + correct: false + why: Godot startet die Datei im richtigen Frame, aber die Datei beginnt mit Stille. Hörbar ist erst die Transiente. + - text: Ist die Hallfahne länger als der Abstand zwischen zwei Auslösungen, überlagern sich die Kopien oder die laufende wird abgeschnitten. + correct: true + why: Mit mehreren Stimmen klingen Fahnen übereinander, mit `max_polyphony` 1 schneidet jedes neue `play()` die laufende ab. + - text: Eine weichere Attack in der Hüllkurve des Klangs macht den Treffer früher hörbar. + correct: false + why: Eine weichere Hüllkurven-Attack verschiebt die Spitze nach hinten und nimmt ihr die Schärfe. Der Treffer wird später und weicher. + - text: Sakurai lässt den Hauptton eines starken Treffers im Hitstop enden. + correct: true + why: So steht es in „Strong Attack, Light Reverb“ ab 2:13. + explanation: | + Zwischen Auslösen und Hören liegt alles, was in der Datei vor der Transiente steht, und alles, was eine weiche Attack der Hüllkurve verzögert. Nach hinten zählt die Länge gegen die Wiederholrate. Sakurais Regel bindet die Länge des Haupttons an den Hitstop; wie lang deiner ist, entscheidest du, aber in Frames rechnen musst du ihn. + resources: + - title: Masahiro Sakurai · Strong Attack, Light Reverb + url: https://www.youtube.com/watch?v=-nWjT49-cds + - title: Godot Docs · Edit > Trim + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-trim + + - id: signalweg-godot + type: order + title: Der Weg eines Klangs mit Ort + prompt: | + Bring die Stationen in die Reihenfolge, die ein Trefferklang mit Hall-Bereich in Godot 4.7.2 durchläuft. + lang: prose + lines: | + Die WAV-Datei wird importiert und mit „Force > Mono“ zu einem Kanal gemischt + Ein AudioStreamPlayer2D spielt sie an seiner Position, Abstand und Seite bestimmen die Kanalgewichte + Ein Area2D, dessen Ebene in area_mask steht, lenkt den Klang auf den Bus Hall + Auf dem Bus Hall verhallt ein AudioEffectReverb den Klang + Der Bus Hall sendet an Master, Master geht an die Lautsprecher + explanation: | + Der Import entscheidet über die Kanäle, bevor irgendetwas spielt. Der Player gewichtet die Kanäle nach Position und gibt den Klang an einen Bus; welchen, entscheidet das Area2D, aber nur, wenn `area_mask` seine Ebene enthält. Effekte sitzen auf dem Bus, und jeder Bus endet in Master. Fehlt ein Klang oder klingt er trocken, gehst du denselben Weg ab. + resources: + - title: Godot Docs · Area2D.audio_bus_override + url: https://docs.godotengine.org/en/stable/classes/class_area2d.html#class-area2d-property-audio-bus-override + + - id: stille-in-frames + type: output + title: Wie spät ist der Treffer? + prompt: | + In deiner Datei liegen 2400 Samples Stille vor der Transiente, die Datei hat 48 kHz, das Spiel läuft mit 60 FPS. Was gibt das Programm aus? + lang: python + code: | + RATE = 48000 + FPS = 60 + stille_samples = 2400 + ms = stille_samples * 1000 / RATE + print(ms, "ms") + frames = round(ms * FPS / 1000, 1) + print(frames, "Frames") + expected: | + 50.0 ms + 3.0 Frames + hints: + - 800 Samples sind bei 48 kHz und 60 FPS ein Frame. + explanation: | + 2400 Samples durch 48 000 sind 0,05 s, also 50 ms. Bei 16,7 ms pro Frame sind das genau 3 Frames. Bei einem Hitstop von 4 Frames landet die Transiente damit fast am Ende des Stillstands statt an seinem Anfang. Schneid die Stille beim Export oder mit Edit > Trim beim Import. + resources: + - title: Godot Docs · Edit > Trim + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-trim + + - id: kopien-gleichzeitig + type: output + title: Wie viele Fahnen gleichzeitig? + prompt: | + Ein Klang ist mit Fahne 900 ms lang. Er wird alle 60, alle 29 oder alle 12 Frames ausgelöst. Wie viele Kopien klingen höchstens gleichzeitig, wenn der Player genug Stimmen hat? Was gibt das Programm aus? + lang: python + code: | + import math + + FPS = 60 + fahne_ms = 900 + lang = fahne_ms * FPS // 1000 + for abstand in [60, 29, 12]: + n = math.ceil(lang / abstand) + print(abstand, n) + expected: | + 60 1 + 29 2 + 12 5 + hints: + - 900 ms sind 54 Frames. + explanation: | + 54 Frames passen in 60 einmal, in 29 knapp zweimal und in 12 viereinhalbmal; aufgerundet klingen also bis zu 1, 2 und 5 Kopien übereinander. Bei schnellen Ereignissen wie Schritten oder Schüssen wird aus einer schönen Fahne ein Teppich. Mit `max_polyphony` 1 wäre es stattdessen ein Abbruch alle 12 Frames. Die Länge eines Klangs plant man gegen den kürzesten Abstand, mit dem er kommen kann. + resources: + - title: Godot Docs · AudioStreamPlayer2D · max_polyphony + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-property-max-polyphony + + - id: kompressor-varianten + type: output + title: Drei Varianten durch den Kompressor + prompt: | + Drei Varianten haben Spitzen bei −4, −10 und −16 dBFS. Schwelle −18 dB, Ratio 3:1, statische Kennlinie ohne Attack und Release. Was gibt das Programm aus? + lang: python + code: | + def komp(pegel, schwelle, ratio): + if pegel <= schwelle: + return pegel + ueber = pegel - schwelle + ueber /= ratio + return schwelle + ueber + + + def spanne(werte): + return max(werte) - min(werte) + + + varianten = [-4.0, -10.0, -16.0] + aus = [] + for v in varianten: + a = komp(v, -18.0, 3.0) + aus.append(round(a, 1)) + print(aus) + print(spanne(varianten), "->", + round(spanne(aus), 1)) + expected: | + [-13.3, -15.3, -17.3] + 12.0 -> 4.0 + hints: + - Alle drei liegen über −18 dB. Der Überschuss wird durch 3 geteilt. + explanation: | + Die Überschüsse über der Schwelle sind 14, 8 und 2 dB, geteilt durch 3 also 4,7, 2,7 und 0,7 dB. Aus 12 dB Abstand zwischen der lautesten und der leisesten Spitze werden 4 dB. Das ist die Kennlinie; ein echter Kompressor mit Attack lässt die ersten Millisekunden durch, die Spitzen liegen dann höher. Ob die Varianten danach gleich laut wirken, entscheidet dein Ohr und ein Lautheitsmesser. + resources: + - title: REAPER Effects Guide · ReaComp, Ratio (PDF, S. 9) + url: https://www.reaper.fm/userguide/REAPEREffectsGuide2021.pdf#page=9 + + - id: stereo-vorhersage + type: output + title: Die Stereo-Datei auf der anderen Seite + prompt: | + Gemessene Spitzenpegel aus der Lektion, Godot 4.7.2, Bild 320 × 180. „li“ und „re“ heißen links und rechts der Bildmitte. Sag den fehlenden Wert voraus: Die Stereo-Datei mit Signal nur links steht 150 px links der Bildmitte. Antworte mit zwei Zahlen, erst links, dann rechts, etwa `0.25 0`. + lang: text + code: | + Datei, Position links rechts + Mono Mitte 0.2500 0.2500 + Mono 150 px li 0.3469 0.1156 + Stereo L Mitte 0.2500 0 + Stereo L 150 re 0.1156 0 + Stereo L 150 li ? ? + expected: | + 0.3469 0 + accept: + - 0.3469 0.0 + - 0.3469 0.000 + - 0.3469 0.0000 + - 0.3469, 0 + - 0.3469, 0.0 + - 0,3469 0 + - 0,3469 0,0 + - 0,3469 0,0000 + hints: + - Godot gewichtet jeden Kanal der Datei mit dem Faktor seiner Seite, wie bei der Mono-Datei. + explanation: | + Der linke Kanal bekommt bei 150 px links denselben Faktor wie der linke Kanal der Mono-Datei, also 0.3469. Der rechte Kanal der Datei ist leer, und ein leerer Kanal bleibt leer, egal mit welchem Faktor. Gemessen: 0.3469 und 0. Steht die Quelle links, passt das zufällig; steht sie rechts, kommt der Klang trotzdem von links. Deshalb gehören Klänge mit Ort als Mono ins Spiel. + + - id: rollen-baender + type: gap + title: Drei Rollen, drei Bereiche + prompt: | + Ergänze das Arbeitsmodell aus der Lektion. + lang: prose + code: | + Unten trägt ein kurzer Klang sein ⟦Gewicht⟧, in der Mitte seine ⟦Identität⟧, oben das ⟦Detail⟧. Auf kleinen Handy-Lautsprechern fehlt laut Koukoi Games vor allem der ⟦Bass⟧. + gaps: + - label: unten + hint: Wie schwer etwas wirkt. + accept: ["Wucht"] + - label: Mitte + hint: Woran man erkennt, was es ist. + accept: ["Erkennbarkeit", "Charakter"] + - label: oben + hint: Was den Klang nah und scharf macht. + accept: ["Details"] + - label: was fehlt + hint: Der Bereich, den Thakkar und Vreeland als Quelle der Wucht nennen. + accept: ["tiefe Bass", "tiefen Bass", "Tiefbass", "Bassbereich", "Bässe", "Subbass", "Tiefen", "Tieftonbereich"] + explanation: | + Das Modell ersetzt keinen EQ, es sortiert Fragen: Wirkt es schwer genug, erkennt man es, ist es nah genug? Hyper Light Drifter verteilt Schichten auf diese Bereiche, und die Wucht kommt aus dem Bass. Der fehlt auf kleinen Lautsprechern. Wer dort spielt, braucht Gewicht, das auch weiter oben ankommt, etwa über Obertöne. + resources: + - title: Game Developer · Making good sounding audio for mobile games + url: https://www.gamedeveloper.com/audio/making-good-sounding-audio-for-mobile-games + + - id: hall-bereich-code + type: gap + title: Einen Hall-Bereich einrichten + prompt: | + Ergänze den Code, der in Godot 4.7.2 einen Hall-Bereich einrichtet. Den Bus `Hall` mit seinem `AudioEffectReverb` hast du im Audio-Panel angelegt. Der Bereich `HallArea` soll Klänge auf diesen Bus lenken, und der Player `Treffer` soll von Ebene 1 erreicht werden. + lang: gdscript + code: | + func _ready() -> void: + var area: ⟦Area2D⟧ = $HallArea + area.⟦audio_bus_override⟧ = true + area.audio_bus_name = &"Hall" + $Treffer.⟦area_mask⟧ = 1 + gaps: + - label: Typ des Bereichs + hint: Die Klasse für 2D-Bereiche, die Klänge umlenken können. + - label: Umlenken an + hint: Die Eigenschaft heißt in der Klassenreferenz von Area2D „override“. + - label: Ebenen des Players + hint: Steht in 4.7.2 standardmäßig auf 0. + explanation: | + `var area: Area2D` holt den Bereich in eine Variable vom Typ `Area2D`, damit die folgenden Zeilen kurz bleiben. `audio_bus_override` schaltet das Umlenken des Bereichs an, `audio_bus_name` nennt den Ziel-Bus. Ohne `area_mask = 1` hört der Player den Bereich auf Ebene 1 nicht; der Standard 0 lässt den Hall-Bus stumm. So gemessen mit Godot 4.7.2: mit 0 blieb der Bus stumm, mit 1 lief der Ton darüber. + resources: + - title: Godot Docs · AudioStreamPlayer2D · area_mask + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-property-area-mask + - title: Godot Docs · Area2D.audio_bus_override + url: https://docs.godotengine.org/en/stable/classes/class_area2d.html#class-area2d-property-audio-bus-override + + - id: datenblatt-seite + type: bug + title: Der Treffer kommt von der falschen Seite + prompt: | + Das Datenblatt deines Trefferklangs. Im Spiel steht der Dummy rechts von der Figur, der Treffer klingt aber von links, und das bei jedem Treffer. Finde die Zeile, die das erklärt. + lang: text + code: | + treffer_holz_01.wav + Stille vorn: 0 ms + Transiente: Sample 0 + Kanäle: stereo, Quelle links + Hall: trocken, Bus Hall + Player: AudioStreamPlayer2D + lines: [4] + explanation: | + Godot 4.7.2 faltet eine Stereo-Datei im `AudioStreamPlayer2D` nicht zu Mono, sondern gewichtet beide Kanäle nach der Position. Liegt der Inhalt fast nur links, bleibt er links, egal wo die Quelle steht. Gemessen: Stereo nur links, Quelle 150 px rechts, ergab 0.1156 links und 0 rechts. Exportier den Klang mono oder nutz „Force > Mono“ beim Import. Die übrigen Zeilen sind sauber: keine Stille vorn, trocken auf den Hall-Bus. + resources: + - title: Godot Docs · Force > Mono + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#force-mono + + - id: hall-bus-stumm + type: bug + title: Der Hall-Bus schlägt nie aus + prompt: | + Im Hall-Bereich klingt der Treffer so trocken wie draußen. Die Pegelanzeige des Busses `Hall` bewegt sich nie, der Bus existiert und hat sein Reverb. Finde die Zeile und korrigiere sie. + lang: gdscript + code: | + func _ready() -> void: + var area: Area2D = $HallArea + area.audio_bus_override = true + area.audio_bus_name = &"Hall" + $Treffer.area_mask = 0 + lines: [5] + fix: + - line: 5 + answers: + - $Treffer.area_mask = 1 + - $Treffer.area_mask |= 1 + - $Treffer.set_area_mask(1) + explanation: | + Mit `area_mask = 0` erreicht kein Bereich den Player, also bleibt er auf seinem eigenen Bus. Das ist in Godot 4.7.2 auch der Standard, die Zeile wiederholt ihn nur. Gemessen: mit 0 blieb der Bus `Hall` bei −200 dB, also stumm, mit 1 lief der Ton darüber. Die Ebene im Player muss zur `collision_layer` des Bereichs passen, hier Ebene 1. + resources: + - title: Godot Docs · AudioStreamPlayer2D · area_mask + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html#class-audiostreamplayer2d-property-area-mask + + - id: messung-erklaeren + type: explain + title: Was die Stereo-Messung zeigt + prompt: | + Erkläre an dieser Messung aus Godot 4.7.2 („re“ heißt rechts der Bildmitte), was mit Stereo-Dateien in einem `AudioStreamPlayer2D` passiert und was daraus für Klänge mit Ort und für die Draufsicht folgt. + lang: text + code: | + Datei, Position links rechts + Mono Mitte 0.2500 0.2500 + Mono 150 px re 0.1156 0.3469 + Mono 150 px oben 0.2312 0.2312 + Stereo L Mitte 0.2500 0 + Stereo L 150 re 0.1156 0 + points: + - Godot mischt die Stereo-Datei nicht zu Mono, sondern gewichtet jeden Kanal mit dem Faktor seiner Seite. + - Inhalt, der nur in einem Kanal liegt, bleibt auf dieser Seite, auch wenn die Quelle auf der anderen steht. + - Eine Mono-Datei verteilt sich nach der Position, hier 1 zu 3 bei 150 px rechts. + - Oben und unten ändern nur die Lautstärke über den Abstand, nicht die Seite. + - Klänge mit Ort gehören als Mono ins Spiel; Stereo bleibt für Bett und Musik ohne Ort. + explanation: | + Die Mono-Zeilen zeigen das Panning: 150 px rechts verteilen den Ton etwa 1 zu 3 auf links und rechts, 150 px oben lassen ihn mittig und machen ihn nur um den Abstand leiser. Die Stereo-Zeilen zeigen, dass Godot nicht zusammenmischt. Der linke Kanal bekommt bei 150 px rechts denselben kleinen Faktor wie bei Mono, der rechte Kanal ist leer und bleibt leer. Ein einseitig aufgenommener Klang klingt also immer von seiner Seite. Darum exportiert man Klänge mit Ort mono. In der Draufsicht heißt die dritte Zeile: Ein Gegner oberhalb der Figur klingt mittig, nur ferner. + resources: + - title: Godot Docs · AudioStreamPlayer2D + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html + + - id: lautsprecher-vergleich + type: practice + title: Handy-Lautsprecher gegen Kopfhörer + prompt: | + Spiel einen eigenen kurzen Klang oder deinen Prüfklatscher aus [Feldaufnahme mit dem Handy](/sound/feldaufnahme) einmal über den Lautsprecher deines Handys und einmal über Kopfhörer ab, in gleicher Lautstärke, so gut es geht. Notier in wenigen Sätzen, was sich unten, in der Mitte und oben ändert. + lang: prose + hints: + - "Frag dich bei jeder Rolle: Wirkt es noch schwer? Erkenne ich es noch? Ist es noch nah?" + checklist: + - Du hast notiert, welchen Klang du verwendet hast und über welche zwei Wege du ihn gehört hast. + - Zu jeder der drei Rollen steht ein Satz, was über den Handy-Lautsprecher anders ist. + - Du hast notiert, ob die Transiente auf beiden Wegen zur selben Zeit kommt oder sich anders anfühlt. + - Du hast eine offene Frage für die Arbeit am PC notiert, ohne sie hier zu entscheiden. + explanation: | + Der Vergleich zeigt dir an deinem eigenen Material, was die Quelle zu Handy-Lautsprechern beschreibt. Was dort fehlt, ist meist das Gewicht ganz unten; was bleibt, sind Mitte und Höhen. Ob dein Spiel auf solchen Lautsprechern laufen soll und was du dann änderst, entscheidest du später in REAPER. Die Notiz ist dafür die Grundlage. + resources: + - title: Game Developer · Making good sounding audio for mobile games + url: https://www.gamedeveloper.com/audio/making-good-sounding-audio-for-mobile-games + + - id: datenblatt-eigene-aktion + type: practice + title: Ein Datenblatt für eine eigene Aktion + prompt: | + Wähl eine Aktion aus deinem Spiel oder aus der Testszene, die einen Klang bekommen soll. Schreib ein kurzes Datenblatt: Aktionsdauer und kürzester Abstand zweier Auslösungen in Frames, Frame der Transiente, Länge mit Fahne in Frames, wie viele Kopien sich überlagern können, mono oder stereo, Hall trocken oder eingebacken, jeweils mit einem Satz Begründung. Welche Aktion und welche Werte, entscheidest du; die Werte sind deine Platzhalter. + lang: prose + hints: + - Länge in ms mal 60 durch 1000 ergibt Frames. Überlagerung ist Länge durch Abstand, aufgerundet. + - Frame-Daten von Angriffen findest du im Log der [Testszene](/combat/testszene-angriff), Ton-Bilder in [Animation im Spiel](/animation/animation-im-spiel). + checklist: + - Aktionsdauer und kürzester Abstand stehen in Frames da. + - Du hast notiert, in welchem Frame der Aktion die Transiente liegen soll, und warum gerade dort. + - Die Länge mit Fahne steht in Frames, und die Zahl der möglichen Überlagerungen ist ausgerechnet. + - Mono oder Stereo ist entschieden und mit dem Ort im Spiel begründet. + - Hall trocken oder eingebacken ist entschieden und mit einem Satz begründet. + explanation: | + Das Datenblatt macht aus Hörgefühl prüfbare Angaben. Frame der Transiente, Länge und Abstand kannst du im Spiel gegen das Log halten; Mono und Hall-Weg prüfst du mit der Messung aus der Lektion. Die Entscheidungen bleiben deine, das Blatt macht sie nur sichtbar. Wie du den Klang baust und gegen die Aktion abstimmst, folgt in [Layering und Varianten](/sound/layering-varianten). + resources: + - title: Masahiro Sakurai · Strike a Balance with Sound Effects + url: https://www.youtube.com/watch?v=qKjD5qbLUeI diff --git a/content/sound/modules/klangplan.yaml b/content/sound/modules/klangplan.yaml new file mode 100644 index 0000000..f46c36a --- /dev/null +++ b/content/sound/modules/klangplan.yaml @@ -0,0 +1,573 @@ +id: klangplan +title: Vom Ereignis zur Cue-Liste +level: 1 +minutes: 100 +summary: Wie aus Ton-Bildern der Animation, dem Treffer-Frame im Kampf und den Orten eines Levels eine Cue-Liste wird – jedes Klangereignis mit Auslöser, Typ, Länge, Varianten, Priorität, Bus, Ort, Status und Quelle –, warum Bildnummern und Frames statt Millisekunden zählen und wie ein Zustandsdiagramm Klänge plant, die von Zuständen abhängen. +place: unterwegs +goals: + - Du sammelst Klangereignisse aus Animationsliste, Kampf-Log und Level-Plan und schreibst je Ereignis eine Zeile der Cue-Liste mit festen Spalten. + - Du rechnest zwischen Sprite-Bildern, Frames, Millisekunden und Samples um und begründest, warum der Auslöser als Bild oder Frame notiert wird. + - Du schätzt, wie oft ein Klang pro Minute spielt, und leitest daraus die Zahl der Varianten ab – oder begründest, warum ein Signal bewusst gleich bleibt. + - Du notierst Zustände und Übergänge als Zustandsdiagramm in Textform, leitest Start und Stopp von Loops daraus ab und erkennst, wo ein Wechsel flattern kann. + - Du legst ein Benennungsschema als Platzhalter fest und prüfst Dateinamen dagegen. +resources: + - title: Godot Docs · AnimatedSprite2D, Signal frame_changed + url: https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html#class-animatedsprite2d-signal-frame-changed + - title: Godot Docs · AudioStreamRandomizer, playback_mode + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#class-audiostreamrandomizer-property-playback-mode + - title: Wwise Tour 2016 · Blizzard Overwatch (1 of 7) – Audio in Overwatch + url: https://www.youtube.com/watch?v=fGDOgn2WXLs + - title: Khris Brown · Audio Choreography for Battle Design (GDC 2017) + url: https://www.youtube.com/watch?v=q6Vs3d6CrUc + - title: Universal Category System (UCS) + url: https://universalcategorysystem.com/ +lesson: | + Im Log der Combat-Testszene trifft der Angriff den Dummy in Frame 21. Im Prüfprotokoll des Animationskurses steht in der Zeile „Ton-Bilder“ eine 3 für den Aufprall. Im Level-Design-Kurs steht neben den Hinweisen der Satz „Auch Klang kann ein Hinweis sein“. Drei Kurse haben dir Stellen hinterlassen, an die ein Klang gehört. Keine sagt, wie lang er ist, wie oft er spielt, wie viele Varianten er braucht und über welchen Bus er läuft. Die Liste, die das festhält, heißt in diesem Kurs **Cue-Liste**: eine Zeile pro Klangereignis. + + Aus der Musik kennst du Spurlisten, Arrangements und Session-Notizen. Ein Stück hat eine Zeitleiste, auf der jeder Klang seinen Platz hat. Ein Spiel hat keine. Klänge starten, wenn etwas passiert, so oft es passiert, und zusammen mit allem anderen, was gerade passiert. Die Cue-Liste ersetzt die Zeitleiste: Sie sagt nicht, wann ein Klang spielt, sondern wodurch. + + Alle Werte in diesem Modul sind Platzhalter, die Beispiele stammen aus den Testszenen der anderen Kurse. Welche Klänge dein Spiel bekommt, entscheidest du. Das Modul zeigt, wie du sie notierst, damit du sie bauen, testen und wiederfinden kannst. + + ## Woher die Ereignisse kommen: Animation, Kampf, Level + + Ein **Ereignis** ist ein Moment im Spiel, an dem ein Klang beginnt oder endet. Der **Auslöser** ist das, was das Spiel in diesem Moment meldet: ein Bild der Animation, ein Signal, ein Zustandswechsel. Drei Quellen liefern dir Ereignisse. + + **Animation.** Der Animationskurs markiert Ton-Bilder als Bildnummern, etwa die Kontaktbilder 1 und 5 in einem Gehzyklus mit acht Bildern. Im Prüfprotokoll steht für eine Aktion „Ton-Bilder 3 (Aufprall)“ ([In der Testszene prüfen](/animation/testszene)). In [Animation im Spiel](/animation/animation-im-spiel) hieß es: „Wie die Töne klingen, wie laut sie sind und wie sie sich abwechseln, behandelt später der Kurs Sound.“ Hier beginnt das. + + **Kampf.** Das echte Log der Combat-Testszene mit einem Treffer: + + ```text + 12 Spieler MOVE -> WINDUP + 20 Spieler WINDUP -> ACTIVE + 21 Dummy getroffen: Zone body … + 21 Dummy WAIT -> HITSTUN + 27 Spieler ACTIVE -> RECOVERY + ``` + + Zwei Frames liegen nah beieinander. In Frame 20 geht die Hitbox an, in Frame 21 meldet das Spiel den Treffer. Der Treffer-Ton gehört in Frame 21. Hängt er an Frame 20, klingt auch ein Schlag ins Leere wie ein Treffer ([Trefferfeedback](/combat/trefferfeedback)). Ein Ton, der zu jedem Schlag gehört, ob er trifft oder nicht, ist ein eigenes Ereignis mit eigenem Auslöser. Auch der Gegner liefert Ereignisse. Der Trainingsgegner wechselt im Log mit `10 Dummy WAIT -> TELEGRAPH` in die Ankündigung seines Angriffs ([Testszene: Ausweichen](/combat/testszene-ausweichen)). Ein Klang an dieser Stelle wäre eine Warnung. + + **Level.** Orte und Hinweise liefern Ereignisse ohne Bildnummer: ein Raum, den man betritt, eine Klangmarke an einem Ort, ein Hinweis, der hörbar sein soll ([Ausgestaltung](/level-design/ausgestaltung)). Ihre Auslöser sind Flächen und Zustände, keine Frames. Was eine Klangmarke ist, steht in [Kritisch hören](/sound/hoeren-analysieren). + + Eine Zeile der Cue-Liste ist ein Ereignis, keine Datei. Hinter einem Ereignis können mehrere Dateien stehen, als Varianten oder als Schichten. Overwatch hatte Mitte 2016 rund 5.400 Klänge im Spiel mit im Schnitt etwa 11 Audiodateien, und eine einzelne Schrittdatei wurde von rund 150 Klängen benutzt ([Wwise Tour 2016, Teil 1](https://www.youtube.com/watch?v=fGDOgn2WXLs), ab etwa 20:23 und 21:43). Die Cue-Liste plant die Ereignisse; welche Dateien sie brauchen, folgt aus den Spalten. + + ## Eine Zeile der Cue-Liste: zehn Spalten + + Jede Zeile beantwortet dieselben Fragen. Am Handy schreibst du eine Zeile am besten als Karteikarte, eine Spalte pro Zeile: + + ```text + ID sfx_dummy_treffer + Auslöser Treffer gemeldet + (Log-Frame 21) + Typ One-Shot + Länge max. 15 Frames + Varianten 3 + Priorität 1 + Bus SFX + Ort ja, 2D am Dummy + Status Prüfmuster + Quelle eigene Aufnahme + ``` + + Die Werte sind Platzhalter. Warum jede Spalte da ist: + + - **ID**: der Name nach deinem Benennungsschema. Er verbindet Liste, Dateien und Code. + - **Auslöser**: Signal, Bild oder Frame, dazu die Bedingung, etwa „nur bei Treffer“. Ohne Bedingung spielt der Klang auch dann, wenn er nichts bedeutet. + - **Typ**: Ein **One-Shot** spielt einmal ab und endet von selbst. Ein **Loop** wiederholt sich, bis ihn etwas stoppt, und braucht deshalb einen Start- und einen Stopp-Auslöser. + - **Länge**: die längste Dauer, die die Aktion verträgt, in Frames. Ein Klang, der in die nächste Aktion ragt, verdeckt deren Ton. Wie du Länge und Hüllkurve formst, steht in [Klangformung](/sound/klangformung). + - **Varianten**: wie viele verschiedene Aufnahmen. Die Zahl folgt aus der Häufigkeit, dazu gleich mehr. + - **Priorität**: 1 darf der Spieler nicht verpassen, 3 ist Hintergrund, wie im Hörprotokoll. Was daraus im Mix wird, steht in [Mischung](/sound/mischung-stille). + - **Bus**: über welchen Bus der Klang läuft. Godot findet den Bus über seinen Namen; ein Player mit falsch geschriebenem Namen landet ohne Meldung auf `Master` ([Audio zuordnen](/godot/audio)). + - **Ort**: ob der Klang eine Position im Raum hat. Mit Ort spielt ihn ein `AudioStreamPlayer2D`, ohne Ort ein `AudioStreamPlayer` (ebenda). + - **Status**: geplant, Prüfmuster, aufgenommen, im Spiel, geprüft. So siehst du, was fehlt. + - **Quelle**: eigene Aufnahme oder fremdes Material mit Lizenz, siehe [Quellen und Lizenzen](/sound/quellen-lizenzen). + + Besteht ein Klang aus Schichten, schreib sie mit ihrer Rolle in die Zeile, etwa „Schichten: Transiente, Körper“. Wie man sie baut, zeigen [Klangformung](/sound/klangformung) und [Layering und Varianten](/sound/layering-varianten). + + Für den Überblick reicht dann eine schmale Liste. Ein Beispiel für die Testszenen, OS heißt One-Shot: + + ```text + ID Typ Var + sfx_spieler_schritt OS 4 + sfx_spieler_schwung OS 3 + sfx_dummy_treffer OS 3 + sfx_dummy_telegraph OS 1 + amb_halle_bett Loop 1 + ``` + + Am PC führst du die Liste als Tabelle oder CSV-Datei mit allen zehn Spalten. Eine Zeile pro Ereignis, eine Spalte pro Frage: So kannst du filtern, etwa nach allem mit Status „geplant“ oder allem auf einem Bus. + + > [!PRAXIS] Nach Art ordnen, nicht nach Figur + > Das Overwatch-Team ordnete seine Klänge zuerst nach Helden, alle Klänge einer Figur in einem Ordner. Das erzeugte ständig Fehler: falscher Bus, falsche Priorität, weil jemand es eilig hatte. Wer die Priorität aller Schritte ändern wollte, musste an Hunderte Stellen. Danach ordnete das Team nach Art des Klangs, etwa Fähigkeiten der eigenen Figur getrennt von denen anderer Spieler, und setzte Bus, Hall und Priorität einmal pro Art ([Teil 1](https://www.youtube.com/watch?v=fGDOgn2WXLs), ab etwa 8:02 und 9:31). In deiner Cue-Liste ist das die Kategorie am Anfang der ID: Klänge derselben Kategorie teilen Bus und Priorität, bis du bewusst abweichst. + + ## Bildnummern und Frames statt Millisekunden + + In diesem Kurs heißt ein Bild der Sprite-Animation **Bild** und ein Schritt der Spiellogik **Frame** (im Animationskurs Tick). Godot rechnet die Physik standardmäßig mit 60 Frames pro Sekunde (`physics/common/physics_ticks_per_second`, in Godot 4.7.2 per Skript geprüft: 60, [ProjectSettings](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-physics-common-physics-ticks-per-second)). Eine Animation hat ihr eigenes Tempo in Bildern pro Sekunde; eine neue Animation in einer `SpriteFrames`-Ressource startet mit 5 FPS. Bei 12 FPS steht ein Bild 5 Frames lang. + + Für Audio kommt eine dritte Einheit hinzu: Samples. Bei 48 kHz sind ein Frame 800 Samples, bei 44,1 kHz 735. Welche Abtastrate dein Projekt hat, legst du in [REAPER als Werkbank](/sound/reaper-werkbank) fest. Das Programm rechnet die Ton-Bilder einer Animation in alle drei Einheiten um: + + ```python run + TICKS = 60 # Godot: Frames/s + BILD_FPS = 12 # Platzhalter + RATE = 48000 # Platzhalter: Hz + START = 100 # Frame von Bild 1 + + print("Bild Frame ms Samples") + for bild in (1, 3, 5): + i = bild - 1 + f = i * TICKS // BILD_FPS + ms = round(f * 1000 / TICKS) + smp = f * RATE // TICKS + frame = START + f + print(bild, frame, ms, smp) + ``` + + Warum steht der Auslöser trotzdem als Bild oder Frame in der Liste und nicht in Millisekunden? Weil sich Timing ändert. Wird die Animation schneller, wandert der Ton mit seinem Bild; eine Millisekundenangabe bliebe an der alten Stelle stehen. Dasselbe gilt für Kampfwerte: Verlängert sich der Wind-up um zwei Frames, verschiebt sich der Treffer, und der Ton folgt dem Signal, nicht der Uhr. Millisekunden brauchst du nur, um zu prüfen, ob eine Datei in ihr Zeitfenster passt. + + Zwei Grenzen. Passt das Tempo nicht glatt in 60, etwa bei 8 FPS mit 7,5 Frames pro Bild, steht ein Bild mal 7, mal 8 Frames ([Timing und Spacing](/animation/timing-spacing)). Und jede Millisekunde Stille vor der Transiente der Datei ist eine Millisekunde Verzögerung im Spiel ([Audio zuordnen](/godot/audio)). Der Auslöser kann stimmen, und der Klang kommt trotzdem zu spät. + + > [!MERKE] + > Ein Ereignis, eine Zeile. Den Auslöser notierst du als Bild, Frame oder Signal, nie als Millisekunde. + + ## Häufigkeit und Varianten + + Wie viele Varianten ein Klang braucht, beginnt mit einer Zählung. Ein Gehzyklus mit acht Bildern bei 12 FPS dauert 40 Frames und hat zwei Kontaktbilder. Das sind drei Schritte pro Sekunde, 180 pro Minute. Ein Treffer-Ton spielt so oft, wie der Spieler trifft, eine Ankündigung so oft, wie ein Gegner angreift. + + Wählt das Spiel bei jedem Abspielen zufällig eine von n Varianten, kommt dieselbe Datei mit der Wahrscheinlichkeit 1/n direkt noch einmal. Bei 180 Schritten pro Minute: + + ```python run + pro_minute = 180 # 3 pro Sekunde + print("Varianten Wiederholungen") + for n in (1, 2, 4, 8): + gleich = (pro_minute - 1) / n + print(n, round(gleich)) + ``` + + Mit einer Datei wiederholt sich jeder Schritt; das klingt wie ein Maschinengewehr aus identischen Klicks. Mit vier sind es im Schnitt immer noch rund 45 direkte Wiederholungen pro Minute. Godots `AudioStreamRandomizer` steht standardmäßig auf `PLAYBACK_RANDOM_NO_REPEATS` (in Godot 4.7.2 per Skript geprüft). Laut Doku wählt er dann zufällig, soll aber „avoid playing the same stream twice in a row whenever possible“ ([playback_mode](https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#class-audiostreamrandomizer-property-playback-mode)). Wie du ihn einrichtest, steht in [Audio zuordnen](/godot/audio). Wie du Varianten aufnimmst und angleichst, in [Layering und Varianten](/sound/layering-varianten). + + Mehr Varianten sind nicht immer besser. Khris Brown beschreibt, wie Kampfstimmen früher als „variety pack“ aufgenommen wurden: viele verschiedene Laute für eine Aktion, aus denen das Spiel zufällig wählt, damit das Ohr nicht ermüdet und auch wegen Speichergrenzen ([GDC 2017](https://www.youtube.com/watch?v=q6Vs3d6CrUc), ab etwa 6:46). Für For Honor nahm sie stattdessen kleine, gleichbleibende Laute auf, die genau zu einer Bewegung gehören. Spieler lernten daran die Züge (ab etwa 8:12). Kleine Unterschiede blieben trotzdem, weil eine Stimme nie zweimal genau gleich klingt, und laut Brown ermüdeten die Laute deshalb das Ohr nicht (ab etwa 12:03). Das Overwatch-Team ging bei den stärksten Fähigkeiten ähnlich vor. Tomas Neumann: „the less variation actually meant that learning happened earlier“ ([Teil 6](https://www.youtube.com/watch?v=ojlqmkoLvHg), ab etwa 1:36). + + Die Spalte Varianten hat also zwei Richtungen. Häufige Klänge ohne besondere Bedeutung, etwa Schritte, brauchen Abwechslung. Signale, die der Spieler lernen soll, bleiben erkennbar gleich, mit wenig oder kleiner Abweichung. Welche Klänge in deinem Spiel welche Richtung bekommen, entscheidest du; notier in der Liste den Grund. + + ## Zustände: Klänge, die an einem Zustand hängen + + Manche Klänge hängen nicht an einem Moment, sondern an einem Zustand: ein Bett, solange die Figur in einem Raum ist, ein Loop, solange ein Gegner ausholt, Musik, solange gekämpft wird. Für sie brauchst du ein **Zustandsdiagramm**: Zustände und die Übergänge zwischen ihnen. Am Handy reicht Text. Der Trainingsgegner der Combat-Testszene, nach seinem Code: + + ```text + WAIT + Wartezeit um -> TELEGRAPH + TELEGRAPH + Wind-up um -> ACTIVE + ACTIVE + Aktiv-Frames um -> RECOVERY + RECOVERY + Erholung um -> WAIT + HITSTUN + Hitstun um -> WAIT + aus jedem Zustand + getroffen -> HITSTUN + ``` + + Die letzte Zeile gilt, solange `interruptible` an ist, und das ist im Code der Standard: `take_hit()` schickt den Gegner aus jedem Zustand in `HITSTUN`. Jeder Übergang ist ein möglicher Auslöser. Der Eintritt in `TELEGRAPH` kann einen One-Shot starten. Ein Loop, der den ganzen Telegraph tragen soll, braucht einen Start beim Eintritt und einen Stopp an **jedem** Ausgang. Der Telegraph hat zwei: nach `ACTIVE`, wenn der Wind-up um ist, und nach `HITSTUN`, wenn der Gegner vorher getroffen wird. Der zweite steht nicht unter `TELEGRAPH`, sondern unten bei „aus jedem Zustand“. Das Diagramm zeigt dir alle Ausgänge auf einen Blick, die Liste allein nicht. + + > [!WARNING] Der Loop, der nie aufhört + > Ein Loop ohne Stopp an einem Ausgang läuft weiter, wenn das Spiel diesen Weg nimmt. Meist fällt das erst im Test auf, beim seltenen Weg. Geh für jeden Loop alle Ausgänge seines Zustands durch und trag den Stopp in die Zeile ein. + + Bei Zuständen, die von Spielwerten abhängen, kann ein Wechsel **flattern**. Steht ein Gegner genau an der Grenze, ab der er als nah gilt, wechselt der Zustand in jedem Frame hin und her, und ein Loop startet und stoppt immer wieder. Dagegen helfen zwei Mittel. Erstens eine Wartezeit: Der Wechsel zählt erst, wenn seine Bedingung eine Zeit lang ununterbrochen gilt. Beim Ausstieg heißt diese Wartezeit **Nachlauf**: Der Zustand endet erst, wenn sein Signal eine Zeit lang ununterbrochen fehlt. Zweitens zwei Schwellen, hinein bei einem kleineren Abstand als hinaus. Das heißt **Hysterese**. Für Musik zwischen Zuständen ist das wichtiger als für Geräusche; dort geht [Musik für Zustände](/sound/musik-zustaende) weiter. + + ## Namen, Status und Prüfmuster + + Ein Dateiname nach festem Schema findet sich ohne Suchen und lässt sich per Skript prüfen. Als **Platzhalter** nimmt dieser Kurs für die fertigen Spieldateien `___.wav`, etwa `sfx_dummy_treffer_01.wav`: eine Datei pro Variante. Dein eigenes Schema legst du selbst fest. Diese Regeln helfen bei jedem: + + - Kleinbuchstaben, keine Leerzeichen, keine Umlaute. Pfade in Godot (`res://`), Skripte und Kommandozeilen behandeln sie sonst verschieden. + - Feste Reihenfolge der Teile. Dann sortieren sich alle Klänge eines Objekts zusammen. + - Zweistellige Nummern. Sonst sortiert sich `_10` vor `_2`. + + Für Soundeffekt-Bibliotheken gibt es ein öffentliches System, das Universal Category System. Es will Kategorien vereinheitlichen und „uniformity in a filename structure“ bieten ([UCS](https://universalcategorysystem.com/)). Das Overwatch-Team nannte Ereignisse nach einer Regel: Sie beginnen mit `play_` oder `stop_` (Teil 1, ab etwa 16:43). Ein Prüfskript für das Platzhalter-Schema: + + ```python run + import re + + # kategorie_objekt_aktion_nn.wav + SCHEMA = re.compile( + r"[a-z]+_[a-z]+_[a-z]+" + r"_\d\d\.wav" + ) + namen = [ + "sfx_spieler_schritt_01.wav", + "sfx_spieler_schritt_2.wav", + "sfx_Dummy_treffer_01.wav", + "amb_halle_bett_01.wav", + "sfx_dummy_telegraph 01.wav", + ] + for name in namen: + if SCHEMA.fullmatch(name): + print("ok ", name) + else: + print("FEHLER", name) + ``` + + Solche Skripte, ein Gerüst für die CSV-Datei oder ein Umbenennen per Skript darf dir eine KI schreiben; das ist technische Arbeit. Was in der Liste steht, welche Klänge, wie viele Varianten, welche Priorität, entscheidest du. + + Die Spalte **Status** zeigt, wie weit ein Ereignis ist. Bevor es einen Klang gibt, steht dort ein **Prüfmuster**: ein technisches Signal, mit dem du nur den Auslöser testest, etwa ein Klick, den du mit dem Handy aufnimmst, oder ein Klick des REAPER-Metronoms. Spielt der Klick im richtigen Frame und auf dem richtigen Bus, stimmt die Zeile. Wie du ihn in Godot an ein Signal, eine Zeitleiste oder ein Bild hängst, steht in [Audio zuordnen](/godot/audio). Erst danach lohnt es sich, den eigentlichen Klang zu bauen. + + > [!INTERVIEW] + > „Wir bekommen einen neuen Gegner. Wie planst du seinen Ton?“ Gute Antwort, etwa als Technical Sound Designer: Ich lasse mir die Animationsliste und den Zustandsautomaten geben. Daraus schreibe ich eine Cue-Liste, eine Zeile pro Ereignis, mit Auslöser als Bild oder Frame, Typ, Varianten aus der geschätzten Häufigkeit, Priorität, Bus und ob der Klang einen Ort hat. Für jeden Loop prüfe ich am Diagramm alle Ausgänge. Dann setze ich Prüfmuster ein und vergleiche im Log, ob sie im richtigen Frame kommen. Erst dann entstehen die Klänge, und die Status-Spalte zeigt dem Team, was fertig ist. + + ## Häufige Fehler + + - **Treffer-Ton am Einschalten der Hitbox.** Jeder Fehlschlag klingt wie ein Treffer. Auslöser ist die Treffermeldung. + - **Millisekunden statt Bildnummern.** Nach der nächsten Timing-Änderung sitzt der Klang daneben. + - **Eine Zeile pro Datei.** Varianten und Schichten bekommen eigene Zeilen, und die Liste wird unlesbar. Eine Zeile pro Ereignis, Varianten als Zahl. + - **Loop ohne Stopp-Auslöser.** Er läuft weiter, sobald das Spiel einen Ausgang nimmt, den die Zeile nicht kennt. + - **Atmo als One-Shot.** Die Datei endet, und der Raum ist still. + - **Gleich viele Varianten für alles.** Schritte klingen mechanisch, Signale werden schwer zu lernen. Die Zahl folgt aus Häufigkeit und Aufgabe. + - **Bus und Priorität pro Klang einzeln.** Spätestens bei der zwanzigsten Zeile weicht eine ab. Kategorie zuerst. + - **Namen mit Umlauten und Leerzeichen.** Ein Skript oder ein Pfad scheitert irgendwann daran. + + ## Zusammenfassung + + - Die Cue-Liste hat eine Zeile pro Klangereignis, keine pro Datei. Ereignisse kommen aus Ton-Bildern, Kampf-Log, Zuständen und Orten. + - Zehn Spalten: ID, Auslöser, Typ, Länge, Varianten, Priorität, Bus, Ort, Status, Quelle. + - Auslöser als Bild, Frame oder Signal. Bei 60 Frames pro Sekunde steht ein Bild mit 12 FPS 5 Frames; ein Frame sind 800 Samples bei 48 kHz. + - Der Treffer-Ton gehört zur Treffermeldung, nicht zum Einschalten der Hitbox. + - Varianten folgen aus der Häufigkeit. Bei n zufälligen Varianten wiederholt sich eine mit 1/n; Signale, die man lernen soll, bleiben bewusst gleich. + - Ein Loop braucht einen Start und an jedem Ausgang seines Zustands einen Stopp. Das Zustandsdiagramm zeigt die Ausgänge. + - Gegen flatternde Wechsel helfen zwei Schwellen (Hysterese) und Wartezeiten, die ununterbrochen gelten müssen. Beim Ausstieg heißt die Wartezeit Nachlauf. + - Ein festes Benennungsschema mit Kategorie vorn lässt sich per Skript prüfen. Das Schema hier ist ein Platzhalter. +exercises: + - id: was-gehoert-in-die-liste + type: choice + title: Eine Zeile, ein Ereignis + prompt: | + Welche Aussagen über die Cue-Liste stimmen? Mehrere Antworten richtig. + options: + - text: Ein Schritt mit vier Varianten ist eine Zeile mit der Zahl 4 in der Spalte Varianten. + correct: true + why: Die Liste plant Ereignisse. Die Varianten sind eine Eigenschaft des Ereignisses. + - text: Ein Loop braucht einen Start-Auslöser und einen Stopp-Auslöser. + correct: true + why: Ein Loop endet nicht von selbst. Ohne Stopp läuft er weiter. + - text: Der Auslöser wird in Millisekunden ab Spielstart notiert, damit er eindeutig ist. + correct: false + why: Im Spiel gibt es keine feste Zeitleiste. Der Auslöser ist ein Bild, Frame oder Signal, und er wandert mit, wenn sich das Timing ändert. + - text: Die Spalte Ort sagt, ob der Klang mit einem AudioStreamPlayer2D eine Position bekommt. + correct: true + why: Klänge mit Ort werden mit dem Abstand leiser und wandern zwischen links und rechts, Klänge ohne Ort nicht. + - text: Bus und Priorität legst du am besten für jeden Klang einzeln fest, damit jeder genau stimmt. + correct: false + why: Overwatch hat damit Fehler erzeugt. Eine Kategorie teilt Bus und Priorität, Abweichungen sind bewusst. + explanation: | + Die Cue-Liste ersetzt die Zeitleiste eines Stücks durch Auslöser. Darum ist jede Zeile ein Ereignis, und Auslöser sind Bilder, Frames oder Signale. Loops brauchen zwei Auslöser, One-Shots einen. Bus und Priorität gehören zur Kategorie, damit eine Änderung an einer Stelle reicht. + resources: + - title: Wwise Tour 2016 · Overwatch (1 of 7) – Audio in Overwatch + url: https://www.youtube.com/watch?v=fGDOgn2WXLs + + - id: vom-log-zur-zeile + type: order + title: Vom Log zur Zeile + prompt: | + Bring die Schritte in eine sinnvolle Reihenfolge, um aus dem Kampf-Log eine Zeile der Cue-Liste zu machen und sie zu prüfen. + lang: prose + lines: | + Im Log den Frame suchen, in dem der Treffer gemeldet wird + Die Bedingung notieren: nur bei Treffer, nicht bei jedem Schlag + Typ, Länge, Priorität, Bus und Ort eintragen + Aus der geschätzten Häufigkeit die Zahl der Varianten ableiten + Ein Prüfmuster an den Auslöser hängen und den Status setzen + Im Log prüfen, ob das Prüfmuster im erwarteten Frame spielt + alternatives: + - [1, 2, 4, 3, 5, 6] + explanation: | + Zuerst kommt der Auslöser, denn alles andere hängt daran, wann und unter welcher Bedingung der Klang spielt. Die beschreibenden Spalten und die Varianten kannst du in beliebiger Folge füllen. Danach testet ein Prüfmuster nur den Auslöser, noch ohne gestalteten Klang. Erst wenn es im erwarteten Frame spielt, lohnt sich die Arbeit am Klang. + + - id: hysterese + type: choice + title: Der Loop, der flattert + prompt: | + Ein Loop „Gegner nah“ startet und stoppt im Test fünfmal in zwei Sekunden. Der Gegner steht genau an der Grenze, ab der er als nah gilt. Welche Änderungen am Plan verhindern das? Mehrere Antworten richtig. + options: + - text: Der Wechsel zählt erst, wenn die Bedingung eine Zeit lang ununterbrochen gilt. + correct: true + why: Kurze Wechsel an der Grenze lösen dann nichts aus. + - text: Zwei Schwellen, hinein bei einem kleineren Abstand als hinaus. + correct: true + why: Zwischen den Schwellen bleibt der alte Zustand. Ein Gegner, der an einer Grenze pendelt, überschreitet nicht beide. + - text: Eine kürzere Loop-Datei. + correct: false + why: Die Länge der Datei ändert nichts daran, wie oft der Zustand wechselt. + - text: Mehr Varianten für den Loop. + correct: false + why: Varianten ändern den Klang, nicht die Zahl der Starts und Stopps. + - text: Ein kurzer Fade beim Stoppen. + correct: false + why: Der Fade macht jeden Stopp weicher, aber der Loop pumpt weiter im Rhythmus des Flatterns. + explanation: | + Das Problem liegt im Zustand, nicht im Klang. Zwei Schwellen heißen Hysterese; eine Wartezeit beim Ausstieg heißt Nachlauf. Beide folgen derselben Idee: Ein Zustand wechselt nicht beim ersten Anlass zurück. Für Musik ist das noch wichtiger, weil ein Wechsel dort oft an Takte gebunden ist; das vertieft [Musik für Zustände](/sound/musik-zustaende). + + - id: schritte-in-frames + type: output + title: In welchen Frames klingen die Schritte? + prompt: | + Ein Gehzyklus mit 8 Bildern bei 12 FPS, Kontaktbilder 1 und 5. Die Animation beginnt in Frame 240 und läuft durch. Das Programm sammelt die Frames der ersten vier Schritte. Was gibt es aus? + lang: python + code: | + TICKS = 60 + BILD_FPS = 12 + BILDER = 8 + START = 240 + kontakte = [1, 5] + schritte = [] + runde = 0 + while len(schritte) < 4: + basis = runde * BILDER + for bild in kontakte: + i = basis + bild - 1 + f = i * TICKS // BILD_FPS + schritte.append(START + f) + runde += 1 + print(schritte) + expected: | + [240, 260, 280, 300] + hints: + - Ein Bild steht 60 / 12 = 5 Frames. + - Bild 5 ist das fünfte Bild, also 4 Bilder nach Bild 1. + explanation: | + Ein Bild steht 5 Frames, Bild 5 kommt also 20 Frames nach Bild 1. Ein Zyklus dauert 8 × 5 = 40 Frames, danach beginnt Bild 1 wieder. So entstehen Schritte in 240, 260, 280 und 300: alle 20 Frames einer, drei pro Sekunde. Das ist die Zahl, aus der du die Varianten ableitest. Ändert sich das Tempo der Animation, ändern sich die Frames, die Bildnummern in der Cue-Liste bleiben. + resources: + - title: Godot Docs · SpriteFrames · get_animation_speed + url: https://docs.godotengine.org/en/stable/classes/class_spriteframes.html#class-spriteframes-method-get-animation-speed + + - id: wiederholungen-pro-minute + type: output + title: Wie oft dieselbe Datei? + prompt: | + Ein Klang spielt 90-mal pro Minute. Bei jedem Abspielen wählt das Spiel zufällig und gleich verteilt eine von n Varianten, ohne Wiederholungen zu vermeiden. Das Programm gibt n, die Wahrscheinlichkeit einer direkten Wiederholung und die erwartete Zahl direkter Wiederholungen pro Minute aus. Was gibt es aus? + lang: python + code: | + # Platzhalter: 90-mal pro Minute + pro_min = 90 + for n in (1, 3, 6): + p = round(1 / n, 2) + wdh = round((pro_min - 1) / n) + print(n, p, wdh) + expected: | + 1 1.0 89 + 3 0.33 30 + 6 0.17 15 + hints: + - 90 Abspielungen haben 89 Übergänge von einer zur nächsten. + explanation: | + Bei jedem der 89 Übergänge kommt dieselbe Datei mit der Wahrscheinlichkeit 1/n noch einmal. Mit drei Varianten sind das im Schnitt rund 30 direkte Wiederholungen pro Minute, mit sechs rund 15. Doppelt so viele Varianten halbieren die Wiederholungen, verschwinden lassen sie sie nicht. Das tut der Standardmodus von Godots `AudioStreamRandomizer`, `PLAYBACK_RANDOM_NO_REPEATS`, solange mindestens zwei Varianten da sind. + resources: + - title: Godot Docs · AudioStreamRandomizer, PLAYBACK_RANDOM_NO_REPEATS + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#class-audiostreamrandomizer-constant-playback-random-no-repeats + + - id: flattern-mit-mindestdauer + type: output + title: Welcher Zustand nach dem Hin und Her? + prompt: | + Notation: Jeder Zustand steht links, darunter seine Übergänge. Ein Übergang gilt erst, wenn seine Bedingung so viele Frames **ohne Unterbrechung** zutrifft, wie in Klammern steht. Jede Zeile unter „Ereignisse“ ändert ab diesem Frame, was gilt. Der Plan startet in `RUHE`. Gezählt wird so: Gilt eine Bedingung mit (30) ab Frame 10, wechselt der Plan in Frame 40. + + In welchem Zustand ist der Plan in Frame 250, und in welchem Frame hat er zuletzt gewechselt? Antworte wie `RUHE 0`. + lang: ablauf + code: | + RUHE + Gegner sichtbar (30) -> ALARM + ALARM + kein Gegner (60) -> RUHE + + Ereignisse + 100 Gegner sichtbar + 120 kein Gegner + 130 Gegner sichtbar + 170 kein Gegner + 200 Gegner sichtbar + 215 kein Gegner + expected: ALARM 160 + accept: ["ALARM, 160", "Alarm 160", "alarm 160", "ALARM ab 160", "ALARM seit 160"] + hints: + - Von 100 bis 120 sind es nur 20 Frames. + - Ab 130 ist der Gegner 40 Frames lang sichtbar. + - Nach 170 und nach 215 müsste „kein Gegner“ 60 Frames halten. + explanation: | + Der erste Blick dauert nur 20 Frames, das reicht nicht. Ab 130 ist der Gegner ununterbrochen sichtbar, in Frame 160 sind die 30 Frames voll, und der Plan wechselt nach `ALARM`. „Kein Gegner“ ab 170 hält nur 30 Frames, dann ist er wieder sichtbar. Ab 215 müsste es bis 275 halten. In Frame 250 ist der Plan also noch in `ALARM`, seit Frame 160. Ohne Wartezeiten hätte er in diesem Abschnitt sechsmal gewechselt. + resources: + - title: Godot Docs · Idle and Physics Processing + url: https://docs.godotengine.org/en/stable/tutorials/scripting/idle_and_physics_processing.html + + - id: cue-karten-luecken + type: gap + title: Zwei Karteikarten ausfüllen + prompt: | + Ergänze die Karteikarten. Aus dem Log: Die Hitbox geht in Frame 20 an, der Treffer wird in Frame 21 gemeldet. Der Treffer-Ton soll von der Position des Dummys kommen. Das Bett der Halle läuft, solange die Figur in der Halle ist. + lang: text + code: | + ID sfx_dummy_treffer + Auslöser Treffer gemeldet, + Log-Frame ⟦21⟧ + Typ ⟦One-Shot⟧ + Ort ⟦ja⟧, 2D am Dummy + + ID amb_halle_bett + Typ ⟦Loop⟧ + Start Halle betreten + Stopp Halle ⟦verlassen⟧ + Ort nein + gaps: + - label: Frame + hint: Der Ton gehört zur Treffermeldung, nicht zur Hitbox. + - label: Typ Treffer + hint: Spielt einmal ab und endet von selbst. + accept: ["One-shot", "One shot", "One Shot", "one-shot", "one shot", "OneShot", "Oneshot", "oneshot", "OS"] + - label: Ort Treffer + hint: Hat der Klang eine Position im Raum? + accept: ["Ja", "JA"] + - label: Typ Bett + hint: Läuft, bis ihn etwas stoppt. + accept: ["loop", "LOOP"] + - label: Stopp + hint: Das Gegenstück zu „betreten“. + accept: ["Verlassen", "verlässt", "Verlässt"] + explanation: | + Der Treffer-Ton hängt an Frame 21, weil erst dort feststeht, dass getroffen wurde. Er spielt einmal, also One-Shot, und kommt vom Dummy, also mit Ort. Das Bett ist ein Loop: Es soll so lange laufen, wie die Figur im Raum ist, und braucht deshalb Start und Stopp. Ohne Ort spielt es gleich laut, egal wo die Figur in der Halle steht. Ob dein Bett so funktionieren soll, ist eine Entscheidung; die Karte zwingt dich nur, sie hinzuschreiben. + resources: + - title: Godot Docs · AudioStreamPlayer2D + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html + + - id: dateinamen-luecken + type: gap + title: Namen nach dem Schema + prompt: | + Schreib die Dateinamen nach dem Platzhalter-Schema `___.wav`. + lang: text + code: | + sfx, spieler, schritt, Variante 1 + ⟦sfx_spieler_schritt_01.wav⟧ + sfx, dummy, treffer, Variante 3 + ⟦sfx_dummy_treffer_03.wav⟧ + amb, halle, bett, Variante 1 + ⟦amb_halle_bett_01.wav⟧ + gaps: + - label: Schritt + hint: Zweistellig nummerieren. + - label: Treffer + hint: Variante 3 wird zu 03. + - label: Bett + hint: Kategorie zuerst. + explanation: | + Die Kategorie steht vorn, damit sich alle Klänge einer Art zusammen sortieren und Bus und Priorität teilen können. Die Nummer ist zweistellig, damit `_10` nach `_09` kommt und nicht vor `_2`. Kleinbuchstaben und Unterstriche statt Leerzeichen machen die Namen für Godot-Pfade und Skripte eindeutig. Das Schema ist ein Platzhalter; mit deinem eigenen funktioniert die Prüfung genauso. + resources: + - title: Universal Category System (UCS) + url: https://universalcategorysystem.com/ + + - id: treffer-frame-falsch + type: bug + title: Treffer ins Leere + prompt: | + Im Test hörst du den Treffer-Ton bei jedem Angriff, auch wenn der Dummy ausweicht oder außer Reichweite steht. Finde die Zeile der Karteikarte, die das verursacht, und korrigiere sie. + lang: text + code: | + ID sfx_dummy_treffer + Auslöser Spieler WINDUP -> ACTIVE + Typ One-Shot + Varianten 3 + Priorität 1 + Ort ja, 2D am Dummy + lines: [2] + fix: + - line: 2 + answers: + - Auslöser Treffer gemeldet + - Auslöser Treffer gemeldet (Log-Frame 21) + - Auslöser Treffer gemeldet, Log-Frame 21 + - Auslöser Treffer gemeldet (Frame 21) + - Auslöser Treffermeldung + - Auslöser Treffermeldung (Log-Frame 21) + - Auslöser Log-Frame 21 + - Auslöser Frame 21 + - Auslöser Dummy getroffen + - Auslöser Dummy getroffen (Log-Frame 21) + - Auslöser Dummy WAIT -> HITSTUN + - Auslöser Signal getroffen + hints: + - Die Karte in der Lektion nennt als Auslöser die Treffermeldung mit ihrem Log-Frame. + explanation: | + `WINDUP -> ACTIVE` ist der Frame, in dem die Hitbox angeht, im Log Frame 20. Er kommt bei jedem Angriff, ob er trifft oder nicht. Der Treffer-Ton gehört an die Treffermeldung, im Log Frame 21. Wer `Dummy WAIT -> HITSTUN` einträgt, trifft beim Dummy denselben Frame; die Treffermeldung bleibt der bessere Auslöser, weil sie auch kommt, wenn `interruptible` aus ist und der Gegner nicht in `HITSTUN` wechselt. Ein Ton für jeden Schlag kann trotzdem sinnvoll sein, dann ist er ein eigenes Ereignis mit eigener Zeile. + resources: + - title: Godot Docs · Area2D, Signal area_entered + url: https://docs.godotengine.org/en/stable/classes/class_area2d.html#class-area2d-signal-area-entered + + - id: atmo-verstummt + type: bug + title: Nach 40 Sekunden still + prompt: | + Im Test ist das Bett der Halle beim Betreten zu hören. Nach etwa 40 Sekunden ist die Halle still, obwohl die Figur noch dort steht. Die Datei ist 40 Sekunden lang. Finde die Zeile, die nicht zum Plan passt, und korrigiere sie. + lang: text + code: | + ID amb_halle_bett + Auslöser Halle betreten + Typ One-Shot + Länge 40 s + Stopp Halle verlassen + Ort nein + lines: [3] + fix: + - line: 3 + answers: + - Typ Loop + - Typ loop + - Typ LOOP + explanation: | + Ein One-Shot spielt einmal und endet. Das Bett soll laufen, solange die Figur in der Halle ist, also ein Loop, der beim Verlassen stoppt. Die Zeile mit dem Stopp zeigt schon, dass ein Loop gemeint war; One-Shots brauchen keinen Stopp. In Godot entscheidet bei WAV-Dateien der Loop-Modus im Import-Dock darüber, ob die Datei wiederholt wird ([Audio zuordnen](/godot/audio)). + resources: + - title: Godot Docs · Importing audio samples, Loop Mode + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#edit-loop-mode + + - id: telegraph-karte-erklaeren + type: explain + title: Warum diese Karte so aussieht + prompt: | + Erkläre die Entscheidungen auf dieser Karteikarte: Warum dieser Auslöser, warum nur eine Variante, warum Priorität 1 und ein Ort? Die Werte sind Platzhalter, es geht um die Begründung. + lang: text + code: | + ID sfx_dummy_telegraph + Auslöser Dummy WAIT -> TELEGRAPH + Typ One-Shot + Varianten 1 + Priorität 1 + Ort ja, 2D am Dummy + Status Prüfmuster + points: + - Der Auslöser ist der Zustandswechsel in den Telegraph; der Klang wandert mit, wenn sich die Wartezeit ändert. + - Eine einzige Variante macht die Warnung erkennbar und lernbar. + - Priorität 1, weil der Spieler auf diese Warnung reagieren muss. + - Der Ort sagt, welcher Gegner angreift, auch am Bildrand. + - Das Prüfmuster testet den Auslöser, bevor es einen gestalteten Klang gibt. + explanation: | + Die Karte plant eine Warnung. Ihr Auslöser ist der Eintritt in den Telegraph, kein fester Zeitpunkt; ändert sich die Wartezeit des Gegners, kommt die Warnung trotzdem rechtzeitig. Eine einzige Variante folgt der Idee aus Overwatch und For Honor: Was der Spieler lernen soll, bleibt gleich. Priorität 1 heißt, dass die Warnung in dichten Momenten nicht untergehen darf, und der Ort zeigt, woher der Angriff kommt. Solange der Status „Prüfmuster“ heißt, spielt dort nur ein Klick, mit dem du den Frame im Log prüfst. + resources: + - title: Wwise Tour 2016 · Overwatch (6 of 7) – Pavlovian Response + url: https://www.youtube.com/watch?v=ojlqmkoLvHg + - title: Khris Brown · Audio Choreography for Battle Design (GDC 2017) + url: https://www.youtube.com/watch?v=q6Vs3d6CrUc + + - id: eigene-cue-liste + type: practice + title: Eine Cue-Liste für eine eigene Aktion + prompt: | + Wähl eine Aktion aus deinem Spiel oder einer Testszene, etwa einen Angriff, eine Ausweichrolle oder ein Einsammeln. Welche, entscheidest du. Schreib für sie drei bis sechs Zeilen der Cue-Liste als kurze Karteikarten, mit Auslöser, Typ, Varianten, Priorität, Bus und Ort. Notier zu den Varianten einen Satz Begründung. + lang: prose + checklist: + - Du hast notiert, welche Aktion die Liste abdeckt und woher die Auslöser stammen, etwa aus einer Animationsliste oder einem Log. + - Jede Zeile ist ein Ereignis mit eigenem Auslöser als Bild, Frame, Signal oder Zustandswechsel, keiner in Millisekunden. + - Jeder Loop hat einen Start und für jeden Ausgang seines Zustands einen Stopp. + - Zu den Varianten steht eine Begründung aus Häufigkeit oder Aufgabe. + - Jede ID folgt einem Benennungsschema, das du vorher aufgeschrieben hast. + - Der Status jeder Zeile ist „geplant“ oder „Prüfmuster“. + hints: + - Fang mit dem Ereignis an, das der Spieler auf keinen Fall verpassen darf. + explanation: | + Die kurze Liste ist der Anfang einer längeren. Sie zeigt dir, welche Klänge eine Aktion braucht und welche Fragen du noch nicht beantworten kannst, etwa die Länge, solange die Animation nicht fertig ist. Am PC setzt du für jede Zeile ein Prüfmuster ein und prüfst den Frame im Log. Die Klänge selbst baust du in [REAPER als Werkbank](/sound/reaper-werkbank) und [Layering und Varianten](/sound/layering-varianten). + resources: + - title: Godot Docs · AnimatedSprite2D, Signal frame_changed + url: https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html#class-animatedsprite2d-signal-frame-changed diff --git a/content/sound/modules/layering-varianten.yaml b/content/sound/modules/layering-varianten.yaml new file mode 100644 index 0000000..6516c1d --- /dev/null +++ b/content/sound/modules/layering-varianten.yaml @@ -0,0 +1,601 @@ +id: layering-varianten +title: "Layering und Varianten: ein Klang für eine eigene Aktion" +level: 2 +minutes: 150 +summary: Wie du einen Klang aus Schichten baust, ohne dass sie sich gegenseitig auslöschen, wie viele Varianten eine Aktion braucht, ob du sie vorab renderst oder Godot sie kombinieren lässt, und wie du das Ergebnis im Spiel blind gegen eine zweite Fassung prüfst. +place: pc +goals: + - Du baust einen Klang aus Schichten in einem Folder-Track, richtest die Transienten aus und prüfst Versatz, Polarität und Summenpegel. + - Du rechnest aus Häufigkeit und Variantenzahl aus, wie oft dieselbe Variante kommt, und begründest den Modus des `AudioStreamRandomizer` mit dem in Godot 4.7.2 gemessenen Verhalten. + - Du wägst vorgerenderte Kombinationen gegen Schichten ab, die `AudioStreamSynchronized` zur Laufzeit kombiniert. + - Du legst die Transiente auf das Kontaktbild einer selbst gewählten Aktion und rechnest Bildnummern in Frames und Samples um. + - Du vergleichst zwei Fassungen im Spiel blind und liest aus der Trefferzahl, ob du sie unterscheiden kannst. +resources: + - title: Godot Docs · AudioStreamRandomizer, PlaybackMode + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#enum-audiostreamrandomizer-playbackmode + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html + - title: REAPER User Guide 7.81 · Folder Tracks (S. 99) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=99 + - title: Kotaku · Jonathan Blow über die Schritte in The Witness (2012) + url: https://kotaku.com/sounds-like-every-footstep-will-matter-in-the-next-game-5964696 +lesson: | + Im Training mit dem Dummy aus der [Testszene](/combat/testszene-angriff) schlägst du in einer Minute vierzigmal zu. Jeder Treffer spielt dieselbe Datei. Nach dem zehnten Schlag hörst du nicht mehr den Treffer, sondern die Datei. Jon Fagerström, Sebastian Schlecht und Vesa Välimäki beschreiben das so: Ein einzelnes Sample immer wieder abzuspielen „results in a mechanical output, often called the ‚machine-gun effect‘“ ([DAFx 2021](https://dafx2020.mdw.ac.at/proceedings/papers/DAFx20in21_paper_22.pdf)), kurz: der Maschinengewehr-Effekt. + + Aus der Musik kennst du Round Robin bei Drum-Samplern. Im Spiel kommt hinzu: Wie oft ein Klang spielt, bestimmt der Spieler, und jede Wiederholung hängt an seinem Tastendruck. Dieses Modul baut einen Klang für eine Aktion, die du selbst wählst: aus Schichten, in Varianten, abgestimmt auf die Bilder der Animation und im Spiel gegen eine zweite Fassung getestet. Wie du einen `AudioStreamRandomizer` im Inspektor einrichtest, steht in [Audio zuordnen](/godot/audio). Hier geht es darum, was du hineinlegst und wie er sich verhält. Welche Aktion, welche Quellen und wie es klingt, entscheidest du. Seitenangaben meinen die PDF-Seiten des [REAPER User Guide 7.81](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf). + + ## Schichten bauen: ein Folder-Track je Klang + + Eine **Schicht** (Layer) ist ein Teilklang, der mit anderen zusammen einen Klang ergibt. Nach der Zeit getrennt: Anschlag (die Transiente), Körper, Fahne. Nach der Frequenz: tief, mittel, hoch. Welche Rolle die Bänder spielen und wie Hyper Light Drifter einen Schuss aus 9 Schichten baut, behandelt [Hüllkurve, Frequenz, Dynamik und Raum](/sound/klangformung). Quellen sind deine Aufnahmen, ein Synthesizer wie Serum 2 oder fremdes Material mit geklärter Lizenz ([Quellen und Lizenzen](/sound/quellen-lizenzen)). + + In REAPER bekommt jeder Klang einen **Folder-Track**, jede Schicht eine Spur darin. Die Kinder senden an den Ordner statt an den Master, FX auf dem Ordner wirken auf die Summe, und mit den Fadern der Kinder stellst du das Verhältnis ein ([S. 99](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=99)). Die Regionen liegen über dem ganzen Stapel. In der Region Render Matrix aus [REAPER als Werkbank](/sound/reaper-werkbank) kreuzt du den Ordner an, um die Summe zu rendern, denn der Render einer Spur enthält alles, was in sie geroutet ist (S. 417). Kreuzt du die Kinder an, bekommst du jede Schicht einzeln, für den Fall, dass Godot sie kombinieren soll. + + ```text + Ordner „schlag“ FX für die Summe + ├── anschlag Items a1, a2, a3 + ├── koerper Items k1, k2, k3, k4 + └── fahne Items f1, f2 + Regionen schlag_01 … schlag_04 über dem Stapel + (Namen und Anzahl sind Platzhalter) + ``` + + ## Ausrichten: Transienten, Versatz, Polarität, Summe + + Für das Ausrichten hilft der **Snap Offset**: ein Punkt im Item, an dem es einrastet, statt an seinem Anfang (S. 134). Setzt du ihn auf die Transiente und stellst *Media items snap to* auf *Only snap at start/snap offset*, stapeln sich Items an ihren Transienten (S. 142). Dynamic split kann den Snap Offset auf den Spitzenwert in den ersten Millisekunden setzen (S. 190), und Tab springt zur nächsten Transiente im Item (S. 186). + + Kleine Versätze kosten Tiefe. Spielen zwei ähnliche Schichten mit wenigen Millisekunden Abstand, löschen sich manche Frequenzen aus, andere verstärken sich: ein Kammfilter. Das Modell rechnet zwei gleiche Schichten, eine um 5 ms verschoben, relativ zu einer Schicht allein: + + ```python run + import math + + versatz_ms = 5.0 + for f in (50, 100, 200, 300, 400): + faktor = abs(2 * math.cos(math.pi * f * versatz_ms / 1000)) + db = 20 * math.log10(faktor) if faktor > 1e-9 else float("-inf") + print(f, "Hz", round(db, 1)) + ``` + + Bei 5 ms fällt 100 Hz ganz weg, die erste Auslöschung liegt bei 1 / (2 × Versatz). Echte Schichten sind nie gleich, die Auslöschung ist dann teilweise. Du hörst sie trotzdem, wenn du ein Item um ein paar Millisekunden schiebst und das Gewicht dünner wird. + + **Polarität:** Jede Spur hat einen Knopf, der die Phase umkehrt, und jedes Item die Eigenschaft *invert phase* (S. 19, S. 135). Stammen zwei Schichten aus derselben Aufnahme, etwa eine Kopie mit Tiefpass als eigene tiefe Schicht, löscht eine gedrehte Polarität genau das aus, was beide gemeinsam haben. Klingt die Summe dünner als eine Schicht allein, dreh bei einer die Polarität und vergleiche. + + **Summe:** Schichten addieren sich. Wie viel lauter zwei Signale zusammen werden, hängt davon ab, wie ähnlich sie sind; die Rechnung steht in [Mischung](/sound/mischung-stille). Behalte die Anzeige des Ordners im Blick; der *Dry run* beim Rendern markiert Übersteuerungen (S. 416). + + ## Varianten: wie viele, und was Godot daraus macht + + Eine **Variante** ist eine andere Fassung desselben Klangs. Wie viele du brauchst, folgt aus der Cue-Liste ([Vom Ereignis zur Cue-Liste](/sound/klangplan)): wie oft der Klang pro Minute spielt und wie dicht die Wiederholungen aufeinander folgen. + + Was der `AudioStreamRandomizer` daraus macht, hängt am Modus. Gemessen in Godot 4.7.2, je 20 000 Wiedergaben, jede Variante ein Signal, an dem sich ablesen ließ, welche lief: + + | Modus | n | direkt gleich | A-B-A | + | --- | --- | --- | --- | + | `PLAYBACK_RANDOM` | 4 | 24,8 % | 25,8 % | + | `PLAYBACK_RANDOM_NO_REPEATS` | 4 | 0 % | 33,3 % | + | `PLAYBACK_RANDOM` | 6 | 16,9 % | 16,7 % | + | `PLAYBACK_RANDOM_NO_REPEATS` | 6 | 0 % | 20,4 % | + | `PLAYBACK_RANDOM_NO_REPEATS` | 2 | 0 % | 100 % | + + `PLAYBACK_RANDOM` wiederholt mit 1/n, wie ein Würfel. `PLAYBACK_RANDOM_NO_REPEATS` schließt die zuletzt gespielte Variante aus, laut Doku „whenever possible“ ([PlaybackMode](https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#enum-audiostreamrandomizer-playbackmode)). Dafür kehrt die vorletzte mit 1/(n − 1) zurück, häufiger als beim Würfel. Dieser Modus ist der Standard (`playback_mode` 0). Über einen echten `AudioStreamPlayer` mit 40 `play()`-Aufrufen, aufgenommen am Master-Bus, ergab er 0 direkte Wiederholungen, `PLAYBACK_RANDOM` 10. + + > [!WARNING] Zwei Varianten, ein neues Muster + > Mit zwei Varianten bleibt `PLAYBACK_RANDOM_NO_REPEATS` keine Wahl: A B A B A B. Das ist wieder ein festes Muster, nur mit doppelter Periode. Und dieselbe Datei zweimal im Pool zählt in diesem Modus als eine: Mit A, A, B wechselte Godot 4.7.2 streng zwischen A und B, statt A doppelt so oft zu spielen. Gewichten geht über das Gewicht je Eintrag (`stream_N/weight`, im Code [`set_stream_probability_weight()`](https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#class-audiostreamrandomizer-method-set-stream-probability-weight)). Gemessen in 4.7.2 mit Gewicht 2 für A und 1 für B und C, je 20 000 Wiedergaben: A kam mit `PLAYBACK_RANDOM` in 49 % der Fälle, mit `PLAYBACK_RANDOM_NO_REPEATS` in 40 %, ohne Gewicht wären es 33 %. + + Ein Modell in Python verhält sich genauso, wenn es die zuletzt gezogene Variante ausschließt: + + ```python run + import random + random.seed(1) + + def ziehen(n, zuege, ohne_wiederholung): + folge, letzte = [], None + for _ in range(zuege): + auswahl = [v for v in range(n) if not (ohne_wiederholung and v == letzte)] + letzte = random.choice(auswahl or list(range(n))) + folge.append(letzte) + return folge + + for modus, ohne in (("RANDOM", False), ("NO_REPEATS", True)): + f = ziehen(4, 20000, ohne) + direkt = sum(a == b for a, b in zip(f, f[1:])) / (len(f) - 1) + aba = sum(a == c for a, c in zip(f, f[2:])) / (len(f) - 2) + print(f"{modus:10} direkt {direkt:.3f} A-B-A {aba:.3f}") + ``` + + **Woher die Unterschiede kommen:** Am stärksten unterscheiden sich echte Takes, weil Anschlag und Körper jedes Mal anders ausfallen; deshalb nimmst du in [Feldaufnahme mit dem Handy](/sound/feldaufnahme) gleich mehrere auf. Aus einem Take werden weitere, wenn du einen anderen Ausschnitt nimmst. Die Tonhöhe stellst du je Item in Halbtönen ein, Shift+9 und Shift+0 verschieben um einen Halbton, Shift+7 und Shift+8 um einen Cent ([S. 193–194](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=193)). Godot kann Tonhöhe und Lautstärke bei jeder Wiedergabe zufällig ändern; Nicolae Berbece rät zu leicht veränderter Tonhöhe bei jedem Abspielen ([Game Feel: Why Your Death Animation Sucks](https://www.youtube.com/watch?v=pmSAG51BybY), ab etwa 13:04). Eine Tonhöhenänderung verschiebt aber den ganzen Klang gleich, das Muster aus Anschlag und Körper bleibt. Zufallswerte strecken deine Takes, sie ersetzen sie nicht. + + Gleich laut? Eine Variante ist hier die Summe mehrerer Schichten, also normalisierst du die gerenderte Datei, nicht die Items darin: im Render-Dialog unter *Postprocess…*, „each file separately“ gleicht die Varianten an, „to loudest file“ erhält ihre Unterschiede (S. 414). *Normalize items* je Item würde das Verhältnis der Schichten ändern und den Pegel vor den FX des Ordners ([REAPER als Werkbank](/sound/reaper-werkbank)). Ein Normalisierungswert ist ein Startpunkt. Ob eine Variante lauter wirkt, hörst du im Spiel. + + > [!PRAXIS] 1119 Schritte in The Witness + > Jonathan Blow erklärte 2012 auf dem Blog des Spiels, zitiert bei Kotaku, warum The Witness damals schon 1119 Schrittklänge hatte: getrennte Klänge für linken und rechten Fuß, je Material „5-6 variations for each foot, to avoid mechanical-sounding repeats“, dazu Schritte mit eigenem Hall für bestimmte Orte und „texture footsteps“, die über einen Grundschritt gelegt werden. Läuft man über Gras, durch das etwas Erde schaut, spielt das Spiel den Grasschritt mit einer Erdschicht, deren Lautstärke davon abhängt, wie viel Erde sichtbar ist ([Kotaku](https://kotaku.com/sounds-like-every-footstep-will-matter-in-the-next-game-5964696)). Varianten und Schichten sind dort keine Verzierung, sondern Datenmodell. + + ## Vorgerendert oder in Godot kombiniert + + Zwei Wege führen zu Abwechslung aus Schichten: + + - **Vorgerendert:** Jede Kombination wird eine Datei. Jede hast du gehört und geprüft, aber 3 Anschläge × 4 Körper × 2 Fahnen sind 24 Dateien. + - **Zur Laufzeit:** Du renderst die Schichten einzeln und lässt Godot kombinieren. 9 Dateien ergeben 24 Kombinationen, von denen du manche nie zusammen gehört hast. + + Für die Laufzeit gibt es `AudioStreamSynchronized`. Laut Doku beginnen seine Streams „at exactly the same time when play is pressed“ und enden, wenn der letzte endet ([AudioStreamSynchronized](https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html)). Legst du in jeden Platz einen `AudioStreamRandomizer`, wählt jede Schicht bei jeder Wiedergabe neu: + + ```gdscript title=schichten.gd + extends AudioStreamPlayer + ## Spielt zwei Schichten im selben Sample. Jede Schicht wählt ihre Variante + ## bei jeder Wiedergabe neu. Varianten im Inspektor eintragen. + + @export var anschlag: Array[AudioStream] = [] + @export var koerper: Array[AudioStream] = [] + + + func _ready() -> void: + var schichten := AudioStreamSynchronized.new() + schichten.stream_count = 2 + schichten.set_sync_stream(0, _randomizer(anschlag)) + schichten.set_sync_stream(1, _randomizer(koerper)) + stream = schichten + + + func _randomizer(varianten: Array[AudioStream]) -> AudioStreamRandomizer: + var r := AudioStreamRandomizer.new() + r.playback_mode = AudioStreamRandomizer.PLAYBACK_RANDOM_NO_REPEATS + for v in varianten: + r.add_stream(-1, v) + return r + ``` + + Gemessen in Godot 4.7.2: Mit 3 Anschlägen und 4 Körpern ergaben 200 Wiedergaben alle 12 Kombinationen. Im Aufbau mit dem Master-Bus begannen beide Schichten in jeder von 40 Wiedergaben im selben Sample, und `finished` kam einmal je Wiedergabe. Zwei getrennte Player, im selben Frame gestartet, trafen im selben Messaufbau 100-mal von 100 ebenfalls dasselbe Sample. Das zeigt nur, dass es dort so war; zugesagt ist der gemeinsame Start beim `AudioStreamSynchronized`. Im Inspektor geht es ohne Code: *Stream* → *New AudioStreamSynchronized*, *Stream Count* 2, in jeden Platz einen neuen `AudioStreamRandomizer`. + + > [!DEEP] Wie die Messung lief + > Jede Variante war ein `AudioStreamWAV` mit einem konstanten Wert, je Variante ein anderer. `instantiate_playback()` und `mix_audio()` lieferten für jede Wiedergabe die ersten Samples, und am Wert ließ sich ablesen, welche Variante lief. Für die Gegenprobe spielte ein `AudioStreamPlayer` headless, ein `AudioEffectRecord` auf dem Master-Bus nahm auf, und ein Python-Skript las die WAV aus. Bei den Schichten war eine Schicht in Hundertsteln, die andere in Zehnteln abgestuft, sodass jede Summe genau eine Kombination verriet. + + ## Auf deine Aktion abgestimmt + + Wähl eine Aktion aus deinen Animationen, für die du im Prüfprotokoll von [In der Testszene prüfen](/animation/testszene) die Ton-Bilder markiert hast. Dort stehen Bildnummern, keine Millisekunden ([Animation im Spiel](/animation/animation-im-spiel)). Jetzt rechnest du um. Bild k einer Animation mit s Bildern pro Sekunde beginnt bei (k − 1) / s Sekunden, wenn alle Bilder gleich lang stehen: + + ```text + Animation 12 Bilder/s, Kontakt in Bild 4 + Beginn Bild 4 (4 − 1) / 12 = 0,25 s + Physik-Frames 0,25 × 60 = 15 + Samples 0,25 × 48 000 = 12 000 + ``` + + Die Transiente gehört auf dieses Bild, bei einem Treffer in den Frame, in dem der Treffer gemeldet wird ([Trefferfeedback](/combat/trefferfeedback)). Zwei weitere Maße prüfst du: die Länge des Klangs gegen die Dauer der Aktion und gegen den Abstand zur nächsten Wiederholung. Ein Körper, der länger klingt als der Abstand zwischen zwei Schlägen, liegt beim nächsten Schlag unter dessen Anschlag. + + In REAPER stellst du die Zeitleiste per Rechtsklick auf *Absolute Frames* (S. 46) und die Bildrate unter *Project Settings → Video* auf 60 (S. 408, S. 410). Dann liest du Frames direkt ab. Wie du einen Clip deiner Szene als Bildspur ins Projekt holst, steht in [Foley, Atmo und eigenes Material](/sound/foley-umgebung). + + ## Im Spiel prüfen: A/B blind + + Im Editor klingt jede Fassung anders als im Spiel, zwischen Schritten, Musik und Hitstop. Also vergleichst du im Spiel, und zwar blind: Wer weiß, welche Fassung läuft, hört, was er erwartet. Das Skript hängt an einem `AudioStreamPlayer` unter dem Dummy und spielt je Treffer Fassung A oder B. Tab schaltet um. Mit `blind` wählt es bei jedem Treffer zufällig, und erst F2 gibt aus, was lief. + + Warum F2: Startest du das Spiel aus dem Editor, gehören drei Funktionstasten dem Editor. F8 beendet das Spiel ([Running the scene](https://docs.godotengine.org/en/stable/getting_started/step_by_step/nodes_and_scenes.html#running-the-scene)), F9 hält es im Game-Fenster an, F10 rückt einen Frame vor ([Game embedding](https://docs.godotengine.org/en/stable/tutorials/editor/game_embedding.html#pause-f9)). Laut Quelltext von 4.7.2 erkennt das aus dem Editor gestartete Spiel diese Tasten selbst und meldet sie dem Editor; deine Skripte bekommen sie trotzdem ([scene_debugger.cpp](https://github.com/godotengine/godot/blob/4.7.2-stable/scene/debugger/scene_debugger.cpp)). Eine Auswertung auf F9 hielte das Spiel also zugleich an. Tab und F2 fängt es nicht ab. Getestet ist das Skript in Godot 4.7.2 headless mit simulierten Tasten, nicht im Editor. + + ```gdscript title=ab_hit_sound.gd + extends AudioStreamPlayer + ## Spielt bei jedem Treffer Fassung A oder B desselben Klangs. + ## Tab schaltet zwischen A und B um. Mit blind = true wählt das Skript + ## bei jedem Treffer zufällig; F2 gibt danach aus, was lief. + + @export var stream_a: AudioStream + @export var stream_b: AudioStream + @export var blind := false + + var _use_b := false + var _played: PackedStringArray = [] + + + func play_hit() -> void: + if blind: + _use_b = randi() % 2 == 1 + stream = stream_b if _use_b else stream_a + _played.append("B" if _use_b else "A") + play() + + + func _unhandled_key_input(event: InputEvent) -> void: + if not event.is_pressed() or event.is_echo(): + return + var key := (event as InputEventKey).keycode + if key == KEY_TAB and not blind: + _use_b = not _use_b + print("Fassung jetzt: ", "B" if _use_b else "A") + elif key == KEY_F2: + print("Gespielt: ", " ".join(_played)) + _played.clear() + ``` + + In `enemy.gd` aus der Testszene ruft `take_hit()` den Ton auf, direkt nach der Zeile, die den Treffer ins Log schreibt. So liegt er im Frame der Treffermeldung: + + ```gdscript + HitLog.add("%s getroffen: …" % […]) + if has_node(^"HitSound"): + $HitSound.play_hit() + ``` + + Der Ablauf: zehn Treffer blind, nach jedem A oder B notieren und einen Satz zum Eindruck, erst dann F2. Wer rät, kommt in 5,5 % der Fälle auf 8 von 10 oder mehr; die Rechnung steht in einer Übung. Liegst du bei 5 von 10, unterscheidest du die Fassungen im Spiel nicht, so deutlich der Unterschied im Editor auch war. + + ```text + A/B-Protokoll Datum, Godot 4.7.2 + Aktion …, Treffer in Frame … + A Fassung 1 + B Fassung 2, eine Änderung: … + Blind, 10 Tipp: A A B … + Gespielt: A B B … + Richtig … von 10 + Eindruck „…“ (vor F2 notiert) + Entscheidung … + ``` + + > [!INTERVIEW] + > „Unsere Schritte nerven nach zwei Minuten. Was machst du?“ Gute Antwort, etwa als Audio Designer: Erst zählen, wie oft sie pro Minute spielen und wie viele Varianten es gibt, dann den Modus des Containers prüfen; mit zwei Varianten und Wiederholungsschutz entsteht ein A-B-A-B. Danach mehr echte Takes statt nur Tonhöhe, eventuell Schichten, die die Engine kombiniert. Jede Änderung höre ich im Spiel gegen die alte Fassung, möglichst blind, und ich höre mindestens so lange, wie es dauert, bis es nervt. + + ## Häufige Fehler + + - **Nur die Tonhöhe variiert.** Das Muster aus Anschlag und Körper bleibt erkennbar. + - **Zwei Varianten mit Wiederholungsschutz.** Ergibt A B A B. + - **Dieselbe Datei zweimal im Pool.** Gewichtet sie mit `PLAYBACK_RANDOM_NO_REPEATS` nicht doppelt; dafür gibt es das Gewicht je Eintrag. + - **Schichten um Millisekunden versetzt.** Ein Kammfilter frisst das Gewicht. + - **Gedrehte Polarität bei Schichten aus derselben Aufnahme.** Gemeinsame Anteile löschen sich aus. + - **Transiente nach Millisekunden statt nach dem Kontaktbild gesetzt.** Nach der nächsten Timing-Änderung sitzt sie falsch. + - **Körper länger als der Abstand zur nächsten Wiederholung.** Der nächste Anschlag verschwimmt. + - **Im Editor verglichen, nicht blind.** Du hörst, was du erwartest. + + ## Zusammenfassung + + - Schichten nach Zeit (Anschlag, Körper, Fahne) und Frequenz; in REAPER ein Folder-Track je Klang, Regionen über dem Stapel. + - Snap Offset auf der Transiente richtet Schichten aus; wenige Millisekunden Versatz löschen tiefe Frequenzen aus. + - `PLAYBACK_RANDOM` wiederholt mit 1/n, `PLAYBACK_RANDOM_NO_REPEATS` nie direkt, dafür A-B-A mit 1/(n − 1); mit zwei Varianten entsteht A B A B. + - Echte Takes tragen die Abwechslung, Tonhöhe und Lautstärke strecken sie. + - Vorgerendert heißt geprüft, aber viele Dateien; `AudioStreamSynchronized` mit Randomizern kombiniert zur Laufzeit und startet die Schichten gemeinsam. + - Die Transiente liegt auf dem Kontaktbild: Bild k bei (k − 1) / s Sekunden. + - Fassungen vergleichst du im Spiel und blind; die Trefferzahl sagt, ob du sie unterscheidest. +exercises: + - id: modus-wahl + type: choice + title: Was der Wiederholungsschutz tut + prompt: | + Welche Aussagen über `AudioStreamRandomizer` in Godot 4.7.2 stimmen, so wie in der Lektion gemessen? + options: + - text: Mit `PLAYBACK_RANDOM_NO_REPEATS` und vier Varianten kommt dieselbe Variante nie zweimal direkt hintereinander. + correct: true + why: In 20 000 Wiedergaben gab es keine direkte Wiederholung. + - text: Mit `PLAYBACK_RANDOM_NO_REPEATS` und zwei Varianten wechseln sie streng ab. + correct: true + why: Schließt der Modus die letzte Variante aus, bleibt nur die andere. Gemessen kam A-B-A in 100 % der Fälle. + - text: "`PLAYBACK_RANDOM` vermeidet Wiederholungen, solange es mehr als zwei Varianten gibt." + correct: false + why: "`PLAYBACK_RANDOM` wählt wie ein Würfel; mit vier Varianten war fast jede vierte Wiedergabe eine direkte Wiederholung." + - text: Trägst du dieselbe Datei zweimal ein, spielt `PLAYBACK_RANDOM_NO_REPEATS` sie doppelt so oft. + correct: false + why: Im Modus ohne Wiederholung zählte sie als eine. Mit A, A, B wechselten A und B streng ab. + - text: Ohne Änderung im Inspektor steht ein neuer Randomizer auf `PLAYBACK_RANDOM`. + correct: false + why: Der Standard ist `PLAYBACK_RANDOM_NO_REPEATS`, `playback_mode` 0. + explanation: | + Der Wiederholungsschutz verhindert nur die direkte Wiederholung. Er verlagert sie: Die vorletzte Variante kommt häufiger zurück. Mit zwei Varianten wird daraus ein festes Muster. Mehr echte Varianten sind deshalb das wirksamere Mittel gegen Gleichförmigkeit als der Modus allein. + resources: + - title: Godot Docs · AudioStreamRandomizer, PlaybackMode + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#enum-audiostreamrandomizer-playbackmode + + - id: schichten-ordnen + type: order + title: Ein Klang aus Schichten + prompt: | + Bring die Arbeitsschritte in eine sinnvolle Reihenfolge, vom leeren Projekt zur gerenderten Summe. + lang: prose + lines: | + Einen Folder-Track für den Klang anlegen, je Schicht eine Kinderspur. + Die Items der Schichten auf die Spuren legen. + Snap Offset jedes Items auf seine Transiente setzen und die Items daran ausrichten. + Die Summe gegen jede Schicht allein hören, Versatz und Polarität prüfen. + Mit den Fadern der Kinder das Verhältnis einstellen, die Anzeige des Ordners im Blick. + Je Variante eine Region über den ganzen Stapel legen. + In der Region Render Matrix den Ordner ankreuzen und rendern. + alternatives: + - [1, 2, 3, 4, 6, 5, 7] + explanation: | + Erst die Struktur, dann der Inhalt, dann die Ausrichtung. Versatz und Polarität prüfst du, bevor du Pegel einstellst, weil eine Auslöschung jeden Fader-Wert wertlos macht. Die Regionen hängen nur an der Lage der Items; ob du sie vor oder nach den Fadern legst, ist gleich. Gerendert wird zuletzt, und zwar der Ordner, weil sein Render alle Kinder enthält. + resources: + - title: REAPER User Guide 7.81 · Folder Tracks (S. 99) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=99 + - title: REAPER User Guide 7.81 · Snap Offset (S. 142) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=142 + + - id: wiederholungen + type: output + title: Wie oft hörst du dieselbe Variante? + prompt: | + Ein Treffer spielt 30-mal pro Minute (Platzhalter), es gibt vier Varianten. Das Programm rechnet erwartete Werte aus. Was gibt es aus? + lang: python + code: | + n = 4 # Varianten + treffer = 30 # pro Minute + # PLAYBACK_RANDOM: erwartete direkte Wiederholungen pro Minute + print(round((treffer - 1) / n, 2)) + # PLAYBACK_RANDOM_NO_REPEATS: erwartete A-B-A pro Minute + print(round((treffer - 2) / (n - 1), 2)) + # PLAYBACK_RANDOM: mindestens eine direkte Wiederholung in 10 Treffern + print(round(1 - (1 - 1 / n) ** 9, 3)) + expected: | + 7.25 + 9.33 + 0.925 + explanation: | + Bei 30 Treffern gibt es 29 Paare direkt aufeinanderfolgender Treffer; jedes ist mit 1/4 gleich, also etwa 7 Wiederholungen pro Minute. Der Wiederholungsschutz streicht sie, aber 28 Dreiergruppen kehren mit 1/3 zur vorletzten Variante zurück, also etwa 9 A-B-A pro Minute. Mit `PLAYBACK_RANDOM` ist schon in zehn Treffern zu 92,5 % mindestens eine direkte Wiederholung dabei. Mehr Varianten senken beide Zahlen; welche davon dich stört, hörst du im Spiel. + resources: + - title: Godot Docs · AudioStreamRandomizer + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html + + - id: kombinationen + type: output + title: Vorab rendern oder kombinieren lassen + prompt: | + Drei Anschläge, vier Körper, zwei Fahnen, jede Datei 0,6 Sekunden lang (Platzhalter). Was gibt das Programm aus? + lang: python + code: | + anschlag, koerper, fahne = 3, 4, 2 + laenge = 0.6 + print(anschlag * koerper * fahne) + print(anschlag + koerper + fahne) + print(round(anschlag * koerper * fahne * laenge, 1)) + print(round((anschlag + koerper + fahne) * laenge, 1)) + expected: | + 24 + 9 + 14.4 + 5.4 + explanation: | + Vorgerendert brauchst du eine Datei je Kombination, 24 Dateien mit zusammen 14,4 Sekunden Audio. Lässt du Godot kombinieren, reichen 9 Dateien mit 5,4 Sekunden, und es entstehen dieselben 24 Kombinationen. Der Preis: Du hast nicht jede davon gehört, und jede Wiedergabe mischt drei Stimmen statt einer. Welche Seite überwiegt, hängt an der Zahl der Schichten und daran, wie kritisch einzelne Kombinationen sind. + resources: + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html + + - id: kammfilter + type: output + title: Zwei Schichten, 2,5 ms auseinander + prompt: | + Dasselbe Modell wie in der Lektion, aber mit 2,5 ms Versatz. Was gibt es aus? + lang: python + code: | + import math + + versatz_ms = 2.5 + for f in (100, 200, 400): + faktor = abs(2 * math.cos(math.pi * f * versatz_ms / 1000)) + db = 20 * math.log10(faktor) if faktor > 1e-9 else float("-inf") + print(f, "Hz", round(db, 1)) + expected: | + 100 Hz 3.0 + 200 Hz -inf + 400 Hz 6.0 + hints: + - Die erste Auslöschung liegt bei 1 / (2 × Versatz). + explanation: | + Bei 2,5 ms liegt die erste Auslöschung bei 1 / 0,005 s = 200 Hz. 100 Hz sind eine Viertelperiode verschoben und gewinnen 3 dB, 400 Hz eine ganze Periode und gewinnen die vollen 6 dB. Halbierst du den Versatz, wandert die Auslöschung eine Oktave nach oben. Das ist der Grund, Schichten am Snap Offset auszurichten statt nach Augenmaß. + + - id: zufall-oder-gehoer + type: output + title: Geraten oder gehört? + prompt: | + Im Blindtest lagst du bei 8 von 10 Treffern richtig. Das Programm rechnet, wie wahrscheinlich das beim reinen Raten mit 50 % je Treffer ist. Was gibt es aus? + lang: python + code: | + from math import comb + + richtig, versuche = 8, 10 + p = sum(comb(versuche, k) for k in range(richtig, versuche + 1)) / 2 ** versuche + print(round(p, 3)) + expected: | + 0.055 + explanation: | + Es gibt 45 Wege, genau 8 von 10 zu treffen, 10 Wege für 9 und einen für 10, zusammen 56 von 1024 möglichen Folgen. Raten führt also in 5,5 % der Fälle zu diesem Ergebnis oder einem besseren. Das ist ein Hinweis, dass du die Fassungen unterscheidest, kein Beweis. Ein zweiter Durchgang mit zehn Treffern macht ihn deutlich stärker. Bei 5 oder 6 von 10 unterscheidet dein Ohr die Fassungen im Spiel vermutlich nicht. + + - id: kontaktbild-umrechnen + type: gap + title: Vom Kontaktbild zur Sampleposition + prompt: | + Eine Animation mit 10 Bildern pro Sekunde, alle Bilder gleich lang. Der Kontakt ist Bild 5, Bild 1 beginnt bei 0 ms. Rechne den Beginn von Bild 5 um. + lang: text + code: | + Beginn Bild 5 ⟦400⟧ ms + Physik-Frames ⟦24⟧ bei 60 pro Sekunde + Samples ⟦19200⟧ bei 48 kHz + gaps: + - label: Millisekunden + hint: (5 − 1) / 10 Sekunden + - label: Frames + hint: 0,4 × 60 + - label: Samples + hint: 0,4 × 48 000 + accept: ["19 200", "19.200"] + explanation: | + Bild 5 beginnt, wenn vier Bilder zu je 100 ms abgelaufen sind, also bei 400 ms. Das sind 24 Physik-Frames und 19 200 Samples. Wer mit 5 / 10 rechnet, landet ein ganzes Bild zu spät, bei 500 ms. Rechne mit Bildnummern, solange du planst, und erst beim Platzieren in Frames oder Samples. + resources: + - title: REAPER User Guide 7.81 · Zeitleiste in Absolute Frames (S. 46) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=46 + + - id: schichten-gdscript + type: gap + title: Zwei Schichten, ein Start + prompt: | + Ergänze das Skript so, dass beide Schichten gemeinsam starten und jede ihre Variante ohne direkte Wiederholung wählt. + lang: gdscript + code: | + func _ready() -> void: + var schichten := ⟦AudioStreamSynchronized⟧.new() + schichten.stream_count = 2 + schichten.⟦set_sync_stream⟧(0, _randomizer(anschlag)) + schichten.set_sync_stream(1, _randomizer(koerper)) + stream = schichten + + + func _randomizer(varianten: Array[AudioStream]) -> AudioStreamRandomizer: + var r := AudioStreamRandomizer.new() + r.playback_mode = AudioStreamRandomizer.⟦PLAYBACK_RANDOM_NO_REPEATS⟧ + for v in varianten: + r.⟦add_stream⟧(-1, v) + return r + gaps: + - label: Stream-Klasse + hint: Der Stream, dessen Teile „at exactly the same time“ beginnen. + - label: Platz belegen + hint: Die Methode, die einen Stream in einen nummerierten Platz legt. + - label: Modus + hint: Der Modus, der die zuletzt gespielte Variante auslässt. + - label: Variante anhängen + hint: Ein negativer Index hängt ans Ende des Pools an. + explanation: | + `AudioStreamSynchronized` startet seine Plätze gemeinsam, `set_sync_stream` belegt einen Platz. Jeder Platz bekommt einen eigenen Randomizer, also wählt jede Schicht unabhängig. `add_stream(-1, v)` hängt laut Doku ans Ende an. Der Modus steht hier ausdrücklich, obwohl er der Standard ist; so sieht jeder, der das Skript liest, was gemeint ist. + resources: + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html + - title: Godot Docs · AudioStreamRandomizer · add_stream + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#class-audiostreamrandomizer-method-add-stream + + - id: transiente-zu-spaet + type: bug + title: Der Schlag kommt ein Bild zu spät + prompt: | + Im Spiel klingt der Schlag sichtbar nach dem Kontakt, etwa ein Bild zu spät. Die Animation hat 12 Bilder pro Sekunde, alle gleich lang. Welche Zeile der Planung ist falsch? Korrigiere sie; es reicht `Transiente bei` mit dem Wert in ganzen Millisekunden und `ms`. + lang: text + code: | + Aktion Schlag, 6 Bilder + Kontaktbild Bild 4 + Bilddauer 1000 / 12 = 83,3 ms + Transiente bei 4 × 83,3 = 333 ms + Länge Klang bis Ende Bild 6 + lines: [4] + fix: + - line: 4 + answers: + - Transiente bei (4 − 1) × 83,3 = 250 ms + - Transiente bei 250 ms + - transiente bei 250 ms + - Transiente bei 249,9 ms + - Transiente bei (4 - 1) × 83,3 = 250 ms + - Transiente bei (4 - 1) * 83,3 = 250 ms + - Transiente bei (4 - 1) x 83,3 = 250 ms + - Transiente bei 3 × 83,3 = 250 ms + - Transiente bei 3 * 83,3 = 250 ms + - Transiente bei 3 x 83,3 = 250 ms + - Transiente bei 3 × 83,3 = 249,9 ms + - Transiente bei 3 * 83,3 = 249,9 ms + - Transiente bei 3 x 83,3 = 249,9 ms + explanation: | + Bild 4 beginnt, wenn drei Bilder abgelaufen sind, also bei 3 × 83,3 ms, gerundet 250 ms. 333 ms ist der Beginn von Bild 5. Der Fehler ist ein klassisches Um-eins-daneben: Bild 1 beginnt bei 0, nicht bei einer Bilddauer. Wer Ton-Bilder als Nummern plant, rechnet erst ganz am Ende in Zeit um. + + - id: polaritaet + type: bug + title: Die Summe ist dünner als eine Schicht + prompt: | + Die tiefe Schicht ist eine Kopie des Körpers mit ReaEQ-Tiefpass. Jede klingt allein voll. Spielen beide zusammen, fehlt das Gewicht fast ganz. Welche Zeile erklärt das? + lang: text + code: | + Ordner schlag, Region schlag_01 + anschlag a2 Versatz 0 ms Polarität normal + koerper k1 Versatz 0 ms Polarität normal + tief k1 Versatz 0 ms Polarität invertiert + Fader anschlag −4 dB, koerper 0 dB, tief 0 dB + lines: [4] + explanation: | + „tief“ und „koerper“ stammen aus derselben Aufnahme und liegen ohne Versatz übereinander. Mit gedrehter Polarität ist „tief“ im Bassbereich das Spiegelbild des Körpers, und beide löschen sich dort aus, wo sie gleich sind. Die Fader-Werte sind kein Fehler, nur Platzhalter. Polarität zurückdrehen, und die Summe hat ihr Gewicht. + resources: + - title: REAPER User Guide 7.81 · Media Item Properties, invert phase (S. 135) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=135 + + - id: ab-label-vertauscht + type: bug + title: Blind fast immer daneben + prompt: | + Ohne `blind` hörst du beim Umschalten mit Tab klar, welche Fassung läuft. Im Blindtest liegst du aber bei 1 von 10, obwohl du dir sicher warst. Finde die Zeile und korrigiere sie. + lang: gdscript + code: | + func play_hit() -> void: + if blind: + _use_b = randi() % 2 == 1 + stream = stream_b if _use_b else stream_a + _played.append("A" if _use_b else "B") + play() + lines: [5] + fix: + - line: 5 + answers: + - _played.append("B" if _use_b else "A") + - _played.append("A" if not _use_b else "B") + explanation: | + Die Zeile schreibt „A“, wenn B läuft, und umgekehrt. Die Auflösung nach F2 ist also das Gegenteil des Gespielten. 1 von 10 ist beim Raten genauso unwahrscheinlich wie 9 von 10; ein Ergebnis weit unter dem Zufall ist deshalb auch ein Signal, meist für einen Fehler im Aufbau. Prüfe ein Testwerkzeug, bevor du ihm glaubst: zwei klar verschiedene Prüfklänge, drei Treffer mit Tab, Auflösung vergleichen. + + - id: randomizer-folgen + type: explain + title: Zwei echte Folgen lesen + prompt: | + Diese Folgen kommen aus der Messung in Godot 4.7.2, jeweils die ersten 20 von 20 000 Wiedergaben mit vier Varianten. Erkläre, was die Modi unterscheidet und was das für einen Klang bedeutet, der 30-mal pro Minute spielt. + lang: text + code: | + PLAYBACK_RANDOM + 2 1 4 1 1 2 3 3 2 4 1 1 3 4 4 4 3 2 4 3 + PLAYBACK_RANDOM_NO_REPEATS + 1 3 2 4 2 1 3 1 2 1 2 3 2 4 3 2 3 2 3 2 + points: + - RANDOM wiederholt direkt (1 1, 3 3, sogar 4 4 4), im Mittel mit 1/n. + - NO_REPEATS wiederholt nie direkt, springt aber oft zur vorletzten Variante zurück (2 1 2 1 2, 3 2 3 2), mit 1/(n − 1). + - Bei 30 Wiedergaben pro Minute heißt das etwa 7 direkte Wiederholungen oder etwa 9 A-B-A-Folgen pro Minute. + - Mehr Varianten senken beides; mit nur zwei Varianten wird NO_REPEATS zu einem festen A B A B. + explanation: | + Im oberen Modus wählt der Randomizer jedes Mal frei, deshalb tauchen Dopplungen und sogar Dreier auf, etwa jede vierte Wiedergabe. Im unteren Modus ist die direkte Wiederholung ausgeschlossen, dafür pendelt die Folge oft zwischen zwei Varianten. Beides kann bei einem häufigen Klang auffallen, auf verschiedene Weise. Das wirksamste Mittel sind mehr Varianten. Welcher Modus besser klingt, entscheidet dein Ohr im Spiel, nicht die Statistik. + resources: + - title: Godot Docs · AudioStreamRandomizer, PlaybackMode + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html#enum-audiostreamrandomizer-playbackmode + + - id: ab-test-bauen + type: practice + title: Den A/B-Test in die Testszene bauen + prompt: | + Bau den A/B-Test in die Testszene aus [Testszene: Angriff mit Frame-Daten](/combat/testszene-angriff): ein `AudioStreamPlayer` namens `HitSound` unter dem Dummy mit `ab_hit_sound.gd`, zwei Prüfklänge als A und B, der Aufruf in `take_hit()`. Prüfe das Werkzeug, bevor du es für deine Klänge nutzt. + lang: gdscript + solution: | + # Szenenbaum (Ausschnitt) + # Dummy (CharacterBody2D) enemy.gd + # ├── … + # └── HitSound (AudioStreamPlayer) ab_hit_sound.gd + # stream_a: pruef_hoch.wav, stream_b: pruef_tief.wav + # bus: SFX (Platzhalter) + + # enemy.gd, in take_hit(), direkt nach HitLog.add(...) + if has_node(^"HitSound"): + $HitSound.play_hit() + + # Prüfung des Werkzeugs: + # 1. blind aus: drei Treffer, Tab, drei Treffer. + # Hörbar erst A, dann B; F2 gibt "A A A B B B" aus. + # 2. blind an: fünf Treffer, Tipp notieren, F2. + # Mit zwei klar verschiedenen Prüfklängen stimmt jeder Tipp. + # 3. HitSound testweise entfernen: Treffer laufen weiter, + # keine Fehlermeldung. + checklist: + - "`HitSound` liegt unter dem Dummy und hat `ab_hit_sound.gd`; entfernst du `HitSound` testweise, läuft der Dummy ohne Fehlermeldung weiter." + - Der Ton kommt im Frame der Treffermeldung, also direkt nach der Zeile mit `HitLog.add` in `take_hit()`. + - Mit `blind` aus gibt F2 nach drei Treffern, Tab und drei Treffern genau `A A A B B B` aus, und das Spiel läuft dabei weiter. + - Mit `blind` an und zwei klar verschiedenen Prüfklängen stimmen alle deine notierten Tipps mit der Ausgabe von F2 überein. + explanation: | + Das Werkzeug ist Technik und darf deshalb eine Musterlösung haben. Die Prüfung mit zwei klar verschiedenen Prüfklängen ist der wichtigste Schritt: Erst wenn das Werkzeug bei einem offensichtlichen Unterschied stimmt, kannst du ihm bei einem feinen trauen. `has_node()` hält das Skript des Gegners für Instanzen ohne `HitSound` lauffähig. + resources: + - title: Godot Docs · Node._unhandled_key_input + url: https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-private-method-unhandled-key-input + + - id: eigene-varianten + type: practice + title: Varianten für deine Aktion + prompt: | + Wähl eine Aktion aus deinen Animationen mit markierten Ton-Bildern. Bau dafür einen Klang aus Schichten in einem Folder-Track, leg Varianten an, richte die Transiente auf das Kontaktbild aus und teste zwei Fassungen blind im Spiel. Schreib das A/B-Protokoll. Welche Schichten, wie viele Varianten und welcher Klang, entscheidest du. + lang: prose + checklist: + - Du hast notiert, welche Aktion es ist und in welchem Bild der Kontakt liegt, mit Umrechnung in Frames. + - Jede Variante ist eine eigene Region mit einem Namen nach deinem Schema. + - Du hast die Summe gegen jede Schicht allein gehört und notiert, ob du Versatz oder Polarität geändert hast. + - Die Zahl der Varianten ist aus der Häufigkeit in deiner Cue-Liste begründet, und der Modus des Randomizers steht in deiner Notiz. + - Das A/B-Protokoll enthält zehn Tipps, die Auflösung von F2 und deinen Eindruck, notiert vor F2. + - Du hast eine Entscheidung festgehalten und sie mit dem Ergebnis des Blindtests begründet. + explanation: | + Die Checkliste prüft, ob deine Entscheidungen nachvollziehbar sind, nicht ob sie gefallen. Die Umrechnung in Frames zeigt, dass der Klang an der richtigen Stelle sitzt. Die Begründung der Variantenzahl verbindet den Klang mit dem Spiel. Und das Protokoll trennt, was du erwartet hast, von dem, was du im Spiel tatsächlich unterscheiden konntest. + resources: + - title: Godot Docs · AudioStreamRandomizer + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html diff --git a/content/sound/modules/loops-stems.yaml b/content/sound/modules/loops-stems.yaml new file mode 100644 index 0000000..377318f --- /dev/null +++ b/content/sound/modules/loops-stems.yaml @@ -0,0 +1,568 @@ +id: loops-stems +title: "Eigenes Stück aufbereiten: Loops, Hallfahnen, Stems, Übergangsstücke" +level: 2 +minutes: 150 +summary: Wie du ein eigenes Stück in REAPER so renderst, dass Godot es nahtlos schleift und schaltet – Raster und Länge in Samples, Prüfloop aus Metronom-Klicks, Hallfahne im 2nd pass render, gleich lange Stems, Übergangsstücke und Stinger als eigene Dateien und Import-Werte, die zum Export passen. +place: pc +goals: + - Du legst in REAPER Tempo, Taktart und Regionen auf Taktgrenzen fest und rechnest die Länge eines Loops in Samples vorher aus. + - Du prüfst Loop-Länge und Naht mit einem Prüfloop aus Metronom-Klicks, in REAPER und in Godot. + - Du bringst die Hallfahne mit dem 2nd pass render an den Anfang des Loops und erklärst mit Messungen aus Godot 4.7.2, warum eine Fahne hinter dem Loop-Ende nicht hilft. + - Du renderst Stems mit gleichem Start, gleicher Länge und einer Normalisierung, die ihr Verhältnis hält, sowie Übergangsstücke und Stinger als eigene Dateien. + - Du trägst `loop`, `loop_offset`, `bpm`, `beat_count` und `bar_beats` beim Ogg-Import so ein, dass sie zum Export passen. +resources: + - title: REAPER User Guide 7.81 · Kapitel 21, Rendering (PDF) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=413 + - title: Godot Docs · Importing audio samples, Import options (Ogg Vorbis and MP3) + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#import-options-ogg-vorbis-and-mp3 + - title: Godot Docs · AudioStreamOggVorbis · beat_count + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamoggvorbis.html#class-audiostreamoggvorbis-property-beat-count + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html + - title: Winifred Phillips · Pure Vertical Layering (Game Developer, 2021) + url: https://www.gamedeveloper.com/game-platforms/pure-vertical-layering-for-game-music-composers-from-spyder-to-sackboy-gdc-2021- +lesson: | + Du renderst dein Stück als Datei, ziehst sie nach Godot und schaltest Loop an. Am Loop-Punkt reißt der Hall ab, die Eins kommt einen Schlag zu spät, und die zweite Schicht läuft nach drei Durchläufen hörbar neben der ersten. In REAPER klang alles richtig. + + Rendern, Stems und Hallfahnen kennst du aus der Produktion. Im Spiel kommt hinzu: Die Datei muss so lang sein wie ihr Raster, auf das Sample genau. Die Hallfahne gehört an den Anfang statt ans Ende. Alle Schichten müssen gleich lang sein und gleich anfangen. Und die Engine braucht Tempo und Schläge als Zahlen, sonst kann sie nicht im Takt wechseln. In diesem Modul bereitest du ein eigenes Stück für mindestens zwei Zustände so auf. Was das Stück enthält, welche Zustände es gibt und wie es klingt, entscheidest du; den Plan dafür hast du in [Musik für Zustände](/sound/musik-zustaende) gezeichnet. Tempo, Takte und Dateinamen hier sind Platzhalter. Jeder Schritt geht mit REAPER-Bordmitteln. + + > [!NOTE] Wie hier gemessen wurde + > Alle Godot-Ergebnisse in diesem Modul stammen aus Godot 4.7.2, headless, aufgenommen mit `AudioEffectRecord` am Master-Bus. Die Messdateien sind Prüfloops aus Klicks (1500 Hz auf Takt 1, sonst 750 Hz), mit ffmpeg als Ogg Vorbis kodiert, nicht mit REAPER. Deine eigenen Exporte prüfst du deshalb selbst mit einem Prüfloop. + + ## Das Raster: Tempo, Taktart und Länge in Samples + + Leg Tempo und Taktart in den Project Settings fest, bevor du schneidest (`Alt+Enter`, [User Guide S. 185](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=185)). Dort steht auch die Abtastrate des Projekts (S. 32). Mit Snap auf das Raster (`Alt+S`, S. 141) setzt du für jeden Teil des Stücks eine Zeitauswahl von Taktgrenze zu Taktgrenze und machst mit `Shift+R` eine Region daraus (S. 179). Jede Region wird später eine Datei. + + Wie lang die Datei sein muss, rechnest du vorher aus: Takte mal Schläge pro Takt mal 60 durch Tempo ergibt Sekunden, mal Abtastrate ergibt Samples. + + ```python run + RATE = 48000 # Abtastrate der Datei (Platzhalter) + SCHLAEGE = 4 # pro Takt (Platzhalter) + TAKTE = 8 # Länge des Loops (Platzhalter) + + for bpm in [120, 130, 95]: + sekunden = TAKTE * SCHLAEGE * 60 / bpm + samples = sekunden * RATE + print(f"{bpm} BPM: {sekunden:.4f} s = {samples:.2f} Samples") + ``` + + Nicht jedes Tempo ergibt ganze Samples. Bei 130 BPM sind acht Takte 708923,08 Samples lang; eine Datei kann nur 708923 haben, der Rest fällt weg. Diese Rundung hörst du nicht. Ein Fehler von einem Schlag dagegen sind bei 120 BPM 24000 Samples, und den hörst du sofort. Vergleiche deine Zahl mit der Länge, die REAPER zeigt: in den Item-Eigenschaften (`F2`) auch in Samples (S. 134) und im Fenster nach einem Dry Run mit der Option, die Dateilänge in Samples einzublenden ([S. 416](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=416)). + + Godot mischt standardmäßig mit 44,1 kHz (`audio/driver/mix_rate` ist 44100, gemessen) und rechnet Dateien mit anderer Rate um. Die Loops der Messdateien mit 48 kHz liefen trotzdem genau im Raster. Welche Rate du exportierst, entscheidest du in [Lautheit, Export, Import](/sound/export-im-spiel); für die Rechnung oben zählt die Rate der Datei. + + ## Der Prüfloop: Metronom-Klicks als Messsignal + + Bevor deine Musik ins Spiel geht, schickst du einen **Prüfloop** voraus: ein technisches Signal mit derselben Länge und demselben Raster, an dem du Fehler hörst, statt sie zu vermuten. REAPER liefert ihn mit. `Insert → Click source` legt ein Item an, das den Metronom-Klick als Audio spielt; Tempo und eigene Klickdateien stellst du in seinen Source Properties ein ([S. 377](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=377)). Im Metronom spielt Klang A den ersten Klick jedes Takts und Klang B die übrigen (S. 62). Stell es so ein, dass die Eins hoch und die übrigen Schläge tief klingen, und hör nach dem Rendern nach, ob die gerenderte Datei das auch tut. + + Leg den Click-Source-Track über dieselben Regionen wie die Musik und render ihn als eigene Datei. In Godot hörst du dann, ob nach jedem Durchlauf die Eins wiederkommt. So klangen zwei Prüfloops in Godot 4.7.2, einer mit acht Schlägen und einer mit einem Schlag zu viel (Zeiten in Sekunden, H = hoher Klick, T = tiefer Klick, gekürzt und umbrochen): + + ```text + loop8.ogg, 8 Schläge, Loop an + 0.000H 0.500T 1.000T 1.500T + 2.000H 2.500T 3.000T 3.500T + 4.000H 4.500T 5.000T 5.500T + + loop9.ogg, 9 Schläge, Loop an + 0.000H 0.500T 1.000T 1.500T + 2.000H 2.500T 3.000T 3.500T + 4.000H 4.500H 5.000T 5.500T + ``` + + Ein Schlag zu viel klingt als zwei hohe Klicks hintereinander. Der neunte Schlag ist die Eins des dritten Takts, danach springt der Loop zurück auf die Eins des ersten. In einer Musikdatei hörst du an derselben Stelle ein Stolpern, das man leicht für ein Timing-Problem der Aufnahme hält. + + ## Hallfahnen: was über das Loop-Ende hinausklingt + + Hall und Delay vom letzten Takt klingen über das Ende der Region hinaus. Schneidest du sie ab, ist der Anfang jeder Wiederholung trocken, während das Ende noch hallt: Der Loop-Punkt springt hörbar. Hängst du die Fahne hinten an die Datei, hilft das im Spiel nicht. Zwei Messungen mit derselben Datei, acht Schläge Klicks und danach eine Sekunde 440-Hz-Ton als Ersatz für die Fahne: + + ```text + loop8_tail.ogg: 8 Schläge + 1 s Ton + + bpm 120, beat_count 8: + Eins bei 0, 2, 4, 6, 8, 10 s + Ton: nie hörbar + (dieselbe Datei ohne Klick auf + der Eins: nach 4 s rund 5 ms) + + bpm 0, beat_count 0: + Eins bei 0, 2, 5, 7, 10 s + Ton: 4,0–5,0 s und 9,0–10,0 s + ``` + + Mit `bpm` und `beat_count` springt Godot nach `beat_count` Schlägen zurück; die Klassenreferenz sagt dazu, die Datei dürfe länger sein, `beat_count` lege das Ende fürs Schleifen fest ([beat_count](https://docs.godotengine.org/en/stable/classes/class_audiostreamoggvorbis.html#class-audiostreamoggvorbis-property-beat-count)). Was hinter diesem Ende steht, blendet Godot aus. In der Messung war der Ton nach dem Sprung nicht nachzuweisen; in einer Fassung derselben Datei ohne Klick auf der Eins klang er noch rund 5 ms. Die Fahne hörst du also nicht über dem Anfang. Ohne `beat_count` spielt Godot die ganze Datei, und die Fahne verlängert den Loop: Die Eins rutscht nach dem ersten Durchlauf um eine Sekunde. Bei Übergängen zwischen Clips gilt dasselbe Ende: „Clip-Ende“ heißt `beat_count` Schläge, ohne `beat_count` die Dateilänge. Ob ein Loop am Wechselpunkt abbricht oder ausblendet, legt der Fade-Modus fest; beides misst [Musik in Godot](/sound/godot-musik). + + Die Fahne muss deshalb in die Datei, an den Anfang des Loops. Dafür hat REAPER den **2nd pass render**. Laut Guide spielt REAPER den Bereich dabei einmal ab, bevor es rendert, damit Effektfahnen wie Hall angewendet werden ([S. 415](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415), für Stem-Aktionen S. 426). Für einen Loop heißt das: Wenn der gerenderte Durchlauf beginnt, klingt im Hall noch das Ende des ersten Durchlaufs. Die Region selbst renderst du ohne angehängte Fahne, also mit *Tail* aus. Wo die Option sitzt und für welche Grenzen sie gilt, steht in [REAPER als Werkbank](/sound/reaper-werkbank) (Guide S. 413 und 437). + + Ohne die Option geht es von Hand: Das Material zweimal hintereinander legen, mit Fahne rendern und aus dem Ergebnis den zweiten Durchlauf ausschneiden, auf die Taktgrenzen genau. Das ist dasselbe Prinzip. + + Mit einem Intro in derselben Datei hilft der 2nd pass nicht. Er gilt für den ganzen Render-Bereich, also landet die Fahne vom Loop-Ende über dem Intro statt am Loop-Anfang. Render Intro und Loop dann als zwei Regionen, den Loop im 2nd pass, und verbinde sie in Godot, etwa mit Auto-Advance in `AudioStreamInteractive` wie beim Stinger in [Musik in Godot](/sound/godot-musik). Oder leg die Fahne von Hand unter den Loop-Anfang. + + Prüf das Ergebnis, bevor es nach Godot geht. Zieh die Datei in einen leeren Track und verlängere das Item über mehrere Durchläufe. REAPER-Items sind standardmäßig Loops, und die Kerben im Item zeigen die Loop-Punkte (S. 175). Hörst du am Loop-Punkt einen Klick, liegt dort ein Sprung in der Wellenform. Schneidest du selbst, hilft das Trennen an Nulldurchgängen; der Guide nennt die Option im Dialog Auto trim/split ausdrücklich gegen Klicks (S. 128). + + > [!MERKE] + > Eine Loop-Fahne gehört an den Anfang des Loops, gerendert im 2nd pass; ein Intro bekommt dafür eine eigene Region. Eine Stinger-Fahne darf ausklingen, denn ein Stinger wiederholt sich nicht. + + ## Stems für vertikale Schichten + + Aus der Produktion kennst du Stems als Teile eines Mixes, die am Ende alle zusammen klingen. Winifred Phillips betont, dass Schichten im Spiel anders gedacht sind: „separate audio files that are not always meant to play simultaneously“ ([Pure Vertical Layering](https://www.gamedeveloper.com/game-platforms/pure-vertical-layering-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). Für die Dateien heißt das: + + - **Gleicher Start.** Jeder Stem beginnt auf derselben Eins, auch wenn er dort noch schweigt. + - **Gleiche Länge,** auf das Sample. Das gilt auch für einen Stem, der nur im letzten Takt spielt. + - **Gleiche Fahnenbehandlung.** Jeder Stem wird im 2nd pass gerendert, sonst ist der Stem ohne Fahne am Loop-Punkt trocken, sobald er allein läuft. + - **Gemeinsame Pegel.** Normalisieren nur so, dass das Verhältnis der Stems bleibt. + - **Jeder Stem trägt allein** oder in jeder Kombination, die dein Diagramm erzeugen kann. + + Welche Quelle im Render-Dialog was liefert, steht im Guide ([S. 417](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=417)). „Stems (selected tracks)“ schreibt für jeden ausgewählten Track eine Datei, nach Track-FX, Fader und Pan, aber ohne die FX des Master-Tracks. „Selected tracks via master“ schickt jeden Track einzeln durch die Master-FX. Liegt auf dem Master ein Kompressor oder Limiter, reagiert er dann auf jeden Stem allein anders als auf den ganzen Mix. Beide Wege haben einen Preis. Welchen du nimmst, entscheidest du, und den Unterschied hörst du, wenn du die Stems zusammen gegen den Master-Mix abspielst. Mit „Add rendered items to new tracks in project“ landen sie dafür direkt im Projekt (S. 415). + + > [!WARNING] Getrennt normalisiert + > Die Postprocess-Optionen des Render-Dialogs normalisieren „each file separately, to loudest file, or as if files play together“ (S. 414). Getrennt normalisiert, zieht jeder Stem auf denselben Zielwert, und die Balance deines Arrangements ist weg. „to loudest file“ und „as if files play together“ halten das Verhältnis der Dateien; für Items beschreibt der Guide das beim Normalisieren auf S. 141. Für Stems kommt nur eine dieser beiden Optionen in Frage oder gar keine Normalisierung. + + Mehrere Teile mit mehreren Stems renderst du über die **Region Render Matrix** (`File → Region Render Matrix`): Spalten sind Regionen, Zeilen Tracks, und jedes Häkchen wird eine Datei ([S. 419](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=419)). Im Render-Dialog wählst du dann die Quelle „Region render matrix“ mit den Grenzen „All project regions“. Wildcards wie `$project`, `$region` und `$track` bauen den Dateinamen; der Guide zeigt `$project\$region-$track` als Beispiel (S. 420). Wie Matrix und Wildcards im Einzelnen funktionieren, steht in [REAPER als Werkbank](/sound/reaper-werkbank). + + Was passiert, wenn ein Stem nicht gleich lang ist, zeigt eine Messung mit `AudioStreamSynchronized`, Godots Stream für synchrone Schichten. Stem 0 ist der Prüfloop mit acht Schlägen, Stem 1 eine Datei mit einer Markierung auf Schlag 1 und neun Schlägen Länge: + + ```text + Stem 1 ein Schlag zu lang, Loop an + Markierung bei 0.00, 4.50, 9.00 s + + beide mit bpm 120, beat_count 8 + Markierung bei 0, 4, 8, 12 s + ``` + + Jeder Stem schleift für sich. Ist einer länger, wandert er pro Durchlauf um die Differenz weiter. Mit `beat_count` springen beide nach acht Schlägen zurück, der überzählige Schlag fällt weg. Laut Godot-Doku braucht Ogg Vorbis beim Abspielen deutlich mehr Rechenzeit als WAV ([Supported audio formats](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#supported-audio-formats)); jeder Stem ist ein eigener Ogg-Stream, also zählt die Zahl der Schichten. + + ## Übergangsstücke und Stinger als eigene Dateien + + Ein **Übergangsstück** führt von einem Teil zum anderen und hat eine eigene Region. Mach es so lang, wie dein Plan es vorsieht, in ganzen Schlägen oder Takten, und gib ihm dieselben Import-Werte wie den Loops. Sonst bricht das Raster nach dem Übergang. Wie lange Godot es spielt (`beat_count` Schläge) und wie das Ziel danach einsetzt, misst [Musik in Godot](/sound/godot-musik). + + Ein **Stinger** ist ein kurzes Musikstück, das ein Ereignis markiert. Meist liegt er über der laufenden Musik, er kann sie auch kurz ersetzen. Als Datei ist er ein One-Shot ohne Loop. Er beginnt auf seiner ersten Transiente, ohne Stille davor, denn jede Millisekunde Stille ist Verzögerung im Spiel ([Audio zuordnen](/godot/audio)). Seine Fahne klingt aus. Render ihn mit *Tail*, nicht im 2nd pass. + + Gib jeder Datei einen Namen, aus dem Stück, Teil und Schicht hervorgehen. Ein Platzhalter-Schema, dein eigenes legst du selbst fest: + + ```text + mus___.ogg + mus__-.ogg + mus__stinger_.ogg + mus__pruefloop.ogg + ``` + + > [!PRAXIS] Komponieren und Einbauen + > Komposition und Einbau liegen oft bei verschiedenen Leuten. Für Sackboy: A Big Adventure schrieb Winifred Phillips Musik, darunter ihre Fassung von „Material Girl“. Das Audio-Team des Spiels teilte das Stück beim Einbau nach der Songform in Abschnitte ([Horizontal Resequencing and Dynamic Transitions](https://www.gamedeveloper.com/audio/horizontal-resequencing-and-dynamic-transitions-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). Bill Elm beschreibt die Arbeit an Red Dead Redemption so: „with the video game, we're working in five-minute loops“ ([The Guardian, 2010](https://www.theguardian.com/technology/gamesblog/2010/may/26/red-dead-redemption-soundtrack)). Wer Dateien liefert, die jemand anderes einbaut, legt eine **Lieferliste** bei: zu jeder Datei eine Zeile mit Teil, Schicht, Länge in Samples, Tempo, Schlägen, Taktart und Loop ja oder nein. Mit dieser Liste kann ein Audio-Programmierer die Import-Werte eintragen, ohne dich zu fragen, und du prüfst nach dem Einbau Zeile für Zeile. + + ## Ogg Vorbis exportieren und in Godot eintragen + + Für Musik nimmst du in Godot meist Ogg Vorbis, für kurze Effekte WAV ([Audio zuordnen](/godot/audio)). Im Render-Dialog ist OGG Vorbis eines der Ausgabeformate, die Formatoptionen hängen vom Format ab (S. 415). Die Qualität ist ein Platzhalter: Render einmal als WAV und einmal als Ogg und vergleiche beide mit Kopfhörern, bevor du dich festlegst. + + Nach dem Import stehen Tempo und Schläge in Godot auf 0, auch wenn die Datei ein BPM-Tag trägt: Gemessen landet ein Tag `BPM=120` nur in `tags`, `bpm` bleibt 0. Du trägst die Werte im Import-Dock ein; ein Doppelklick auf die Datei öffnet einen Dialog, in dem du den Loop-Punkt mit Vorhör setzt und Tempo und Schläge einträgst ([Import options](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#import-options-ogg-vorbis-and-mp3)). Die Werte landen in der `.import`-Datei neben der Ogg. Renderst du die Datei unter demselben Namen neu, importiert Godot sie automatisch neu und wendet die gespeicherten Einstellungen wieder an ([Automatic reimport](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport)). Gemessen: Eine Datei mit `beat_count = 12` wurde durch eine kürzere mit acht Schlägen ersetzt, und nach dem Neuimport stand dort weiter `beat_count = 12`. Ändert sich die Länge, änderst du die Werte mit. Die Standardwerte für eine neue Datei, gemessen in 4.7.2: + + | Feld | Standard | Bedeutung | + | --- | --- | --- | + | `loop` | `false` | Datei wiederholen | + | `loop_offset` | `0` | Sprungziel in Sekunden | + | `bpm` | `0` | Tempo | + | `beat_count` | `0` | Länge in Schlägen | + | `bar_beats` | `4` | Schläge pro Takt | + + So sieht der Teil `[params]` aus, nachdem für eine Datei mit einem Takt Intro und zwei Takten Loop bei 120 BPM die Werte eingetragen und neu importiert wurden: + + ```ini title=intro_loop.ogg.import + [params] + + loop=true + loop_offset=2.0 + bpm=120.0 + beat_count=12 + bar_beats=4 + ``` + + `beat_count` zählt ab dem Anfang der Datei, also mit Intro: drei Takte zu je vier Schlägen. `loop_offset` ist das Ende des Intros in Sekunden. Gemessen spielt das Intro einmal, danach springt Godot nach 6 Sekunden immer wieder auf 2 Sekunden zurück. + + Drei weitere Messungen zeigen, wofür die Felder gebraucht werden. Mit `bpm = 0` greift ein Übergang auf den nächsten Takt in `AudioStreamInteractive` sofort statt auf der Eins, ohne Fehlermeldung. Mit `bpm = 120` und `beat_count = 0` wartet er weiter auf die Eins. Und `bar_beats` legt fest, wo die Eins ist: Eine Anfrage bei 0,6 Sekunden wechselte mit 4 bei 2,0 Sekunden, mit 3 bei 1,5 Sekunden. Die Tutorial-Seite der Doku nennt Bar Beats „the number of bars within a single beat“ und die drei Takt-Felder „currently unused“; die Klassenreferenz („the number of beats within a single bar“) und die Messungen zeigen, dass beides nicht mehr stimmt. `AudioStreamWAV` hat Loop-Punkte in Samples, aber gar keine Felder für Tempo und Schläge (gemessen: `loop_mode`, `loop_begin`, `loop_end`, kein `bpm`). Wie du die Clips zu einer Übergangstabelle verbindest, steht in [Musik in Godot](/sound/godot-musik). + + > [!INTERVIEW] + > „Wie lieferst du Musik für ein adaptives System?“ Gute Antwort, etwa als Composer oder Technical Sound Designer: Ich lege Tempo und Taktart vorher fest und rendere jeden Teil als Region auf Taktgrenzen, Länge in Samples vorher gerechnet. Loops rendere ich im 2nd pass, damit die Fahne am Anfang liegt, Stinger mit ausklingender Fahne. Stems haben gleichen Start und gleiche Länge und werden nur so normalisiert, dass ihr Verhältnis bleibt. Zu jeder Datei gibt es eine Zeile mit Tempo, Schlägen, Taktart und Loop, denn nach dem Import stehen sie in Godot auf 0. Bevor die Musik eingebaut wird, läuft ein Prüfloop aus Metronom-Klicks mit denselben Werten im Spiel. + + ## Häufige Fehler + + - **Region einen Schlag zu lang.** Im Prüfloop zwei hohe Klicks hintereinander, in der Musik ein Stolpern am Loop-Punkt. Region auf die Taktgrenze setzen, Länge in Samples vergleichen. + - **Fahne hinten angehängt.** Mit `beat_count` blendet Godot sie in rund 5 ms aus, ohne `beat_count` verlängert sie den Loop. Fahne im 2nd pass an den Anfang. + - **Stems getrennt normalisiert.** Die Balance im Spiel stimmt nicht mehr mit dem Projekt. „to loudest file“, „as if files play together“ oder gar nicht normalisieren. + - **Ein Stem kürzer oder länger.** Er wandert pro Durchlauf weiter. Alle Stems einer Region gemeinsam über die Region Render Matrix rendern. + - **`bpm` nicht eingetragen.** Wechsel auf den nächsten Takt greifen sofort. Import-Werte aus der Lieferliste übernehmen. + - **`beat_count` ohne Intro gezählt.** Godot zählt ab Dateianfang; der Loop endet zu früh. + - **Neu gerendert, Länge geändert, Werte alt.** Der Neuimport behält `bpm` und `beat_count`. Lieferliste und Import-Werte zusammen ändern. + - **Stille vor dem Stinger.** Er kommt später als das Ereignis. Auf die erste Transiente schneiden. + + ## Zusammenfassung + + - Tempo und Taktart stehen fest, bevor du schneidest. Jeder Teil ist eine Region auf Taktgrenzen; ihre Länge in Samples rechnest du vorher aus. + - Ein Prüfloop aus Metronom-Klicks (`Insert → Click source`, Takt 1 hoch) zeigt Längenfehler als zwei hohe Klicks hintereinander. + - Loop-Fahnen gehören an den Anfang des Loops, im 2nd pass render; ein Intro bekommt eine eigene Region. Eine Fahne hinter `beat_count` blendet Godot in rund 5 ms aus. + - Stems haben gleichen Start, gleiche Länge, gleiche Fahnenbehandlung und gemeinsame Pegel; ein längerer Stem wandert pro Durchlauf weiter. + - Übergangsstücke haben ganze Schläge und dieselben Import-Werte, Stinger beginnen auf der Transiente und klingen aus. + - Nach dem Import stehen Tempo und Schläge auf 0. `bpm`, `beat_count` (ab Dateianfang), `bar_beats` und `loop_offset` trägst du beim Import ein; ohne `bpm` greifen Takt-Übergänge sofort. +exercises: + - id: render-quelle + type: choice + title: Welche Quelle für die Stems? + prompt: | + Du willst für jeden ausgewählten Track eine eigene Datei, mit seinen Track-FX und seinem Fader, aber ohne die FX, die auf dem Master-Track liegen. Welche Quelle wählst du im Render-Dialog? + options: + - text: Stems (selected tracks) + correct: true + why: Laut Guide eine Datei je ausgewähltem Track, „post FX, post-fader and post-pan“, ohne den Weg über den Master. + - text: Selected tracks via master + correct: false + why: Damit läuft jeder Track einzeln durch die Master-FX und den Master-Fader. Die sollten hier wegbleiben. + - text: Master mix + correct: false + why: Das ist eine einzige Datei mit allem, was über den Master läuft. + - text: "`File → Consolidate/Export tracks…` statt Render" + correct: false + why: Das exportiert die Tracks ohne Track-FX, Fader und Pan. Der Guide empfiehlt es gerade dann, wenn du die Verarbeitung nicht mitnehmen willst. + explanation: | + Die Quellen unterscheiden sich darin, welche Verarbeitung in der Datei landet. Für Schichten, deren Summe im Spiel ohne Master-Bearbeitung laufen soll, ist „Stems (selected tracks)“ der direkte Weg. Mit „via master“ bekommt jeder Stem die Master-Kette einzeln, und ein Kompressor dort reagiert auf jeden Stem anders als auf den Mix. Beides kann richtig sein; hör die Stems zusammen gegen den Master-Mix, bevor du dich festlegst. + resources: + - title: REAPER User Guide 7.81 · Render to File, Source Options (S. 417) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=417 + + - id: vom-projekt-zum-import + type: order + title: Vom Projekt bis zum Prüfloop im Spiel + prompt: | + Bring die Arbeitsschritte in eine Reihenfolge, in der jeder Schritt auf dem vorigen aufbaut. + lang: prose + lines: | + Tempo und Taktart in den Project Settings festlegen. + Für jeden Teil eine Region auf Taktgrenzen anlegen und ihre Länge in Samples ausrechnen. + Einen Click-Source-Track als Prüfloop über dieselben Regionen legen. + Prüfloop und Stems über die Region Render Matrix im 2nd pass als Ogg rendern. + In Godot bpm, beat_count, bar_beats und loop für jede Datei eintragen. + Erst den Prüfloop im Spiel abhören, dann die Musik. + explanation: | + Das Raster kommt zuerst, weil Regionen, Klicks und Längen davon abhängen. Der Prüfloop entsteht vor dem Rendern, damit er mit denselben Einstellungen gerendert wird wie die Musik. Die Import-Werte gehören zu jeder Datei, auch zum Prüfloop. Im Spiel hörst du zuerst die Klicks: Stimmen sie, liegt ein Problem in der Musik nicht mehr an Länge oder Raster. + resources: + - title: REAPER User Guide 7.81 · Rendering Regions (S. 419) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=419 + + - id: loop-samples + type: output + title: Wie viele Samples hat der Loop? + prompt: | + Ein Loop von acht Takten im 3/4-Takt bei 96 BPM (Platzhalter). Das Programm gibt `beat_count`, die Länge in Sekunden und die Länge in Samples bei 48 und 44,1 kHz aus. Was steht da? + lang: python + code: | + BPM = 96 + SCHLAEGE = 3 + TAKTE = 8 + + beat_count = TAKTE * SCHLAEGE + sekunden = beat_count * 60 / BPM + print(beat_count) + print(sekunden) + for rate in [48000, 44100]: + print(round(sekunden * rate)) + expected: | + 24 + 15.0 + 720000 + 661500 + hints: + - Ein Schlag dauert 60 / 96 = 0,625 s. + explanation: | + 24 Schläge zu je 0,625 s ergeben genau 15 Sekunden, und 15 Sekunden sind bei 48 kHz 720000 Samples, bei 44,1 kHz 661500. Diese Zahl vergleichst du mit der Länge in REAPER, bevor du renderst, und mit dem Ergebnis danach. `beat_count` trägst du so in Godot ein, wie das Programm es ausgibt: Schläge, nicht Takte. + resources: + - title: REAPER User Guide 7.81 · Media Item Properties, Länge in Samples (S. 134) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=134 + + - id: intro-und-loop + type: output + title: Intro, Loop und zwei Zahlen + prompt: | + Eine Datei beginnt mit zwei Takten Intro, danach folgen acht Takte Loop, 4/4 bei 128 BPM (Platzhalter). Godot zählt `beat_count` ab dem Anfang der Datei, `loop_offset` ist in Sekunden. Was gibt das Programm aus? + lang: python + code: | + BPM = 128 + SCHLAEGE = 4 + INTRO_TAKTE = 2 + LOOP_TAKTE = 8 + + schlag_s = 60 / BPM + loop_offset = INTRO_TAKTE * SCHLAEGE * schlag_s + beat_count = (INTRO_TAKTE + LOOP_TAKTE) * SCHLAEGE + print(loop_offset) + print(beat_count) + print(beat_count * schlag_s) + expected: | + 3.75 + 40 + 18.75 + hints: + - Ein Schlag dauert 60 / 128 = 0,46875 s. + explanation: | + Das Intro hat acht Schläge, also 3,75 Sekunden; dorthin springt Godot nach jedem Durchlauf zurück. `beat_count` umfasst Intro und Loop, 40 Schläge, und das Ende der Schleife liegt bei 18,75 Sekunden. Wer nur die 32 Schläge des Loops einträgt, lässt den Loop bei 15 Sekunden enden, acht Schläge zu früh. In der Messung mit einem Takt Intro und zwei Takten Loop sprang Godot mit `beat_count = 12` nach 6 Sekunden auf 2 Sekunden zurück. + resources: + - title: Godot Docs · AudioStreamOggVorbis · loop_offset + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamoggvorbis.html#class-audiostreamoggvorbis-property-loop-offset + + - id: pruefloop-hoeren + type: output + title: Was hörst du im Prüfloop? + prompt: | + Ein Prüfloop im 4/4-Takt: hoher Klick (H) auf jeder Eins, sonst tiefer Klick (T). Die Region war einen Schlag zu lang, die Datei hat also neun Schläge. In Godot ist `loop` an, `bpm` und `beat_count` stehen auf 0. Welche 14 Klicks hörst du ab dem Start? Schreib sie in eine Zeile, durch Leerzeichen getrennt. + lang: text + code: | + Schlag in der Datei: 1 2 3 4 5 6 7 8 9 + Klick: H T T T H T T T H + expected: | + H T T T H T T T H H T T T H + accept: + - HTTTHTTTHHTTTH + hints: + - Nach dem neunten Schlag beginnt die Datei wieder bei Schlag 1. + explanation: | + Die Datei spielt ihre neun Schläge, H T T T H T T T H, und beginnt dann von vorn mit H. So entstehen zwei hohe Klicks hintereinander. Die Messung in Godot 4.7.2 zeigte sie bei 4,0 und 4,5 Sekunden. Danach liegt jede Eins einen Schlag neben dem Raster, in dem der Spieler das Stück gehört hat. Mit `beat_count = 8` spränge Godot nach dem achten Schlag zurück, und der überzählige Schlag fiele weg; die richtige Lösung bleibt eine Region auf der Taktgrenze. + resources: + - title: REAPER User Guide 7.81 · Automating the Metronome, Click Source (S. 377) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=377 + + - id: import-werte + type: gap + title: Die Werte für den Import + prompt: | + Eine Datei: ein Takt Intro, danach vier Takte Loop, 4/4 bei 100 BPM (Platzhalter). Die Datei endet auf der Taktgrenze. Ergänze die Import-Werte. + lang: ini + code: | + [params] + + loop=⟦true⟧ + loop_offset=⟦2.4⟧ + bpm=⟦100.0⟧ + beat_count=⟦20⟧ + bar_beats=⟦4⟧ + gaps: + - label: Wiederholen + hint: Der Loop soll laufen. + - label: Sprungziel + hint: Ein Takt bei 100 BPM, in Sekunden. + accept: ["2.40"] + - label: Tempo + hint: Das Projekttempo. + accept: ["100"] + - label: Schläge + hint: Godot zählt ab Dateianfang, Intro eingeschlossen. + - label: Taktart + hint: Schläge pro Takt. + explanation: | + Ein Schlag dauert bei 100 BPM 0,6 Sekunden, ein Takt 2,4. Dorthin springt Godot zurück. `beat_count` sind fünf Takte zu vier Schlägen, mit Intro, also 20. `bar_beats` ist 4 und muss gesetzt bleiben, denn Wechsel auf den nächsten Takt richten sich danach. Weil die Datei auf der Taktgrenze endet, würde der Loop hier auch ohne `beat_count` richtig springen; für Übergänge in `AudioStreamInteractive` brauchst du die Werte trotzdem. + resources: + - title: Godot Docs · Import options (Ogg Vorbis and MP3) + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#import-options-ogg-vorbis-and-mp3 + + - id: render-blatt + type: gap + title: Ein Render-Blatt für Stems + prompt: | + Ergänze das Render-Blatt für Loops mit mehreren Stems pro Teil. Die Dateinamen sollen aus Regionsname und Trackname bestehen. + lang: text + code: | + Quelle: Region render matrix + Grenzen: All project regions + Dateiname: mus_⟦$region⟧_⟦$track⟧ + 2nd pass: ⟦an⟧ + Normalisieren: ⟦gemeinsam⟧ + Format: OGG Vorbis + gaps: + - label: Regionsname + hint: Wildcard mit Dollarzeichen. + - label: Trackname + hint: Wildcard mit Dollarzeichen. + - label: 2nd pass render + hint: Damit die Fahne am Anfang liegt. + accept: ["An", "ein", "Ein", "ja", "Ja", "on", "On"] + - label: Normalisieren + hint: So, dass das Verhältnis der Stems bleibt. + accept: ["Gemeinsam", "to loudest file", "To loudest file", "as if files play together", "As if files play together", "aus", "Aus", "nein", "Nein", "keine", "Keine"] + explanation: | + Mit `$region` und `$track` heißt jede Datei nach ihrem Teil und ihrer Schicht. Der 2nd pass legt die Fahne an den Anfang jeder Region. Normalisieren darfst du nur so, dass das Verhältnis bleibt („to loudest file“ oder „as if files play together“), oder gar nicht, sonst verschiebt sich die Balance. Speichere das Blatt als Preset im Render-Dialog, dann bleiben die Einstellungen bei jedem neuen Render gleich (S. 414). + resources: + - title: REAPER User Guide 7.81 · Region Render Matrix und Wildcards (S. 420) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=420 + + - id: region-zu-lang + type: bug + title: Zwei hohe Klicks hintereinander + prompt: | + Projekt bei 120 BPM im 4/4. Im Prüfloop in Godot hörst du bei einem Teil am Loop-Punkt zwei hohe Klicks im Abstand eines Schlags. Die anderen Teile laufen sauber. Finde die Region, die falsch gesetzt ist, und korrigiere ihr Ende. Positionen in REAPER-Schreibweise Takt.Schlag. + lang: text + code: | + Region teil1: 1.1.00 bis 5.1.00 + Region teil2: 5.1.00 bis 9.1.00 + Region teil3: 9.1.00 bis 11.2.00 + Region uebergang: 11.1.00 bis 12.1.00 + lines: [3] + fix: + - line: 3 + answers: + - "Region teil3: 9.1.00 bis 11.1.00" + hints: + - Eine Region auf Taktgrenzen endet auf Schlag 1. + explanation: | + Die Region teil3 endet auf Schlag 2 von Takt 11 und ist damit zwei Takte und einen Schlag lang. Der neunte Schlag ist die Eins von Takt 11, dann springt der Loop auf die Eins von Takt 9: zwei Einsen hintereinander. Die übrigen Regionen enden auf Schlag 1. Dass die Region uebergang bei 11.1.00 beginnt und mit teil3 überlappt, ist der zweite Hinweis auf dieselbe Ursache. + resources: + - title: REAPER User Guide 7.81 · Creating and Using Regions (S. 179) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=179 + + - id: normalisiert-getrennt + type: bug + title: Die Percussion ist zu laut + prompt: | + Im Projekt sitzt die Percussion leise unter Pad und Bass. Im Spiel, mit allen drei Schichten an und gleichen Lautstärken in Godot, ist sie fast so laut wie das Pad. Finde die Einstellung im Render-Blatt, die das verursacht, und korrigiere sie. + lang: text + code: | + Quelle: Region render matrix + Grenzen: All project regions + Dateiname: mus_$region_$track + 2nd pass: an + Normalisieren: each file separately, -1 dB + Format: OGG Vorbis + lines: [5] + fix: + - line: 5 + answers: + - "Normalisieren: as if files play together, -1 dB" + - "Normalisieren: aus" + - "Normalisieren: Aus" + - "Normalisieren: as if files play together" + - "Normalisieren: to loudest file, -1 dB" + - "Normalisieren: to loudest file" + - "Normalisieren: gemeinsam, -1 dB" + - "Normalisieren: gemeinsam" + - "Normalisieren: Gemeinsam, -1 dB" + - "Normalisieren: Gemeinsam" + explanation: | + Getrennt normalisiert zieht jede Datei für sich auf −1 dB. Eine leise Percussion-Schicht wird dabei stärker angehoben als das Pad, und das Verhältnis aus deinem Mix ist weg. Mit „to loudest file“ oder „as if files play together“ bleibt das Verhältnis, oder du normalisierst gar nicht. Im Spiel stellst du die Lautstärken dann an den Schichten ein, nicht in den Dateien. + resources: + - title: REAPER User Guide 7.81 · Render to File, Postprocess (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: stem-wandert + type: bug + title: Eine Schicht läuft davon + prompt: | + Drei Stems eines Teils laufen in einem `AudioStreamSynchronized`, Loop an, ohne `beat_count`, 120 BPM, 48 kHz. Nach dem ersten Durchlauf liegt eine Schicht einen Schlag hinter den anderen, nach dem zweiten zwei Schläge. Finde den Stem in der Lieferliste, der die Ursache ist. + lang: text + code: | + Datei Samples + mus_a_teil1_pad.ogg 768000 + mus_a_teil1_bass.ogg 768000 + mus_a_teil1_perc.ogg 792000 + lines: [4] + hints: + - Ein Schlag bei 120 BPM dauert 0,5 s. + explanation: | + 768000 Samples sind bei 48 kHz 16 Sekunden, acht Takte bei 120 BPM. Die Percussion-Datei ist 24000 Samples länger, genau ein Schlag. Jeder Stem schleift für sich, also wandert sie pro Durchlauf um einen Schlag weiter, wie in der Messung mit `AudioStreamSynchronized` (Markierung bei 0, 4,5 und 9 s statt 0, 4 und 8 s). `bpm = 120` mit `beat_count = 32` würde alle drei nach 32 Schlägen zurückspringen lassen; besser ist ein neuer Render über die Region, damit alle Stems dieselbe Länge haben. + resources: + - title: Godot Docs · AudioStreamSynchronized + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html + + - id: fahne-gemessen + type: explain + title: Wohin mit der Fahne? + prompt: | + Erkläre an diesen zwei Messungen aus Godot 4.7.2, warum eine Hallfahne hinter dem Loop-Ende nicht hilft und was du stattdessen tust. Die Datei hat acht Schläge Klicks und danach eine Sekunde Ton als Ersatz für eine Fahne. + lang: text + code: | + bpm 120, beat_count 8: + Eins bei 0, 2, 4, 6, 8, 10 s + Ton: nie hörbar + (dieselbe Datei ohne Klick auf + der Eins: nach 4 s rund 5 ms) + + bpm 0, beat_count 0: + Eins bei 0, 2, 5, 7, 10 s + Ton: 4,0–5,0 s und 9,0–10,0 s + points: + - Mit beat_count springt Godot nach acht Schlägen zurück und blendet den Rest der Datei in wenigen Millisekunden aus; die Fahne liegt nicht über dem Anfang. + - Ohne beat_count spielt Godot die ganze Datei; die Fahne verlängert den Loop, und die Eins rutscht aus dem Raster. + - Die Fahne muss deshalb am Anfang der Datei liegen, gerendert im 2nd pass, und die Datei endet auf der Taktgrenze. + - Ob es nahtlos klingt, prüfst du in REAPER mit einem geloopten Item und in Godot mit einem Prüfloop. + explanation: | + Im ersten Lauf kommt die Eins alle zwei Sekunden, der Loop springt also nach acht Schlägen. Der Ton dahinter ist nach dem Sprung nicht zu hören; ohne Klick auf der Eins zeigt die Messung noch rund fünf Millisekunden, dann blendet Godot ihn aus. Eine Fahne an dieser Stelle ginge genauso verloren. Im zweiten Lauf kennt Godot das Raster nicht und spielt bis zum Ende der Datei. Der Ton ist eine ganze Sekunde zu hören, aber die Eins kommt danach erst bei 5 Sekunden statt bei 4, der Loop ist um die Fahne länger geworden. In beiden Fällen ist der Anfang trocken. Deshalb renderst du Loops im 2nd pass, sodass der Hall vom Ende schon am Anfang klingt, und lässt die Datei auf der Taktgrenze enden. + resources: + - title: REAPER User Guide 7.81 · 2nd pass render (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 + - title: Godot Docs · AudioStreamOggVorbis · beat_count + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamoggvorbis.html#class-audiostreamoggvorbis-property-beat-count + + - id: render-vorlage + type: practice + title: Eine Render-Vorlage und eine Lieferliste + prompt: | + Leg in REAPER ein Render-Preset für Musik-Loops an und eine Lieferliste als CSV-Datei. Das Preset rendert über die Region Render Matrix im 2nd pass als Ogg Vorbis, mit Dateinamen aus Region und Track und ohne getrennte Normalisierung. Die Lieferliste hat eine Zeile je Datei mit Teil, Schicht, Länge in Samples, Tempo, Schlägen, Taktart, Loop und Loop-Versatz. Trag die Einstellungen des Presets und die Kopfzeile der Liste hier ein. Werte sind Platzhalter. + lang: text + starter: | + Preset-Name: + Quelle: + Grenzen: + Dateiname: + 2nd pass: + Normalisieren: + Format und Qualität: + + Lieferliste (Kopfzeile und eine Zeile): + solution: | + Preset-Name: musik-loops (Bounds and output + Options and format) + Quelle: Region render matrix + Grenzen: All project regions + Dateiname: mus_$project_$region_$track + 2nd pass: an + Normalisieren: aus (oder to loudest file / as if files play together) + Format und Qualität: OGG Vorbis, Qualität = Platzhalter nach Hörvergleich + + Lieferliste: + datei,teil,schicht,samples,rate,bpm,beat_count,bar_beats,loop,loop_offset + mus_a_teil1_pad.ogg,teil1,pad,768000,48000,120,32,4,true,0 + checklist: + - Das Preset ist im Render-Dialog gespeichert und lässt sich laden. + - Quelle, Grenzen, 2nd pass und Normalisierung stehen so, wie es die Lektion für Loops beschreibt. + - Der Dateiname enthält Wildcards für Region und Track. + - Die Lieferliste ist eine Datei mit Kopfzeile und den Spalten Länge in Samples, Tempo, Schläge, Taktart, Loop und Loop-Versatz. + - Du hast mit einem Testrender geprüft, dass die Dateinamen so herauskommen, wie du sie geplant hast. + explanation: | + Ein Preset nimmt dir das Wiederholen ab und verhindert, dass beim fünften Render eine Option anders steht als beim ersten. Der Render-Dialog speichert Presets für Grenzen und Ausgabe, für Optionen und Format oder für alles (S. 414). Die Lieferliste ist der Vertrag mit dem Einbau: Godot übernimmt Tempo und Schläge nicht aus der Datei, also müssen sie irgendwo stehen. Die Musterlösung zeigt die Form, nicht die Werte. + resources: + - title: REAPER User Guide 7.81 · Render to File, Presets (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: stueck-aufbereiten + type: practice + title: Dein Stück für zwei Zustände + prompt: | + Bereite ein eigenes Stück für mindestens zwei Zustände aus deinem Diagramm auf: Loops für jeden Zustand, mindestens ein Übergangsstück oder ein Stinger, wenn dein Plan es vorsieht, Stems, wenn du vertikal schichtest. Render zuerst den Prüfloop über dieselben Regionen und hör ihn in Godot ab, dann die Musik. Notiere hier Lieferliste, Prüfergebnis und was du geändert hast. Was das Stück enthält und wie es klingt, entscheidest du. + lang: prose + starter: | + Stück, Tempo, Taktart: + + Regionen (Name, Takte, Samples gerechnet / in REAPER): + + Prüfloop in Godot (Was hörst du am Loop-Punkt?): + + Fahne am Anfang hörbar (ja / nein, wie geprüft): + + Stems (Namen, Samples): + + Import-Werte je Datei: + + Geändert nach dem Test: + checklist: + - Für jede Region stehen die gerechnete Länge in Samples und die Länge in REAPER, und sie stimmen überein. + - Der Prüfloop läuft in Godot mit denselben Import-Werten wie die Musik, und nach jedem Durchlauf kommt genau ein hoher Klick auf der Eins. + - Du hast an einem geloopten Item in REAPER oder in Godot geprüft, dass am Loop-Punkt kein Klick und kein trockener Anfang zu hören ist. + - Alle Stems einer Region haben dieselbe Länge in Samples. + - Für jede Datei stehen bpm, beat_count, bar_beats, loop und loop_offset in der Lieferliste und in der .import-Datei. + - REAPER-Projekt und Rohmaterial liegen getrennt von den exportierten Ogg-Dateien. + explanation: | + Die Checkliste prüft das Handwerk, nicht den Klang: Länge, Naht, Synchronität, Werte. Der Prüfloop zuerst trennt zwei Fehlerquellen. Stimmen die Klicks und es stolpert trotzdem, liegt es an der Musik; stolpern schon die Klicks, an Region oder Import. Ob dein Stück in den Zuständen das sagt, was du willst, prüfst du im Spiel; den Einbau mit Übergangstabelle beschreibt [Musik in Godot](/sound/godot-musik), die Werkstatt [ein kleiner Audioabschnitt](/sound/werkstatt-audioabschnitt). + resources: + - title: REAPER User Guide 7.81 · 2nd pass render (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 diff --git a/content/sound/modules/mischung-stille.yaml b/content/sound/modules/mischung-stille.yaml new file mode 100644 index 0000000..f35a287 --- /dev/null +++ b/content/sound/modules/mischung-stille.yaml @@ -0,0 +1,522 @@ +id: mischung-stille +title: "Mischung: Musik, Geräusche und Stille nach Priorität" +level: 2 +minutes: 100 +summary: Wie ein Spiel zur Laufzeit entscheidet, welcher Klang Vorrang hat – Priorität statt Lautstärkekampf, Maskierung in Frequenz und Zeit, Summenpegel und Stimmenbegrenzung, Ducking als Plan, Stille als Kontrast –, an Overwatch, Battlefield und Walter Murch, mit in Godot 4.7.2 gemessenen Summenpegeln. +place: unterwegs +goals: + - Du vergleichst Priorität nach Lautheit (HDR-Audio bei DICE) mit Priorität nach Bedrohung (Overwatch) und schreibst eine eigene Prioritätentabelle mit Begründungen. + - Du erkennst Maskierung in Frequenz und Zeit und nennst Mittel dagegen, die nicht „lauter“ heißen. + - Du rechnest Summenpegel gleichzeitiger Klänge – gleiche Klänge im selben Frame +6 dB, verschiedene etwa +3 dB – und planst Stimmenbegrenzung pro Klang. + - Du planst Busse und Ducking als Tabelle und rechnest Release-Zeiten in Frames um. + - Du setzt Stille als Kontrast ein und planst die Dichte des Mixes über einen Abschnitt passend zum Taktplan. +resources: + - title: Wwise Tour 2016 · Blizzard Overwatch (3 of 7) – A Clear Mix + url: https://www.youtube.com/watch?v=2M5cWHswQsM + - title: Walter Murch · Dense Clarity – Clear Density (Transom) + url: https://transom.org/2005/walter-murch/ + - title: DICE · HDR Audio, Adaptive mixing in Frostbite (Folien) + url: https://www.slideshare.net/slideshow/adaptive-mixing-in-frostbite/3128152 + - title: Godot Docs · AudioEffectCompressor + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html + - title: Handbook for Acoustic Ecology (SFU) · Mask + url: https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Mask.html +lesson: | + Drei Trainingsgegner greifen gleichzeitig an. Musik läuft, dazu das Bett der Halle, Schritte, Schwünge, Treffer. Ein vierter Gegner holt am Bildrand aus, und seine Warnung ist nicht zu hören. Kein einzelner Klang ist zu leise eingestellt. Zusammen verdecken sie ihn. + + Aus der Musik kennst du das Problem und die Werkzeuge: Platz im Spektrum schaffen, komprimieren, automatisieren, bis jede Spur ihren Ort hat. Im Stück gelingt das, weil die Kombination feststeht. Im Spiel entsteht der Mix zur Laufzeit aus Ereignissen, die niemand vorher kennt, und jede Kombination klingt anders. Du mischst also keine Stelle, sondern Regeln: welcher Klang Vorrang hat, wie viele gleichzeitig spielen dürfen, wer wen leiser macht und wo Stille hingehört. + + Dieses Modul plant diese Regeln am Handy, als Prioritätentabelle und Bus-Plan. Umgesetzt und gemessen wird am PC in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel). Alle Werte hier sind Platzhalter oder Werte anderer Spiele. Die Mischentscheidungen für dein Spiel triffst du. + + ## Priorität: die richtigen Klänge, nicht alle + + Walter Murch, Sounddesigner und Mischtonmeister von Apocalypse Now, beschreibt in [Dense Clarity – Clear Density](https://transom.org/2005/walter-murch/) eine Grenze. Von gleichartigen Klängen kann man etwa zweieinhalb Schichten einzeln verfolgen, er nennt das „Law of Two-and-a-half“. Sind die Schichten über sein Spektrum von Sprache bis Musik verteilt, werden es rund fünf. Für die Landung der Hubschrauber hielt er sich an fünf: „If I wanted to add something new, I had to take something else away.“ Als ein junger Soldat im Hubschrauber „I'm not going!“ ruft, nahm Murch die Musik ganz heraus, obwohl sie aus genau diesem Hubschrauber kommt. Sie war „the sacrificial victim“, und „you do not hear it go away“. + + Spiele brauchen dieselbe Entscheidung, nur automatisch. DICE beschreibt für Battlefield: Bad Company ein System namens HDR-Audio. Es misst die Lautheit am Ort des Hörers und skaliert alle Quellen danach. Auf den Folien steht „Prioritize, instead of compete for dynamic range“ und „Play the right sounds, not all sounds“. Priorität folgt dort der Lautheit: „loudness is a good approximation for ‘importance’ especially in FPS game“, und HDR-Audio „functions as a culling algorithm“, sortiert also leise Klänge aus ([Adaptive mixing in Frostbite](https://www.slideshare.net/slideshow/adaptive-mixing-in-frostbite/3128152), Folien 11 und 16). + + Sakurai ordnet in Smash die Lautstärke nach Wichtigkeit: KO zuerst, dann Treffer und Spezialangriffe, Schritte zuletzt ([Trefferfeedback](/combat/trefferfeedback), Abschnitt Ton). Eine **Prioritätentabelle** schreibt so eine Reihenfolge auf, bevor du etwas einstellst. Am Handy reicht Text: + + ```text + Rang Klangart Grund + 1 … darf man nicht + verpassen + 2 … hilft beim + Spielen + 3 … Stimmung, Bett + ``` + + Was auf welchem Rang steht, entscheidest du für dein Spiel. Die Spalte Grund ist Pflicht: Ohne Grund wird die Tabelle beim ersten Konflikt umgeschrieben. + + ## Bedrohung statt Lautheit: wie Overwatch Vorrang verteilt + + Das Overwatch-Team hat HDR-Audio ausprobiert und wieder verlassen. Ein Sounddesigner drehte den Klang eines brechenden Schilds immer lauter, weil er im Mix nicht durchkam, bis er Stimmen und anderes stark wegduckte ([Wwise Tour 2016, Teil 3](https://www.youtube.com/watch?v=2M5cWHswQsM), ab etwa 2:23). Ein Besucher von Audiokinetic sagte dem Team, HDR sei „basically just a ducking system that works on the only information it has which is volume“ (ab etwa 3:03). Das Team stellte eine andere Frage: „what is your greatest threat“. + + Jeder Gegner bekommt Punkte für seine Bedrohung, etwa wenn er auf dich zielt, schießt oder dich trifft. Dann sortiert das Spiel die Gegner in Plätze mit begrenzter Zahl: Der oberste nimmt nur einen auf, der zweite zwei, der Rest und alle Verbündeten landen weiter unten, und was darüber hinausgeht, im letzten Platz (ab etwa 6:45). Für Waffenfeuer anderer Spieler nannte das Team diese Werte: + + | Platz | Wer | Pegel | Prio | + | --- | --- | --- | --- | + | 1 | 1 Gegner | +7 dB | 95 | + | 2 | 2 Gegner | etwa +3 dB | 60 | + | 3 | Rest, Verbündete | 0 dB | 40 | + | 4 | darüber hinaus | −7 bis −8 dB | 0 | + + Bei Priorität 0, so das Team, hört man den Klang wahrscheinlich nie (ab etwa 8:31 und 9:44). Für Schritte waren die Abstände noch größer, +12 und +9 dB für die beiden oberen Plätze, dann −4 dB, und ihre Priorität lag über der des Waffenfeuers (ab etwa 11:39). Anlass war eine Rückmeldung aus dem Design-Team: Jemand sei von hinten herangelaufen und habe geschossen. + + Die Zahlen gehören zu Overwatch, einem Spiel mit zwölf Spielern. Was du übernehmen kannst, ist der Aufbau: wenige Plätze oben, eine feste Zahl Klänge pro Platz, und eine Regel aus Spielwissen, nicht aus dem Pegel. Welche Spielinformation bei dir die Frage stellt, entscheidest du. + + > [!PRAXIS] Gleiche Dateien, anderer Mix + > Overwatch unterscheidet Freund und Feind ohne eigene Aufnahmen. Waffenfeuer von Verbündeten läuft über dieselben Dateien, aber mit weniger Bass, leiser und leicht tiefer gestimmt, damit man es unbewusst von Gegnern trennt (Teil 3, ab etwa 10:34 und 15:04). Das ist eine Mischregel, keine neue Klanggestaltung: Eine Kategorie bekommt eine feste Behandlung im Mix, und jeder Klang, der hineinfällt, erbt sie. + + ## Maskierung in Frequenz und Zeit + + **Maskierung** ist laut Handbook for Acoustic Ecology „the effect one sound has on another by making it harder or impossible to hear“. Das Handbuch nennt zwei Formen, die du für Spiele brauchst. Tiefe Klänge stören die Wahrnehmung anderer stärker als hohe. Und Maskierung wirkt auch zeitlich, „for a few milliseconds before and after the desired sound“, als Vor- und Nachmaskierung ([Mask](https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Mask.html)). + + Frequenzmaskierung kennst du vom EQ. Im Spiel kannst du aber keine Kombination entzerren, die du nie gehört hast. Deshalb planst du Lagen: Ein Signal, das in dichten Momenten durchkommen muss, braucht einen Bereich im Spektrum, in dem wenig anderes liegt. Die Verbündeten mit weniger Bass in Overwatch zeigen, wie das aussehen kann. Zeitliche Maskierung heißt: Ein leiser Klang direkt nach einer lauten Transiente geht unter, auch wenn er in einer anderen Lage liegt. Für eine Warnung zählt deshalb auch, was im Frame davor spielt. + + Murchs Spektrum ist eine dritte Achse. Er unterscheidet „Encoded“ und „Embodied“: Sprache trägt Bedeutung in einem Code, Musik wirkt direkt. Zwei Gespräche gleichzeitig kann man knapp verfolgen, drei nicht. Schichten aus verschiedenen Bereichen vertragen sich besser als Schichten aus demselben. Für dein Spiel heißt das: Zwei Warnungen mit Sprache gleichzeitig sind riskanter als eine Warnung plus Musik. + + Das Handbuch nennt eine Umgebung, in der Signale sich drängen, Lo-Fi: „overcrowded, resulting in masking and lack of clarity“. Das erste Mittel dagegen ist Murchs: weglassen. Im Spiel kommen begrenzen und verschieben dazu. Lauter drehen ist das letzte Mittel. Der Schild in Overwatch zeigt, warum: Wer immer lauter dreht, verdeckt am Ende das Nächste. + + > [!MERKE] + > Weglassen, begrenzen, verschieben. Lauter drehen kommt zuletzt, denn jeder lautere Klang verdeckt den nächsten. + + ## Gleichzeitig: Summenpegel und Stimmenbegrenzung + + Spielen zwei Klänge zugleich, addieren sie sich. Wie viel lauter das wird, hängt davon ab, ob sie gleich sind. Gemessen in Godot 4.7.2 mit zwei Prüfsignalen, Sinustönen von 440 und 554 Hz mit je 0,5 Sekunden, aufgezeichnet über einen `AudioEffectCapture` auf `Master`: + + ```text + A allein + Spitze 0.2500 RMS 0.1767 + A + A im selben Frame + Spitze +6.02 dB RMS +6.02 dB + A + B im selben Frame + Spitze +6.02 dB RMS +2.98 dB + ``` + + Derselbe Klang zweimal im selben Frame verdoppelt die Amplitude, das sind 20 × log10(2) ≈ +6 dB. Zwei verschiedene Klänge addieren sich in der Leistung, 10 × log10(2) ≈ +3 dB im RMS. Ihre Spitzen können trotzdem zusammenfallen und +6 dB erreichen. Das Modell in Python liefert fast dieselben Zahlen, im RMS von A + B +3,01 statt +2,98 dB: + + ```python run + import math + + RATE = 48000 + N = RATE // 2 # 0,5 s + + + def sinus(freq): + w = 2 * math.pi * freq / RATE + return [0.25 * math.sin(w * i) + for i in range(N)] + + + def rms(x): + q = sum(v * v for v in x) + return math.sqrt(q / len(x)) + + + def db(x, ref): + return 20 * math.log10(x/ref) + + + a, b = sinus(440), sinus(554) + aa = [x + y for x, y in zip(a, a)] + ab = [x + y for x, y in zip(a, b)] + # Anstieg gegenüber A, in dB + daa = db(rms(aa), rms(a)) + dab = db(rms(ab), rms(a)) + dsp = db(max(map(abs, ab)), + max(map(abs, a))) + print(f"A+A RMS {daa:+.2f} dB") + print(f"A+B RMS {dab:+.2f} dB") + print(f"A+B Spitze {dsp:+.2f} dB") + ``` + + Im Spiel heißt das: Trifft ein Schlag zwei Gegner im selben Frame, und beide spielen dieselbe Datei, hörst du einen Treffer, 6 dB lauter, keinen doppelten. Mit verschiedenen Varianten oder leicht verschobener Tonhöhe bleiben es zwei Klänge. Und jede Kategorie, die viele Klänge gleichzeitig erlaubt, braucht Headroom. + + > [!WARNING] Derselbe Klang im selben Frame + > Zehn Kopien desselben Klangs im selben Frame sind 20 dB lauter als einer und klingen wie einer. Plane für Klänge, die oft gleichzeitig ausgelöst werden, Varianten und eine Grenze für gleichzeitige Stimmen. + + Diese Grenze heißt **Stimmenbegrenzung**. In Godot setzt sie `max_polyphony` am Player, Standard 1 (in 4.7.2 per Skript geprüft): „Calling play() after this value is reached will cut off the oldest sounds“ ([max_polyphony](https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-property-max-polyphony)). Wie sich das anhört, misst [Audio zuordnen](/godot/audio). Für die Planung bekommt jede Zeile der [Cue-Liste](/sound/klangplan) eine Zahl „max. Stimmen“. Murch liefert einen Hinweis, wie klein sie sein darf. Bei den Robotern in THX 1138 mussten die Schritte von einem oder zwei synchron zum Bild sein, bei dreien „nothing had to be in sync“. Ab drei hört man eine Gruppe, keine einzelnen. + + ## Ducking und Sidechain als Plan + + **Ducking** heißt: Ein Klang macht einen anderen leiser, solange er spielt. **Sidechain** heißt der Weg dafür. Ein Kompressor auf dem einen Bus hört auf das Signal eines anderen. Godots `AudioEffectCompressor` nennt diese Anwendung: „This technique is common in video game mixing to decrease the volume of music and SFX while voices are being heard. This effect is also known as ‚ducking‘“ ([AudioEffectCompressor](https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html)). Den Bus, auf den er hört, nennt die Eigenschaft `sidechain`, und zwar mit Namen. + + Die Standardwerte in Godot 4.7.2, per Skript geprüft: `threshold` 0 dB, `ratio` 4, `attack_us` 20 µs, `release_ms` 250 ms. Die Attack-Zeit geht laut Doku von 20 bis 2000 Mikrosekunden, die Release-Zeit von 20 bis 2000 Millisekunden. Für deinen Plan rechnest du die Release in Frames: 250 ms sind 15 Frames, 2000 ms 120. Das ist die untere Grenze, nicht die Rückkehrzeit. Gemessen in Godot 4.7.2 mit 250 ms Release fehlten der Musik eine halbe Sekunde nach dem Ende des auslösenden Klangs noch 2,8 dB, eine Sekunde danach noch 0,4 dB ([Lautheit, Export, Import und Hörtest](/sound/export-im-spiel)). + + Ducking ist kein Allheilmittel. Overwatchs Schild zeigt ein Ducking, das niemand geplant hatte. DICE schreibt auf seinen Folien, Alternativen wie „duckers, snapshot mixers, scene defines“ lösten das Problem der Battlefield-Sandbox nicht (Folie 5). Plane deshalb wenige Regeln, jede mit Auslöser und Grund. + + Der Bus-Plan dazu als Baum. Die Namen sind Platzhalter: + + ```text + Master + ├─ Musik + ├─ SFX + │ ├─ SFX_Spieler + │ └─ SFX_Gegner + ├─ Atmo + └─ UI + ``` + + In Godot sendet ein Bus nur an einen Bus weiter links in der Liste, am Ende an `Master` ([Audio zuordnen](/godot/audio)). Eine Ducking-Regel schreibst du als Zeile: + + ```text + Auslöser SFX_Gegner + Ziel Musik + Absenkung Platzhalter + Release 250 ms = 15 Frames + Grund Warnungen hören + ``` + + Die Umsetzung mit `sidechain` und das Messen der Absenkung folgen in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel). + + ## Stille und Dichte über einen Abschnitt + + „There is no such thing as silence“, schloss John Cage nach einem Besuch in einem schalltoten Raum; das Handbuch zitiert ihn im Eintrag [Silence](https://www.sfu.ca/sonic-studio-webdav/cmns/Handbook/Silence.html). Im Spiel ist Stille fast nie digitale Null. Es ist der Moment, in dem Schichten wegfallen und wenig bleibt: ein Room Tone, also das Grundrauschen eines Raums, ein leises Bett, ein einzelnes Signal. Overwatch dämpft alles, wenn du dich in eine Nische zurückziehst, und dann hörst du „clearly one person walk in the room“ ([Teil 4](https://www.youtube.com/watch?v=F2N8bbzhHd8), ab etwa 7:19). Stille macht das Nächste hörbar, das kommt. + + Murch überträgt die Regel der Lautstärke auf die Dichte: „Conceptual density is something that should obey the same rules as loudness dynamics.“ Und: „A monotonously dense soundtrack is just as wearing as a monotonously loud film.“ Für ein Spiel heißt das, die Dichte folgt dem Rhythmus des Abschnitts. Im Level-Design-Kurs hat jeder Takt eine geschätzte Intensität, und dort steht: „Musik und Klang tragen den Rhythmus mit“ ([Rhythmus](/level-design/rhythmus)). Neben den Taktplan schreibst du, welche Schichten pro Takt spielen. Eine ausgedachte Form, keine Vorgabe: + + ```text + Takt Int. Schichten + 1 1 Bett + 2 2 Bett, Spot + 3 4 Musik, SFX + 4 1 Bett, Musik leise + 5 0 Room Tone + ``` + + Mit Kopfhörern prüfst du das, indem du fragst: Was wird wann leiser, und was bleibt? Im Overwatch-Beispiel ab etwa 12:40 in Teil 3 kämpfen zwei Spieler um den oberen Platz, bis sich eine Scharfschützin umdreht und anlegt. Ab da kommen ihre Schüsse durch. Genau solche Wechsel hörst du in deinem eigenen Spiel ab. + + > [!INTERVIEW] + > „Im Bosskampf hört man die Warnung des Bosses nicht. Was tust du?“ Gute Antwort, etwa als Audio Designer: Ich nehme die Stelle auf und schreibe auf, was im Moment der Warnung gleichzeitig spielt. Dann prüfe ich die Prioritätentabelle: Hat die Warnung den Rang, den sie braucht, und hält sich der Mix daran? Danach prüfe ich Maskierung, in der Lage und in der Zeit, etwa eine laute Transiente direkt davor. Erst nehme ich etwas weg, begrenze Stimmen oder ducke die Musik mit einer Regel; lauter drehe ich zuletzt, sonst verdecke ich das Nächste. Danach teste ich dieselbe Situation noch einmal und vergleiche. + + ## Häufige Fehler + + - **Lautstärke gegen Lautstärke.** Jeder wichtige Klang wird lauter gedreht, bis alles laut ist und nichts heraussticht. Priorität und Weglassen zuerst. + - **Derselbe Klang mehrfach im selben Frame.** Er wird bis zu 6 dB pro Verdopplung lauter und klingt wie einer. Varianten und Stimmenbegrenzung planen. + - **Keine Stimmenbegrenzung.** Viele Gegner, viele Schritte, und der Mix wird Lo-Fi. + - **Ducking ohne Grund.** Jede Regel drückt etwas weg. Ohne Auslöser und Grund im Plan weiß später niemand, warum die Musik pumpt. + - **Sidechain auf einen Bus, den es so nicht gibt.** Der Name muss genau stimmen. Sonst hört der Kompressor in Godot 4.7.2 ohne Meldung auf `Master` und duckt bei Klängen, die er nicht soll. + - **Stille als Null.** Wird alles abgeschaltet, klingt es nach Fehler. Ein Room Tone oder ein leises Bett bleibt. + - **Gleiche Dichte im ganzen Abschnitt.** Dann ist der Kampf nicht dichter als der Weg dorthin. + + ## Zusammenfassung + + - Im Spiel mischst du Regeln, nicht Stellen: Priorität, Stimmenbegrenzung, Ducking, Stille. + - Murch: Von gleichartigen Klängen verfolgt man etwa zweieinhalb Schichten, verteilt rund fünf. Wer etwas hinzufügt, nimmt etwas weg. + - DICE priorisiert nach Lautheit und sortiert leise Klänge aus. Overwatch fragt nach der größten Bedrohung und verteilt Vorrang auf wenige Plätze. + - Maskierung wirkt in der Frequenz, tiefe Klänge stärker, und in der Zeit, kurz vor und nach einem Klang. + - Gleiche Klänge im selben Frame: +6 dB pro Verdopplung. Verschiedene: etwa +3 dB im RMS, Spitzen bis +6 dB. Gemessen in Godot 4.7.2. + - `max_polyphony` begrenzt Stimmen und schneidet die ältesten ab. Ducking geht in Godot über `AudioEffectCompressor.sidechain`; 250 ms Release sind 15 Frames, die Rückkehr dauert gemessen länger. + - Stille ist selten Null. Sie macht das Nächste hörbar, und die Dichte folgt dem Rhythmus des Abschnitts. +exercises: + - id: lautheit-oder-bedrohung + type: choice + title: Wer entscheidet, was man hört? + prompt: | + Welche Aussagen über HDR-Audio bei DICE und den Mix von Overwatch stimmen? Mehrere Antworten richtig. + options: + - text: DICE nimmt die Lautheit als Näherung für Wichtigkeit und sortiert leise Klänge aus. + correct: true + why: Auf den Folien steht „loudness is a good approximation for ‘importance’“ und „functions as a culling algorithm“. + - text: Overwatch verteilt Vorrang nach der Bedrohung, die ein Gegner für dich darstellt. + correct: true + why: Die Leitfrage war „what is your greatest threat“. Zielen, Schießen und Treffen geben Punkte. + - text: HDR-Audio ist bei DICE eine Kompression, die alle Klänge auf eine Lautheit bringt. + correct: false + why: Die Folien sagen ausdrücklich „HDR Audio is not compression“. Es arbeitet mit logischen Lautheitswerten und verändert die Wellenform nicht. + - text: In Overwatch kommen beliebig viele Gegner auf den obersten Platz, solange sie auf dich zielen. + correct: false + why: Der oberste Platz nimmt nur einen Gegner auf, der zweite zwei. Die Begrenzung ist der Kern des Systems. + - text: Overwatch ließ HDR fallen, weil es keine Spielinformation kennt, nur den Pegel. + correct: true + why: Ein Besucher von Audiokinetic nannte HDR „just a ducking system that works on the only information it has which is volume“. + explanation: | + Beide Systeme spielen nicht alles, was gerade passiert. Sie unterscheiden sich darin, woher sie wissen, was wichtig ist. DICE nimmt die Lautheit, was in einem Shooter oft passt. Overwatch nimmt Spielwissen, weil ein leiser Gegner, der auf dich zielt, wichtiger sein kann als ein lauter, der wegschaut. Für dein Spiel überlegst du, welche Information die bessere Frage ergibt. + resources: + - title: DICE · HDR Audio, Adaptive mixing in Frostbite (Folien) + url: https://www.slideshare.net/slideshow/adaptive-mixing-in-frostbite/3128152 + - title: Wwise Tour 2016 · Overwatch (3 of 7) – A Clear Mix + url: https://www.youtube.com/watch?v=2M5cWHswQsM + + - id: warnung-geht-unter + type: order + title: Die Warnung geht unter + prompt: | + Eine Warnung ist im dichten Kampf nicht zu hören. Bring die Schritte der Fehlersuche in die Reihenfolge aus der Lektion. + lang: prose + lines: | + Die Stelle aufnehmen und notieren, was im Moment der Warnung gleichzeitig spielt + In der Prioritätentabelle prüfen, welchen Rang die Warnung hat + Maskierung prüfen, in der Lage und in der Zeit + Etwas wegnehmen, Stimmen begrenzen oder mit einer Regel ducken + Erst wenn das nicht reicht, die Warnung lauter machen + Dieselbe Situation noch einmal testen und vergleichen + explanation: | + Zuerst brauchst du einen Befund, keine Vermutung. Die Tabelle sagt dann, ob die Warnung Vorrang haben soll. Maskierung erklärt, warum sie trotzdem untergeht. Die Mittel folgen der Lehre aus Murch und Overwatch: weglassen und begrenzen vor lauter drehen, denn ein lauterer Klang verdeckt den nächsten. Ohne Test derselben Situation weißt du nicht, ob die Änderung gewirkt hat. + + - id: dreimal-derselbe-treffer + type: output + title: Drei Treffer im selben Frame + prompt: | + Ein Schlag trifft n Gegner im selben Frame. Einmal spielt jeder dieselbe Datei, einmal verschiedene Varianten. Das Programm rechnet den Anstieg im RMS gegenüber einem Treffer. Was gibt es aus? + lang: python + code: | + import math + + for n in (2, 3, 4): + gleich = 20 * math.log10(n) + versch = 10 * math.log10(n) + print(n, round(gleich, 1), + round(versch, 1)) + expected: | + 2 6.0 3.0 + 3 9.5 4.8 + 4 12.0 6.0 + hints: + - Gleiche Klänge addieren sich in der Amplitude, verschiedene in der Leistung. + explanation: | + Gleiche Klänge im selben Frame addieren sich wie eine einzige Welle mit n-facher Amplitude, also 20 × log10(n). Vier Kopien sind 12 dB lauter und klingen wie ein Treffer. Verschiedene Klänge addieren sich in der Leistung, 10 × log10(n): Vier Varianten sind etwa 6 dB lauter und als mehrere Treffer zu hören. Die Messung in Godot 4.7.2 bestätigt den Fall n = 2 mit +6,02 und +2,98 dB. + resources: + - title: Godot Docs · Audio buses, Decibel scale + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html#decibel-scale + + - id: spitzen-addieren + type: output + title: Drei Spitzen auf einmal + prompt: | + Drei verschiedene Klänge haben je eine Spitze bei −6 dBFS. Im ungünstigsten Fall fallen ihre Spitzen in dasselbe Sample. Das Programm rechnet die Summe. Was gibt es aus? + lang: python + code: | + import math + + # −6 dBFS als Amplitude + spitze = 10 ** (-6 / 20) + summe = 3 * spitze + print(round(spitze, 3)) + db = 20 * math.log10(summe) + print(round(db, 1)) + print(summe > 1.0) + expected: | + 0.501 + 3.5 + True + hints: + - −6 dB ist ziemlich genau die halbe Amplitude. + explanation: | + −6 dBFS sind eine Amplitude von 0,501. Drei davon im selben Sample ergeben 1,504, also etwa +3,5 dBFS, über der Grenze von 0 dBFS. Im RMS wären drei verschiedene Klänge nur etwa 4,8 dB lauter als einer, ihre Spitzen können aber trotzdem zusammenfallen. Deshalb braucht jede Kategorie, die mehrere Klänge gleichzeitig zulässt, Headroom. Wie du Spitzen und Lautheit im Export misst, folgt in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel). + resources: + - title: Godot Docs · @GlobalScope.linear_to_db + url: https://docs.godotengine.org/en/stable/classes/class_@globalscope.html#class-globalscope-method-linear-to-db + + - id: release-in-frames + type: output + title: Wann ist die Musik zurück? + prompt: | + Eine Warnung spielt von Frame 300 bis Frame 360 und duckt die Musik. Das vereinfachte Modell rechnet nur die Release-Zeit in Frames um und zählt sie zum Ende der Warnung; das ist die früheste Rückkehr. Was gibt das Programm aus? + lang: python + code: | + FPS = 60 + ende = 360 # Frame + for ms in (250, 500, 2000): + n = ms * FPS // 1000 # Frames + print(ms, n, ende + n) + expected: | + 250 15 375 + 500 30 390 + 2000 120 480 + hints: + - 1000 ms sind 60 Frames. + explanation: | + 250 ms sind 15 Frames, 500 ms 30, 2000 ms 120 Frames, die längste Release, die Godots Kompressor erlaubt. Mit 2000 ms ist die Musik frühestens zwei Sekunden nach der Warnung zurück. Folgt in dieser Zeit die nächste Warnung, bleibt die Musik dauerhaft gedrückt. Das Modell ist grob: Ein Kompressor lässt die Absenkung allmählich los, nicht auf einen Schlag. In Godot 4.7.2 gemessen fehlten der Musik mit 250 ms Release eine halbe Sekunde nach dem Ende des auslösenden Klangs noch 2,8 dB, eine Sekunde danach noch 0,4 dB (siehe [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel)). Für den Plan reicht die Rechnung als untere Grenze, um Release-Zeit und Abstand zwischen Ereignissen zu vergleichen; den echten Verlauf misst du im Spiel. + resources: + - title: Godot Docs · AudioEffectCompressor · release_ms + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-release-ms + + - id: godot-eigenschaften + type: gap + title: Plan mit Godot-Namen + prompt: | + Ergänze die Namen der Godot-Eigenschaften und die Umrechnung. Der Kompressor auf dem Bus `Musik` soll auf den Bus `Stimme` hören, der Treffer-Player höchstens drei Klänge gleichzeitig spielen. + lang: text + code: | + Bus Musik + Effekt AudioEffectCompressor + ⟦sidechain⟧ = Stimme + release_ms = 250 + = ⟦15⟧ Frames bei 60/s + Player Treffer + ⟦max_polyphony⟧ = 3 + gaps: + - label: Eigenschaft für den hörenden Bus + hint: Englisch für den Steuerweg eines Kompressors. + accept: ["Sidechain"] + - label: Frames + hint: 250 × 60 / 1000 + - label: Stimmen + hint: Polyphonie, mit Unterstrich davor „max“. + explanation: | + `sidechain` nennt den Bus, dessen Pegel die Absenkung steuert, und zwar mit Namen. `release_ms` ist die Zeit, bis der Kompressor die Absenkung loslässt; 250 ms sind 15 Frames. `max_polyphony` begrenzt, wie viele Klänge ein Player gleichzeitig spielt, und schneidet beim nächsten `play()` die ältesten ab. Die Werte sind Platzhalter. + resources: + - title: Godot Docs · AudioEffectCompressor · sidechain + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-sidechain + - title: Godot Docs · AudioStreamPlayer · max_polyphony + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer.html#class-audiostreamplayer-property-max-polyphony + + - id: begriffe-mix + type: gap + title: Vier Begriffe der Mischung + prompt: | + Ergänze die Begriffe aus der Lektion. + lang: prose + code: | + Wenn ein Klang einen anderen schwerer hörbar macht, heißt das ⟦Maskierung⟧. Ein Klang macht einen anderen leiser, solange er spielt: Das ist ⟦Ducking⟧. Der Steuerweg, über den ein Kompressor auf einen anderen Bus hört, heißt ⟦Sidechain⟧. Die Grenze, wie viele Klänge ein Player gleichzeitig spielen darf, heißt ⟦Stimmenbegrenzung⟧. + gaps: + - label: Verdecken + hint: Englisch masking. + accept: ["Masking", "Verdeckung"] + - label: Absenken + hint: Englisch, vom Ducken. + - label: Steuerweg + hint: Englisch, wörtlich Seitenkette. + accept: ["Seitenkette", "Sidechaining", "Side-Chain", "Side Chain"] + - label: Grenze + hint: In Godot heißt sie max_polyphony. + accept: ["Polyphonie", "max_polyphony", "Voice Limit", "Stimmenlimit"] + explanation: | + Maskierung ist das Problem, die anderen drei sind Werkzeuge. Ducking und Sidechain hängen zusammen: Ducking beschreibt die Wirkung, Sidechain den Weg. Die Stimmenbegrenzung setzt früher an, sie verhindert, dass zu viele Klänge überhaupt spielen. + + - id: sidechain-falscher-bus + type: bug + title: Die Musik duckt bei allem + prompt: | + Laut Plan soll die Musik nur leiser werden, solange ein Gegner etwas spielt. Im Test duckt sie aber bei jedem Effekt, auch bei den Schritten der Spielfigur. Godot meldet keinen Fehler. Die Busse stehen in dieser Reihenfolge von links nach rechts im Audio-Panel. Finde die Zeile, die nicht zum Bus-Plan passt, und korrigiere sie. + lang: text + code: | + Busse + Master + Musik + SFX_Spieler + SFX_Gegner + Ducking + Bus Musik + Effekt AudioEffectCompressor + sidechain SFX_Gegner_Warnung + release 250 ms + lines: [9] + fix: + - line: 9 + answers: + - sidechain SFX_Gegner + - Sidechain SFX_Gegner + - "sidechain = SFX_Gegner" + - "sidechain: SFX_Gegner" + - "Sidechain: SFX_Gegner" + - sidechain = "SFX_Gegner" + - sidechain = &"SFX_Gegner" + explanation: | + `sidechain` nennt einen Bus mit Namen. Einen Bus `SFX_Gegner_Warnung` gibt es im Plan nicht. Godot meldet das nicht, sondern lässt den Kompressor in 4.7.2 auf `Master` hören, gemessen in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel). Auf `Master` liegt schon alles, was Godot vor `Musik` bearbeitet hat, also alle Busse rechts davon, hier beide SFX-Busse. Deshalb duckt jeder Effekt die Musik. Mit `SFX_Gegner` hört der Kompressor nur auf Gegnergeräusche. Sollen nur Warnungen ducken, legst du dafür einen eigenen Bus an. Was du willst, entscheidet der Grund in deiner Ducking-Regel. + resources: + - title: Godot Docs · AudioEffectCompressor · sidechain + url: https://docs.godotengine.org/en/stable/classes/class_audioeffectcompressor.html#class-audioeffectcompressor-property-sidechain + + - id: treffer-klingt-wie-einer + type: bug + title: Drei Gegner, ein Treffer + prompt: | + Du willst hören, wenn ein Schlag mehrere Gegner trifft. Im Test trifft ein Schlag drei Gegner im selben Frame, und du hörst einen einzigen, deutlich lauteren Treffer. Finde die Zeile der Karteikarte, die das verursacht. + lang: text + code: | + ID sfx_dummy_treffer + Auslöser Treffer gemeldet + Typ One-Shot + Varianten 1 + max. Stimmen 3 + Ort ja, 2D am Dummy + lines: [4] + explanation: | + Mit einer einzigen Variante spielen drei Stimmen im selben Frame dieselbe Datei. Sie addieren sich zu einer Welle mit dreifacher Amplitude, etwa 9,5 dB lauter, und klingen wie ein Treffer. Drei Stimmen sind hier gewollt, sonst hörte man nur einen. Damit es nach drei Treffern klingt, brauchen die Stimmen verschiedene Varianten oder eine leicht verschiedene Tonhöhe. Wie viel Abweichung du willst, entscheidest du beim Hören. + resources: + - title: Godot Docs · AudioStreamRandomizer + url: https://docs.godotengine.org/en/stable/classes/class_audiostreamrandomizer.html + + - id: freund-und-feind + type: explain + title: Dieselben Dateien, zwei Lager + prompt: | + Erkläre, was das Overwatch-Team hier gemacht hat und warum es eine Mischentscheidung ist, keine neue Klanggestaltung. + lang: prose + code: | + Waffenfeuer von Verbündeten und Gegnern benutzt dieselben Dateien. + Verbündete laufen über den untersten regulären Platz. + Ihr Waffenfeuer hat weniger Bass, ist leiser und etwas tiefer gestimmt. + points: + - Die Unterscheidung entsteht durch Regeln im Mix, nicht durch eigene Aufnahmen. + - Weniger Bass und tiefere Stimmung geben den Verbündeten eine eigene Lage und verringern Maskierung. + - Gegner behalten den Vorrang, weil sie die Bedrohung sind. + - Jeder neue Klang der Kategorie erbt die Behandlung automatisch. + explanation: | + Das Team brauchte zwei Fassungen jedes Waffenklangs und nahm doch nur eine auf. Die zweite entsteht im Mix: weniger Bass, leiser, etwas tiefer. Spieler trennen so Verbündete von Gegnern, ohne darüber nachzudenken, und die Gegner behalten Vorrang. Weil die Regel an der Kategorie hängt, gilt sie für jede neue Waffe von selbst. Für deinen Plan heißt das: Eine Kategorie kann eine Behandlung haben, die du nur einmal festlegst. + resources: + - title: Wwise Tour 2016 · Overwatch (3 of 7) – A Clear Mix + url: https://www.youtube.com/watch?v=2M5cWHswQsM + + - id: murch-musik-weg + type: explain + title: Die Musik, die man nicht vermisst + prompt: | + Erkläre Murchs Entscheidung in eigenen Worten und was ein Spiel daraus lernen kann. + lang: prose + code: | + Eine Hubschrauberszene mit höchstens fünf Schichten. + Ein junger Soldat im Hubschrauber ruft „I'm not going!“. + Murch nimmt für diesen Satz die Musik ganz heraus, obwohl sie aus genau diesem Hubschrauber kommt. + Nach dem Satz kommt die Musik zurück, und etwas anderes fällt weg. + points: + - Es gibt eine Grenze, wie viele Schichten man gleichzeitig verfolgen kann. + - Wer etwas hinzufügt, muss etwas anderes wegnehmen. + - Die Musik verschwindet, ohne dass man es bemerkt, weil die Aufmerksamkeit beim Satz ist. + - Im Spiel übernimmt eine Regel diese Entscheidung zur Laufzeit, etwa Priorität oder Ducking. + explanation: | + Murch hält die Szene bei fünf Schichten, weil mehr zu Rauschen verschmilzt. Der Satz des Soldaten muss verständlich sein, also muss etwas weichen, und er wählt die Musik, obwohl sie dort am lautesten sein müsste. Man hört sie nicht verschwinden. Ein Spiel kann das nicht von Hand für jede Stelle tun. Es braucht eine Regel, die zur Laufzeit dasselbe entscheidet: Was gerade Vorrang hat, bleibt, anderes wird leiser oder fällt weg. + resources: + - title: Walter Murch · Dense Clarity – Clear Density (Transom) + url: https://transom.org/2005/walter-murch/ + + - id: was-wird-leiser + type: practice + title: Was wird wann leiser? + prompt: | + Hör mit Kopfhörern einen dichten Moment von 20 bis 40 Sekunden aus einem Spiel deiner Wahl, ohne Kommentar. Wenn du keinen zur Hand hast, nimm das Beispiel in [A Clear Mix](https://www.youtube.com/watch?v=2M5cWHswQsM) ab etwa 12:40. Notier in wenigen Zeilen, was wann leiser oder lauter wird, was ganz wegfällt und was durchkommt. + lang: prose + checklist: + - Du hast Spiel oder Clip und den Zeitbereich notiert. + - Mindestens drei Zeilen mit Zeit und dem, was leiser, lauter oder still wird. + - Bei mindestens einer Zeile hast du notiert, welcher Klang dabei Vorrang bekommt. + - Du hast notiert, ob du eine Regel vermutest, etwa Priorität, Ducking oder Stimmenbegrenzung, und sie als Vermutung markiert. + - Bewertungen stehen nicht in den Zeilen. + explanation: | + Einen fertigen Mix kannst du nicht aufschrauben, aber du hörst seine Regeln. Wer gewinnt, wenn es dicht wird? Was verschwindet, ohne dass es auffällt? Die Vermutungen notierst du als Vermutungen; prüfen kannst du sie nur in deinem eigenen Spiel. Das Protokoll aus [Kritisch hören](/sound/hoeren-analysieren) ist dafür die Form. + resources: + - title: Wwise Tour 2016 · Overwatch (3 of 7) – A Clear Mix + url: https://www.youtube.com/watch?v=2M5cWHswQsM + + - id: eigener-mix-plan + type: practice + title: Prioritäten und Busse für einen Abschnitt + prompt: | + Schreib für einen Abschnitt deines Spiels oder einer Testszene einen kurzen Mix-Plan: eine Prioritätentabelle mit drei Rängen, einen Bus-Baum und eine Ducking-Regel. Alle Namen und Werte sind deine Platzhalter. + lang: prose + checklist: + - Die Prioritätentabelle hat drei Ränge, jeder mit Klangart und Grund. + - Der Bus-Baum endet in Master, und jeder Bus hat einen eindeutigen Namen. + - Die Ducking-Regel nennt Auslöser-Bus, Ziel-Bus, Release in Frames und einen Grund. + - Der Auslöser-Bus der Regel steht genau so im Bus-Baum. + - Für mindestens eine Klangart hast du eine Zahl für gleichzeitige Stimmen notiert. + - Du hast notiert, wo im Abschnitt es still wird und was dort bleibt. + hints: + - Fang mit der Frage an, welche Information der Spieler in diesem Abschnitt nicht verpassen darf. + explanation: | + Der Plan hält Mischentscheidungen fest, bevor du in Godot einen Regler anfasst. Er ist kurz genug fürs Handy und genau genug, um ihn am PC umzusetzen und zu messen. Die Prüfung, ob der Auslöser-Bus genau so heißt, verhindert den häufigsten stillen Fehler. Umgesetzt wird der Plan in [Lautheit, Export, Import und Hörtest](/sound/export-im-spiel), geprüft im Spiel. + resources: + - title: Godot Docs · Audio buses + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html diff --git a/content/sound/modules/musik-zustaende.yaml b/content/sound/modules/musik-zustaende.yaml new file mode 100644 index 0000000..32909d1 --- /dev/null +++ b/content/sound/modules/musik-zustaende.yaml @@ -0,0 +1,550 @@ +id: musik-zustaende +title: "Musik für Zustände: horizontal, vertikal, Übergänge" +level: 2 +minutes: 100 +summary: Was ein Stück zusätzlich braucht, damit ein Spiel es umbauen kann – Abschnitte oder Schichten, Stinger, Übergänge mit Zeitpunkt, Ziel und Art, bewusst glatte oder harte Nähte und ein Zustandsdiagramm, das nicht flattert. +place: unterwegs +goals: + - Du unterscheidest horizontale Neuordnung, vertikale Schichtung und Stinger und ordnest veröffentlichte Beispiele zu. + - Du legst für einen Übergang Zeitpunkt, Ziel und Art fest und rechnest aus, wann ein Wechsel auf den nächsten Schlag oder Takt greift. + - Du prüfst eine Naht an Metrum, Klangfarbe, Tonhöhe, Lautstärke und Abruptheit und entscheidest, ob sie glatt oder hart sein soll. + - Du zeichnest ein Zustandsdiagramm mit Auslösern und spielst eine Folge von Ereignissen darin durch. + - Du erkennst Flattern zwischen Zuständen und verhinderst es mit Hysterese oder Nachlauf. +resources: + - title: Winifred Phillips · Horizontal Resequencing and Dynamic Transitions (Game Developer, 2021) + url: https://www.gamedeveloper.com/audio/horizontal-resequencing-and-dynamic-transitions-for-game-music-composers-from-spyder-to-sackboy-gdc-2021- + - title: Winifred Phillips · Pure Vertical Layering (Game Developer, 2021) + url: https://www.gamedeveloper.com/game-platforms/pure-vertical-layering-for-game-music-composers-from-spyder-to-sackboy-gdc-2021- + - title: Elizabeth Medina-Gray · Analyzing Modular Smoothness in Video Game Music (Music Theory Online 25.3, 2019) + url: https://www.mtosmt.org/issues/mto.19.25.3/mto.19.25.3.medina.gray.html + - title: Keith Stuart · Redemption songs, the making of the Red Dead Redemption soundtrack (The Guardian, 2010) + url: https://www.theguardian.com/technology/gamesblog/2010/may/26/red-dead-redemption-soundtrack + - title: Godot Docs · AudioStreamInteractive + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html +lesson: | + Angenommen, ein Spiel hat ein Stück für die Erkundung und eins für den Kampf. Ein Gegner taucht auf, während das Erkundungsstück im dritten Takt steht. Soll die Musik sofort umschlagen, bis zur nächsten Eins warten oder erst ein kurzes Übergangsstück spielen? Und was passiert, wenn der Gegner zwei Sekunden später aus der Reichweite läuft und gleich darauf zurückkommt? + + Aus der Musik kennst du Stücke mit fester Form: Du entscheidest, wann die Strophe endet und der Refrain kommt. Im Spiel entscheidet das der Spieler, und niemand weiß vorher, wie lange er in einem Zustand bleibt. Bill Elm, einer der beiden Komponisten von Red Dead Redemption, sagt dazu: „you don't know how long you're going to be staying in that mood“ ([The Guardian, 2010](https://www.theguardian.com/technology/gamesblog/2010/may/26/red-dead-redemption-soundtrack)). Dieses Modul lehrt keine Komposition. Es zeigt, was ein Stück zusätzlich braucht, damit ein Spiel es umbauen kann: Abschnitte oder Schichten, Übergänge mit Zeitpunkt und Art, ein Zustandsdiagramm und eine Regel gegen Flattern. Welche Zustände dein Spiel hat und wie die Musik dort klingt, entscheidest du. Alle Zahlen in diesem Modul sind Platzhalter. + + ## Zwei Achsen: Abschnitte neu ordnen, Schichten stapeln + + Winifred Phillips, die für Sackboy: A Big Adventure komponiert hat, unterscheidet in ihrem Buch „A Composer's Guide to Game Music“ (MIT Press) zwei Grundtechniken. Bei der **horizontalen Neuordnung** (horizontal re-sequencing) besteht ein Stück aus Abschnitten, die das Spiel in wechselnder Reihenfolge abspielt. Die Abschnitte tragen Marken, an denen gewechselt werden darf, laut Phillips „on each individual beat, at the beginning of each measure, or at designated points in the composition“. Sie zitiert die Stelle selbst in einem Artikel zu ihrem GDC-Vortrag 2021 ([Horizontal Resequencing and Dynamic Transitions](https://www.gamedeveloper.com/audio/horizontal-resequencing-and-dynamic-transitions-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). + + Bei der **vertikalen Schichtung** (vertical layering) laufen mehrere Dateien gleichzeitig und synchron, und das Spiel schaltet sie einzeln zu oder ab. Phillips grenzt das ausdrücklich von den Stems ab, die du aus der Produktion kennst. Mix-Stems klingen am Ende alle zusammen. Schichten im Spiel sind dagegen „not always meant to play simultaneously“ ([Pure Vertical Layering](https://www.gamedeveloper.com/game-platforms/pure-vertical-layering-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). Jede Kombination, die das Spiel erzeugen kann, muss für sich tragen. + + | Spiel | Technik | Was wechselt | + | --- | --- | --- | + | Spyder | horizontal | 30+ Abschnitte, zufällig | + | Sackboy, Bosskampf | horizontal | 3 Abschnitte, steigend | + | Spyder, Bomber | vertikal | Schichten je Fortschritt | + | Super Mario World | vertikal | Percussion auf Yoshi | + | Sackboy, Walzer | beides | 7 Abschnitte, Chor-Schicht | + + Die Angaben zu Spyder und Sackboy stammen aus Phillips' Artikelreihe. In Spyder hat jedes Level über 30 Abschnitte in zufälliger Reihenfolge, damit niemand vorhersagt, was als Nächstes kommt, und die Wechsel sind hörbar. Im Sackboy-Bosskampf sollen die Wechsel dagegen verschwinden. Zu Super Mario World hat Koji Kondo auf der GDC 2007 gezeigt, wie Percussion-Spuren spielen oder schweigen, je nachdem, ob der Spieler auf Yoshi reitet ([Bericht von Vincent Diamante](https://www.gamedeveloper.com/game-platforms/gdc-koji-kondo-s-interactive-musical-landscapes)). + + Die beiden Achsen haben verschiedene Kosten. Horizontal zerlegt das Stück, und jede Stelle, an der zwei Abschnitte aufeinandertreffen, muss funktionieren. Vertikal bleibt das Stück ganz, laut Phillips „without needing to fragment the composition“, aber jede Schicht muss zu allen anderen passen, mit denen sie zusammen klingen kann. + + Die Musiktheoretikerin Elizabeth Medina-Gray nennt diese Stellen Nähte (seams) ([Analyzing Modular Smoothness](https://www.mtosmt.org/issues/mto.19.25.3/mto.19.25.3.medina.gray.html), Absatz 10). Eine Naht ist **sequenziell**, wenn das erste Modul ganz endet, bevor das nächste beginnt, und **simultan**, wenn beide sich überlappen. Vertikale Schichtung erzeugt immer simultane Nähte. Horizontale Neuordnung erzeugt beide Arten: Ein harter Wechsel mit Pause ist sequenziell, eine Kreuzblende simultan. + + ## Stinger: ein kurzer Einwurf + + Ein **Stinger** ist ein kurzes Musikstück, das ein Ereignis markiert. Meist liegt er über der laufenden Musik. Er kann sie auch kurz ersetzen und danach zu ihr zurückkehren; wie das in Godot geht, zeigt [Musik in Godot](/sound/godot-musik). Phillips beschreibt die Erfolgsmelodie von Sackboy: sechs Töne, die bei jedem eingesammelten Dreamer Orb erklingen, in jedem Level und jedem Musikstil. Sie liegt als eigene Schicht über dem Abschnitt, der gerade läuft, und fügt sich harmonisch ein ([Hybrid Horizontal-Vertical Structure](https://www.gamedeveloper.com/game-platforms/hybrid-horizontal-vertical-structure-for-game-music-composers-from-spyder-to-sackboy-gdc-2021-)). + + Für einen Stinger über der Musik heißt das: Er muss über allem klingen, was gerade laufen kann. Passt er nur zu einem Abschnitt, klingt er über den anderen falsch. Ersetzt er die Musik, braucht er stattdessen einen Rückweg, also eine Angabe, wo die Musik danach weiterläuft. Und jeder Stinger braucht einen Zeitpunkt. Sofort ausgelöst, sitzt er im Frame des Ereignisses, aber vielleicht neben dem Schlag. Auf den nächsten Schlag gelegt, sitzt er im Raster, kommt aber bis zu einem Schlag später. + + ## Übergänge: wann, wohin, wie + + Ein Übergang zwischen zwei Abschnitten oder Stücken braucht drei Entscheidungen. + + **Wann** der Wechsel greift: sofort, auf dem nächsten Schlag, auf dem nächsten Takt oder am Ende des laufenden Abschnitts. + + **Wohin** im Ziel: an den Anfang, an dieselbe Stelle (gleiche Position, sinnvoll bei zwei Fassungen desselben Stücks) oder dorthin, wo das Ziel zuletzt aufgehört hat. + + **Wie**: harter Schnitt, Aus- und Einblenden, Kreuzblende (Crossfade) oder über ein **Übergangsstück** (Filler oder Transition-Clip), ein kurzes eigenes Stück, das vom einen zum anderen führt. + + > [!MERKE] + > Jeder Übergang hat einen Zeitpunkt, ein Ziel und eine Art. Schreib alle drei in deinen Plan, sonst entscheidet sie jemand beim Einbauen. + + Je später der Zeitpunkt, desto musikalischer der Wechsel und desto größer die Verzögerung. Wie groß, rechnest du aus Tempo und Taktart. Das Modell sucht für jede Anfrage die erste Schlag- und Taktgrenze danach; liegt eine Anfrage genau auf einer Grenze, nimmt es die folgende: + + ```python run + import math + + BPM = 120 # Platzhalter + TAKT = 4 # Schläge pro Takt + schlag_s = 60 / BPM + takt_s = TAKT * schlag_s + + + def danach(t, raster): + # erste Grenze nach t + n = math.floor(t / raster) + return (n + 1) * raster + + + print("Anfrage: Schlag, Takt") + for t in [0.3, 1.9, 2.05, 6.0]: + s = danach(t, schlag_s) + k = danach(t, takt_s) + print(f"{t}: {s}, {k}") + ``` + + Bei 120 BPM im 4/4-Takt dauert ein Takt 2 Sekunden. Die Anfrage bei 2,05 s kommt knapp nach der Eins und wartet fast einen ganzen Takt, bis 4,0 s. Die Anfrage genau auf der Eins bei 6,0 s wartet einen ganzen Takt, bis 8,0 s; so verhielt sich Godot in der Messung mit einer Anfrage genau auf dem Schlag. Ein ganzer Takt sind bei 60 Physik-Frames pro Sekunde 120 Frames. Ein Trefferton gehört in den Frame, in dem der Treffer gemeldet wird ([Trefferfeedback](/combat/trefferfeedback)); ein Musikwechsel auf den nächsten Takt kann das nicht leisten. Was sofort ankommen muss, trägt deshalb oft ein Geräusch oder ein sofortiger Stinger, und die Musik folgt im Takt. Das ist eine Abwägung, keine Regel. Wie Godot den Zeitpunkt genau bestimmt, misst [Musik in Godot](/sound/godot-musik); dieses Modell ist nur die Rechnung. + + Ein veröffentlichtes Beispiel für einen sofortigen Wechsel: In The Legend of Zelda: The Wind Waker segelt Link vom offenen Meer zu seiner Heimatinsel Outset Island. Medina-Gray hat den Übergang durch wiederholtes Spielen rekonstruiert. Er kann an jeder Stelle der Meeresmusik greifen, die Meeresmusik blendet aus, die Inselmusik blendet ein und beginnt immer an ihrem Anfang, und dazwischen liegen mindestens 0,4 Sekunden Stille (Absatz 8). Weil sich die beiden Stücke nicht überlappen, müssen Tempo und Tonart nicht zueinander passen. + + ## Kontinuität: was eine Naht glatt oder hart macht + + Medina-Gray prüft jede Naht an fünf Merkmalen: Metrum (die zeitliche Ordnung der Schläge), Klangfarbe, Tonhöhe (vor allem die Tonklassen), Lautstärke und Abruptheit, also ob ein Modul mit eigenem Anfang und Ende einsetzt und aufhört oder abgeschnitten wird (Absatz 18). Eine Naht kann in einem Merkmal glatt und in einem anderen hart sein. Der Wind-Waker-Übergang ist glatt in der Blende und hart in der Klangfarbe: Die Kontrabässe streichen auf dem Meer und zupfen auf der Insel (Absatz 11 und 25). + + Hart ist nicht falsch. Medina-Gray zitiert den Komponisten Leonard Paul, nach dem „a good musical break between music segments“ dem Spieler zeigen kann, „that they have entered a new location or game state“ (Absatz 13). Entscheidend ist, dass du den Bruch willst. Was das für deine Dateien heißt, ohne Theorie: + + - **Metrum.** Sollen Abschnitte auf Schlag oder Takt wechseln, brauchen sie ein gemeinsames Raster oder einen Übergang, der das Tempo mitnimmt. Die Engine muss das Raster kennen. Dafür trägst du Tempo und Schläge beim Import ein ([Eigenes Stück aufbereiten](/sound/loops-stems)). + - **Tonhöhe.** Alles, was übereinander liegen kann, also Schichten und Stinger, muss in jeder Kombination zusammen klingen. Bei Abschnitten zählt nur die Stelle, an der sie sich berühren. + - **Klangfarbe und Lautstärke.** Jede Schicht, die dazukommt, verschiebt den Mix. Hör jede Kombination, auch die kleinen. + - **Abruptheit.** Wird ein Abschnitt mitten im Takt verlassen, endet er nicht, er wird abgeschnitten. Blende oder Übergangsstück entscheiden, wie das klingt. + + Für Red Dead Redemption haben Bill Elm und Woody Jackson das Raster als feste Grenze gesetzt. Für einen nahtlosen Klang galt, so der Guardian: „the whole score is composed in A minor and at 130 beats per minute“. Elm beschreibt, was das Spiel verlangt hat: „If the player shoots someone, suddenly the music changes, so we have to think, 'okay, does this work over the top of that?'“ ([The Guardian, 2010](https://www.theguardian.com/technology/gamesblog/2010/may/26/red-dead-redemption-soundtrack)). Das war die Entscheidung dieses Projekts. Welche Grenzen du dir setzt und wo du bewusst brichst, legst du für dein Spiel selbst fest. + + > [!PRAXIS] Seit Jahrzehnten dieselben Mittel + > Koji Kondo zeigte in seinem Vortrag „Painting an Interactive Musical Landscape“ (GDC 2007) Beispiele aus mehreren Spielen. In Super Mario World spielt eine Percussion-Spur nur, solange Mario auf Yoshi reitet. In Super Mario 64 wechselt die Besetzung zwischen Ufer, Wasser und Höhle. In Ocarina of Time setzt das Spiel die Musik der Hyrule-Steppe tagsüber aus zwölf Phrasen zufällig zusammen. Für Kondo beginnt das beim Rhythmus des Spiels: „If the music doesn't reflect the rhythm of the game, it becomes background music that might as well be piped in from any other room.“ Er spielt das Spiel deshalb beim Komponieren immer wieder ([Bericht von Vincent Diamante, Game Developer](https://www.gamedeveloper.com/game-platforms/gdc-koji-kondo-s-interactive-musical-landscapes)). + + ## Zustände planen: Diagramm, Auslöser, Hysterese + + Bevor du Musik für Zustände schreibst, zeichne die Zustände. Ein **Zustandsdiagramm** nennt jeden Zustand der Musik und jeden Übergang mit seinem **Auslöser**, dem Ereignis im Spiel, das ihn startet. Wie du Auslöser aus Animation, Kampf und Level ableitest, steht in [Vom Ereignis zur Cue-Liste](/sound/klangplan). Ein Lehrbeispiel, kein Vorschlag für dein Spiel: + + ```text + RUHE --gesehen--> ALARM + ALARM --verloren--> RUHE + ALARM --angriff--> KAMPF + KAMPF --frei 4 s--> RUHE + ``` + + Jede Zeile ist ein Übergang: Zustand vorher, Auslöser, Zustand danach. „frei 4 s“ heißt: Vier Sekunden nach dem Ereignis `frei`, wenn kein Gegner mehr in Reichweite ist, wechselt die Musik, falls bis dahin nichts anderes passiert. Das ist ein Nachlauf (siehe unten). Ein Ereignis ohne Pfeil im aktuellen Zustand ändert nichts. Zu jedem Pfeil gehört ein Eintrag im Übergangsplan mit Zeitpunkt, Ziel und Art. + + ### Flattern und wie man es verhindert + + Steht der Spieler am Rand der Reichweite eines Gegners, wechselt das Signal „in Reichweite“ ständig. Folgt die Musik jedem Wechsel, springt sie im Sekundentakt zwischen zwei Stücken. Das heißt **Flattern**. Zwei Mittel helfen: + + - **Hysterese:** Für Ein- und Ausstieg gelten verschiedene Schwellen. Der Kampf beginnt unter 100 Pixeln Abstand und endet erst über 160 (Platzhalter). Dazwischen bleibt, was gerade gilt. + - **Nachlauf:** Ein Zustand endet erst, wenn sein Signal eine Zeit lang ununterbrochen fehlt. Kommt es vorher zurück, beginnt die Wartezeit von vorn. + + Das Modell zeigt den Nachlauf an einem Signal mit einem Wert pro Sekunde. Der Kampf (`K`) beginnt sofort. Er endet erst, wenn die letzte Messung mit Gegner `nachlauf` Sekunden zurückliegt: + + ```python run + # 1 = Gegner in Reichweite, + # 0 = nicht; ein Wert pro Sekunde + signal = [0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 0, 0, 0, 0] + + + def musik(signal, nachlauf): + zustand, seit = "-", 0 + zeile = "" + for s in signal: + if s: + zustand, seit = "K", 0 + else: + seit += 1 + if seit >= nachlauf: + zustand = "-" + zeile += zustand + return zeile + + + print("1 s:", musik(signal, 1)) + print("4 s:", musik(signal, 4)) + ``` + + Jedes Zeichen ist eine Sekunde. Mit einer Sekunde Nachlauf folgt die Musik dem Signal und wechselt sechsmal zwischen Sekunde 2 und 8. Mit vier Sekunden wechselt sie zweimal. Der Kampf endet dann bei Sekunde 11, vier Sekunden nach der letzten Messung mit Gegner bei Sekunde 7. Der Preis: Vier Sekunden lang läuft die Kampfmusik ohne Gefahr weiter. + + Valve arbeitet beim Tempo von Left 4 Dead mit ähnlichen Wartezeiten. Der AI Director hält nach der Spitze noch „3-5 seconds“ volle Bedrohung, beginnt die Ruhe erst, wenn sich im Kampf eine Pause ergibt, und hält sie dann „30-45 seconds“ ([Michael Booth, 2009, Folie 81](https://cdn.akamai.steamstatic.com/apps/valve/2009/ai_systems_of_l4d_mike_booth.pdf#page=81)). Wie der Level-Kurs diese Phasen nutzt, steht in [Rhythmus](/level-design/rhythmus). Dein Musikdiagramm kann ähnliche Wartezeiten nutzen. Wie lang sie sind, ist eine Entscheidung, die du im Spiel prüfst. + + > [!WARNING] Wartezeiten addieren sich + > Nachlauf und Takt-Synchronisation zählen zusammen. Vier Sekunden Nachlauf plus Warten auf den nächsten Takt, bei 120 BPM im 4/4 bis zu 2 Sekunden, heißt: Die Ruhe beginnt bis zu 6 Sekunden nach der letzten Messung mit Gegner. Rechne beides zusammen, bevor du im Spiel urteilst. + + ## Was Godot dafür bereitstellt + + Godot 4.7 hat für beide Achsen eigene Streams. `AudioStreamInteractive` hält Clips und eine Übergangstabelle. `add_transition()` nimmt Clip vorher und nachher, Zeitpunkt, Ziel, Blende und ihre Länge in Schlägen und optional ein Übergangsstück ([add_transition](https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition)). Die Zeitpunkte heißen dort `TRANSITION_FROM_TIME_IMMEDIATE`, `…_NEXT_BEAT`, `…_NEXT_BAR` und `…_END`. Die Ziele heißen `TRANSITION_TO_TIME_START`, `…_SAME_POSITION` und `…_PREVIOUS_POSITION`, die Blenden `FADE_DISABLED`, `FADE_IN`, `FADE_OUT`, `FADE_CROSS` und `FADE_AUTOMATIC`. Einen eigenen Stinger-Typ kennt die Klasse nicht. + + `AudioStreamSynchronized` spielt bis zu 32 Streams gleichzeitig, jeden mit eigener Lautstärke; das ist die vertikale Schichtung ([AudioStreamSynchronized](https://docs.godotengine.org/en/stable/classes/class_audiostreamsynchronized.html)). Damit Schlag und Takt stimmen, muss jeder Clip sein Tempo und seine Schläge kennen. Das trägst du beim Import ein ([Eigenes Stück aufbereiten](/sound/loops-stems)). Aufbau, Umschalten aus dem Spiel und gemessene Wechselzeiten stehen in [Musik in Godot](/sound/godot-musik). + + > [!INTERVIEW] + > „Die Musik soll auf Kämpfe reagieren, die jederzeit beginnen und enden können. Wie gehst du vor?“ Gute Antwort, etwa als Composer oder Technical Sound Designer: Erst klären, was die Musik dem Spieler sagen soll und was Geräusche schon sagen. Dann die Zustände als Diagramm mit Auslösern, und für jeden Übergang Zeitpunkt, Ziel und Art. Ich nenne die Abwägung: Wechsel auf den Takt klingen musikalisch, kommen aber bis zu einem Takt später; was sofort ankommen muss, trägt ein Stinger oder ein Geräusch. Gegen Flattern setze ich Hysterese oder Nachlauf und rechne die Wartezeiten zusammen. Getestet wird mit einem Prüfloop und einem Log, das Anfrage und tatsächlichen Wechsel zeigt. + + ## Häufige Fehler + + - **Wechsel auf den Takt für dringende Information.** Bei 120 BPM im 4/4 kommt er bis zu 2 Sekunden zu spät. Leg die Information in ein Geräusch oder einen sofortigen Stinger. + - **Schichten wie Mix-Stems geschrieben.** Allein oder in Teilkombinationen klingen sie leer oder schief. Hör jede Kombination, die das Spiel erzeugen kann. + - **Stinger, der nur zu einem Abschnitt passt.** Über den anderen klingt er falsch. + - **Gleiche Schwelle für Ein- und Ausstieg.** Die Musik flattert am Rand der Reichweite. + - **Nachlauf und Takt nicht zusammengerechnet.** Die Ruhe kommt später, als du beim Testen erwartest. + - **Raster nicht notiert.** Ohne Tempo und Schläge kann die Engine nicht auf Schlag oder Takt wechseln. + - **Jede Naht geglättet.** Der Spieler merkt nicht, dass sich der Zustand geändert hat. Manchmal ist der hörbare Bruch die Information. + + ## Zusammenfassung + + - Horizontale Neuordnung spielt Abschnitte in wechselnder Reihenfolge, vertikale Schichtung schaltet synchrone Schichten zu und ab. Schichten im Spiel müssen in jeder Kombination tragen, anders als Mix-Stems. + - Ein Stinger markiert ein Ereignis. Meist liegt er über der laufenden Musik und muss über allem klingen, was gerade laufen kann; ersetzt er sie kurz, braucht er einen Rückweg. + - Jeder Übergang braucht Zeitpunkt (sofort, Schlag, Takt, Ende), Ziel (Anfang, gleiche oder letzte Position) und Art (Schnitt, Blende, Übergangsstück). + - Ein Wechsel auf den nächsten Takt greift auf der ersten Taktgrenze nach der Anfrage. Er wartet bis zu einem Takt, bei 120 BPM im 4/4 bis zu 2 Sekunden oder 120 Frames. + - Eine Naht ist sequenziell oder simultan und kann in Metrum, Klangfarbe, Tonhöhe, Lautstärke und Abruptheit glatt oder hart sein. Ein harter Bruch kann einen neuen Zustand anzeigen. + - Ein Zustandsdiagramm nennt Zustände und Auslöser. Hysterese (zwei Schwellen) und Nachlauf (Ausstieg erst, wenn das Signal eine Zeit lang fehlt) verhindern Flattern und kosten Zeit. + - In Godot heißen die Bausteine `AudioStreamInteractive` und `AudioStreamSynchronized`. Tempo und Schläge kommen aus dem Import. +exercises: + - id: technik-zuordnen + type: choice + title: Abschnitt, Schicht oder Einwurf? + prompt: | + Welche Zuordnungen stimmen mit den Quellen der Lektion überein? + options: + - text: Super Mario World, Percussion nur auf Yoshi – vertikale Schichtung + correct: true + why: Kondo zeigte Percussion-Spuren, die über der Hintergrundmusik spielen oder schweigen. Das Stück läuft weiter, eine Schicht kommt dazu. + - text: Spyder, über 30 Abschnitte je Level in zufälliger Reihenfolge – horizontale Neuordnung + correct: true + why: Das Spiel ordnet Abschnitte neu an. Phillips betont, dass die Wechsel dort hörbar sein dürfen. + - text: Sackboys Erfolgsmelodie beim Dreamer Orb – horizontale Neuordnung + correct: false + why: Die Melodie ersetzt keinen Abschnitt. Sie liegt als eigene Schicht über dem Abschnitt, der gerade läuft, so wie ein Stinger. + - text: Wind Waker, vom Meer zur Insel mit Blende und Pause – vertikale Schichtung + correct: false + why: Die Meeresmusik endet, bevor die Inselmusik beginnt. Das ist ein horizontaler Wechsel mit sequenzieller Naht. + explanation: | + Die Frage ist jedes Mal, was das Spiel umbaut. Ordnet es Abschnitte nacheinander an, ist es horizontal; schaltet es gleichzeitige Spuren zu oder ab, ist es vertikal. Ein Stinger, der wie Sackboys Erfolgsmelodie über der Musik liegt, wirkt wie eine kurze Schicht: von einem Ereignis ausgelöst, über allem anderen. Sackboys Walzer zeigt, dass ein Stück beides zugleich nutzen kann. + resources: + - title: Winifred Phillips · Hybrid Horizontal-Vertical Structure (Game Developer, 2021) + url: https://www.gamedeveloper.com/game-platforms/hybrid-horizontal-vertical-structure-for-game-music-composers-from-spyder-to-sackboy-gdc-2021- + - title: Vincent Diamante · GDC, Koji Kondo's Interactive Musical Landscapes (Game Developer, 2007) + url: https://www.gamedeveloper.com/game-platforms/gdc-koji-kondo-s-interactive-musical-landscapes + + - id: rdr-raster + type: choice + title: Eine Tonart, ein Tempo + prompt: | + Bill Elm und Woody Jackson schrieben die ganze Musik von Red Dead Redemption in a-Moll bei 130 BPM. Welchen Grund nennt der Guardian-Artikel? + options: + - text: Die Loops sollten ineinander übergehen und übereinander passen, wenn das Spiel die Musik wechselt. + correct: true + why: Der Artikel nennt als Grund „to ensure a seamless sound“. Elm beschreibt die Frage, ob eine Spur über einer anderen funktioniert, wenn der Spieler schießt. + - text: Die Engine konnte nur ein Tempo abspielen. + correct: false + why: Davon steht nichts im Artikel. Die Grenze war eine Entscheidung der Komponisten, keine technische Vorgabe. + - text: Western-Musik steht traditionell in a-Moll. + correct: false + why: Der Artikel begründet die Wahl nicht mit dem Genre, sondern mit den Übergängen zwischen den Loops. + - text: So ließ sich schneller komponieren. + correct: false + why: Elm nennt die Grenze im Nachhinein befreiend, aber der Grund war, dass die Stücke nahtlos ineinandergreifen. + explanation: | + Ein gemeinsames Raster macht jede Naht im Metrum glatt und senkt das Risiko, dass zwei Schichten oder zwei Loops in der Tonhöhe kollidieren. Der Preis ist Spielraum. Elm sagt im Artikel, es habe sich zuerst wie das schwerste Problem angefühlt und sei dann befreiend gewesen. Das ist ein Projekt; ob du dir so eine Grenze setzt, ist deine Entscheidung. + resources: + - title: Keith Stuart · Redemption songs (The Guardian, 2010) + url: https://www.theguardian.com/technology/gamesblog/2010/may/26/red-dead-redemption-soundtrack + + - id: uebergang-ablauf + type: order + title: Ein Wechsel auf die nächste Eins + prompt: | + Ein Übergang ist so geplant: Zeitpunkt „nächster Takt“, über ein Übergangsstück, das Ziel beginnt am Anfang. Bring die Schritte in die Reihenfolge, in der sie im Spiel passieren. + lang: prose + lines: | + Im Spiel tritt der Auslöser ein, und das Zustandsdiagramm wechselt den Zustand. + Die Musik bekommt die Anfrage, zum neuen Stück zu wechseln. + Das laufende Stück spielt bis zur nächsten Taktgrenze weiter. + An der Taktgrenze beginnt das Übergangsstück. + Nach dem Übergangsstück beginnt das Ziel an seinem Anfang. + explanation: | + Der Zustand im Spiel wechselt sofort, die Musik folgt später. Zwischen Anfrage und Taktgrenze vergeht bis zu ein Takt, danach noch die Länge des Übergangsstücks. Diese Verzögerung ist der Preis für einen musikalischen Wechsel, und sie gehört in deinen Plan. Ein Log, das Anfrage und tatsächlichen Wechsel getrennt festhält, macht sie im Test sichtbar. + resources: + - title: Godot Docs · AudioStreamInteractive · add_transition + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-method-add-transition + + - id: naechster-takt-3-4 + type: output + title: Wann greift der Wechsel? + prompt: | + Ein Stück im 3/4-Takt bei 90 BPM (Platzhalter). Die Anfrage kommt bei 5,3 s. Das Programm rechnet den nächsten Schlag, den nächsten Takt und die Wartezeit bis zum Takt aus. Was gibt es aus? + lang: python + code: | + import math + + BPM = 90 + TAKT = 3 + schlag_s = 60 / BPM + takt_s = TAKT * schlag_s + anfrage = 5.3 + + + def danach(t, raster): + # erste Grenze nach t + n = math.floor(t / raster) + return (n + 1) * raster + + + schlag = danach(anfrage, schlag_s) + takt = danach(anfrage, takt_s) + print(f"{schlag:.2f}") + print(f"{takt:.2f}") + print(f"{takt - anfrage:.2f}") + expected: | + 5.33 + 6.00 + 0.70 + hints: + - Ein Schlag dauert 60 / 90 s, ein Takt drei Schläge, also 2 s. + explanation: | + Ein Schlag dauert zwei Drittel Sekunden, ein Takt genau 2 Sekunden. Die Schlaggrenzen liegen bei 4,67, 5,33 und 6,00 s, die Taktgrenzen bei 4, 6 und 8 s. Der nächste Schlag nach 5,3 s kommt also nach 0,03 s, der nächste Takt nach 0,70 s. Im 3/4-Takt bei 90 BPM ist ein Takt genauso lang wie im 4/4 bei 120 BPM; die längste Wartezeit ist in beiden Fällen 2 Sekunden. + resources: + - title: Godot Docs · AudioStreamInteractive, TRANSITION_FROM_TIME_NEXT_BAR + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-constant-transition-from-time-next-bar + + - id: hysterese-folge + type: output + title: Eine Schwelle oder zwei? + prompt: | + Der Abstand zum nächsten Gegner wird einmal pro Sekunde gemessen. `folge()` schreibt für jede Messung `K` für Kampfmusik und `-` für keine. Einmal gilt dieselbe Schwelle für Ein- und Ausstieg, einmal steigt die Musik erst über 160 Pixel aus. Was gibt das Programm aus? + lang: python + code: | + abstand = [180, 120, 95, 110, 98, + 130, 150, 165, 140] + + + def folge(abstand, rein, raus): + kampf, zeile = False, "" + for a in abstand: + if not kampf and a < rein: + kampf = True + elif kampf and a > raus: + kampf = False + if kampf: + zeile += "K" + else: + zeile += "-" + return zeile + + + print(folge(abstand, 100, 100)) + print(folge(abstand, 100, 160)) + expected: | + --K-K---- + --KKKKK-- + hints: + - Geh die Liste Wert für Wert durch und merk dir, ob gerade Kampf ist. + - Bei 140 am Ende ist der Kampf in beiden Fällen schon vorbei; 140 liegt nicht unter 100. + explanation: | + Mit einer Schwelle springt die Musik bei 95 hinein, bei 110 hinaus, bei 98 wieder hinein und bei 130 wieder hinaus: vier Wechsel in vier Sekunden. Mit Hysterese bleibt sie von 95 bis 150 im Kampf, weil kein Wert über 160 liegt, und steigt erst bei 165 aus: zwei Wechsel. Die Zone zwischen den Schwellen hält den aktuellen Zustand fest. Wie breit sie sein muss, hängt an Bewegung und Reichweite in deinem Spiel und ist ein Platzhalter, bis du es getestet hast. + + - id: zustand-durchspielen + type: output + title: Welcher Zustand gilt? + prompt: | + Das Lehrbeispiel aus der Lektion in Pfeil-Notation: Jede Zeile ist ein Übergang `vorher --Auslöser--> danach`. „frei 4 s“ heißt: Vier Sekunden nach dem Ereignis `frei` wechselt die Musik, wenn bis dahin nichts anderes passiert. Ein Ereignis ohne Pfeil im aktuellen Zustand ändert nichts. Die Musik beginnt in RUHE. + + Welcher Zustand gilt bei 3 s, 5,5 s, 8,5 s, 12 s und 14 s? Eine Zeile je Zeitpunkt. + lang: ablauf + code: | + RUHE --gesehen--> ALARM + ALARM --verloren--> RUHE + ALARM --angriff--> KAMPF + KAMPF --frei 4 s--> RUHE + + Ereignisse: + 2 s gesehen + 5 s verloren + 6 s gesehen + 7 s angriff + 8 s verloren + 9 s frei + expected: | + ALARM + RUHE + KAMPF + KAMPF + RUHE + hints: + - Bei 8 s ist die Musik in KAMPF. Gibt es dort einen Pfeil für `verloren`? + - "`frei` bei 9 s plus vier Sekunden." + explanation: | + Bei 2 s geht es nach ALARM, bei 5 s zurück nach RUHE, bei 6 s wieder nach ALARM und bei 7 s nach KAMPF. Das `verloren` bei 8 s hat in KAMPF keinen Pfeil und ändert nichts. Ab 9 s läuft der Nachlauf, bei 13 s wechselt die Musik nach RUHE; bei 12 s ist sie also noch in KAMPF, bei 14 s in RUHE. Ein Ereignis ohne Pfeil und ein laufender Nachlauf sind die Fälle, die ein Diagramm ausdrücklich festlegen muss. Steht dazu nichts darin, entscheidet sie jemand beim Einbauen. + + - id: begriffe + type: gap + title: Die Wörter für den Plan + prompt: | + Ergänze die Begriffe aus der Lektion. + lang: prose + code: | + Spielt das Spiel Abschnitte in wechselnder Reihenfolge, heißt das horizontale ⟦Neuordnung⟧. + Laufen mehrere Dateien synchron und werden einzeln zugeschaltet, heißt das vertikale ⟦Schichtung⟧. + Ein kurzes Musikstück, das ein Ereignis markiert, heißt ⟦Stinger⟧. + Verschiedene Schwellen für Ein- und Ausstieg nennt man ⟦Hysterese⟧. + Endet das erste Modul, bevor das nächste beginnt, ist die Naht ⟦sequenziell⟧. + gaps: + - label: Horizontal + hint: Englisch re-sequencing. + accept: ["Umordnung", "Resequencing", "Re-Sequencing"] + - label: Vertikal + hint: Englisch layering. + accept: ["Layering"] + - label: Einwurf + hint: Englischer Fachbegriff, wie ein Stich. + - label: Zwei Schwellen + hint: Begriff aus der Physik und der Regelungstechnik. + - label: Naht ohne Überlappung + hint: Das Gegenteil von simultan. + accept: ["sequentiell"] + explanation: | + Mit diesen Wörtern lässt sich ein Musikplan knapp beschreiben, auch gegenüber einem Programmierer. Horizontal und vertikal sagen, was das Spiel umbaut, Stinger ein kurzes Stück für ein Ereignis, Hysterese eine Regel gegen Flattern. Sequenziell und simultan stammen von Medina-Gray und sagen, ob sich zwei Module an der Naht überlappen. + resources: + - title: Elizabeth Medina-Gray · Analyzing Modular Smoothness (MTO 25.3) + url: https://www.mtosmt.org/issues/mto.19.25.3/mto.19.25.3.medina.gray.html + + - id: formel-takt + type: gap + title: Die Rechnung für den nächsten Takt + prompt: | + Ergänze das Modell: Ein Takt dauert so viele Sekunden, wie seine Schläge zusammen dauern, und der Wechsel liegt auf der ersten Taktgrenze nach der Anfrage `t`. `schlaege` sind die Schläge pro Takt. + lang: python + code: | + import math + + + def takt_nach(t, bpm, schlaege): + takt_s = ⟦schlaege * 60 / bpm⟧ + n = ⟦math.floor⟧(t / takt_s) + return (n + 1) * takt_s + gaps: + - label: Länge eines Takts + hint: Ein Schlag dauert 60 / bpm Sekunden. + accept: ["60 / bpm * schlaege", "schlaege * (60 / bpm)", "(60 / bpm) * schlaege", "60 * schlaege / bpm", "(schlaege * 60) / bpm", "schlaege / bpm * 60", "schlaege * 60.0 / bpm", "60.0 / bpm * schlaege", "schlaege / (bpm / 60)"] + - label: Abrunden + hint: Die Funktion aus `math`, die zur ganzen Zahl darunter rundet. + accept: ["int"] + explanation: | + Die Anfrage geteilt durch die Taktlänge sagt, im wievielten Takt sie liegt, etwa 2,55. Abgerundet ist das die Nummer der letzten Taktgrenze bis zur Anfrage; plus eins ergibt die nächste, mal Taktlänge ihre Zeit. Liegt die Anfrage genau auf einer Grenze, kommt so die folgende heraus. Godot verhielt sich in der Messung bei einer Anfrage genau auf dem Schlag ebenso ([Musik in Godot](/sound/godot-musik)). `math.ceil` würde in diesem Fall die Grenze selbst liefern; ohne `+ 1` käme die Grenze vor der Anfrage heraus, ein Wechsel in der Vergangenheit. `int()` liefert für positive Zeiten dasselbe wie `math.floor`. + resources: + - title: Python Docs · math.floor + url: https://docs.python.org/3/library/math.html#math.floor + + - id: plan-falsche-zeile + type: bug + title: Der Plan stimmt nicht mit dem Test + prompt: | + Du hast vor dem Test einen Übergangsplan gerechnet. Im Log stimmen drei Wechsel mit dem Plan überein. Bei einer Anfrage wechselt die Musik im Test erst bei 12,0 s. Finde die Zeile im Plan, die falsch gerechnet ist, und korrigiere sie. + lang: text + code: | + Tempo 100 BPM, 4/4 (Platzhalter) + Schlag 0,6 s, Takt 2,4 s + Wechsel: nächster Takt + Anfrage 3,0 s -> Takt bei 4,8 s + Anfrage 5,0 s -> Takt bei 7,2 s + Anfrage 9,7 s -> Takt bei 9,6 s + Anfrage 12,1 s -> Takt bei 14,4 s + lines: [6] + fix: + - line: 6 + answers: + - Anfrage 9,7 s -> Takt bei 12,0 s + - Anfrage 9,7 s -> Takt bei 12 s + - Anfrage 9,7 s -> Takt bei 12.0 s + - Anfrage 9.7 s -> Takt bei 12.0 s + - Anfrage 9.7 s -> Takt bei 12 s + - Anfrage 9.7 s -> Takt bei 12,0 s + hints: + - Die Taktgrenzen liegen bei 0; 2,4; 4,8; 7,2; 9,6; 12,0 und 14,4 s. + explanation: | + 9,6 s liegt vor der Anfrage. Ein Wechsel kann nicht in der Vergangenheit greifen, der nächste Takt nach 9,7 s beginnt bei 12,0 s. Das ist der typische Fehler, die letzte Grenze vor der Anfrage zu nehmen statt der ersten danach. Die Anfrage kam 0,1 s nach der Eins und wartet deshalb fast einen ganzen Takt, 2,3 Sekunden. Plan und Log nebeneinander zu legen ist die schnellste Art, solche Rechenfehler zu finden, bevor du am Klang zweifelst. + + - id: flattern-regel + type: bug + title: Die Musik springt am Rand + prompt: | + Ein Lehrbeispiel. Steht der Spieler ungefähr 100 Pixel vom Gegner entfernt und bewegt sich ein wenig, springt die Musik mehrmals pro Sekunde zwischen RUHE und KAMPF. Der Kampf soll weiter sofort beginnen, wenn der Gegner näher als 100 Pixel kommt. Welche Regel änderst du? + lang: text + code: | + RUHE --abstand < 100--> KAMPF + KAMPF --abstand > 100--> RUHE + KAMPF --spieler tot--> RUHE + lines: [2] + hints: + - Die erste Regel soll bleiben, wie sie ist. + explanation: | + Ein- und Ausstieg hängen an derselben Schwelle. Jede kleine Bewegung über die 100-Pixel-Linie löst einen Wechsel aus. Die Ausstiegsregel braucht entweder eine höhere Schwelle, etwa `abstand > 160` (Hysterese), oder eine Zeit, etwa „abstand > 100 für 4 s“ (Nachlauf). Beides sind Platzhalter, deshalb gibt es hier keine einzige richtige Korrektur. Die dritte Regel ist ein eigener Auslöser und flattert nicht. + + - id: wind-waker-naht + type: explain + title: Vom Meer zur Insel + prompt: | + Medina-Gray hat diesen Übergang aus The Legend of Zelda: The Wind Waker durch wiederholtes Spielen rekonstruiert. Erkläre, warum er ohne Takt-Synchronisation auskommt, was die Pause bewirkt und in welchen Merkmalen die Naht glatt und in welchen hart ist. + lang: text + code: | + Von: Ocean (Schleife) + Nach: Outset Island (Schleife) + Auslöser: Insel ansteuern + Zeitpunkt: an jeder Stelle + Ziel: Anfang von Outset Island + Art: Ocean blendet aus, + Outset Island blendet ein, + mind. 0,4 s Stille dazwischen + points: + - Die Naht ist sequenziell; die Stücke überlappen nicht, deshalb müssen Tempo und Tonart nicht zueinander passen. + - Weil der Wechsel an jeder Stelle greifen darf, folgt die Musik dem Spieler ohne Wartezeit auf einen Takt. + - Blende und Pause machen die Naht in der Abruptheit glatt; nichts wird hörbar abgeschnitten. + - Die Klangfarbe wechselt hart (gestrichen gegen gezupft); der Bruch zeigt einen neuen Ort an. + - Weil die Inselmusik immer am Anfang beginnt, hört der Spieler bei jeder Ankunft denselben Einstieg. + explanation: | + Der Übergang verzichtet auf Synchronisation, weil die beiden Stücke sich nie berühren: Die Meeresmusik ist ausgeblendet, bevor die Inselmusik beginnt, dazwischen liegt Stille. So kann das Spiel an jeder Stelle wechseln, und der Spieler wartet auf nichts. Die Blenden sorgen dafür, dass kein Stück mitten in einer Phrase abreißt. Die Klangfarbe dagegen bricht deutlich, die Bässe streichen auf dem Meer und zupfen auf der Insel. Das ist im Sinne von Leonard Paul ein Bruch, der einen neuen Ort ansagt. Und weil die Inselmusik immer an ihrem Anfang beginnt, wird ihr Einstieg zum Zeichen für die Ankunft. + resources: + - title: Elizabeth Medina-Gray · Analyzing Modular Smoothness (MTO 25.3), Absatz 8 und 11 + url: https://www.mtosmt.org/issues/mto.19.25.3/mto.19.25.3.medina.gray.html + + - id: hoerprotokoll-wechsel + type: practice + title: Drei Musikwechsel abhören + prompt: | + Spiel ein Spiel deiner Wahl mit Kopfhörern oder sieh dir einen Clip davon an, und warte auf drei Stellen, an denen die Musik wechselt. Notiere zu jedem Wechsel kurz: Auslöser, Zeitpunkt (sofort, Schlag, Takt, Ende), Art (Schnitt, Blende, Pause, Übergangsstück), horizontal oder vertikal, und ob die Naht glatt oder hart klingt. Ein Satz pro Wechsel reicht. + lang: prose + starter: | + Spiel und Stelle: + + Wechsel 1: + Wechsel 2: + Wechsel 3: + checklist: + - Du hast Spiel und Stelle notiert, sodass du die Wechsel wiederfindest. + - Zu jedem der drei Wechsel steht ein Auslöser im Spiel. + - Zu jedem Wechsel steht ein Zeitpunkt aus den vier Kategorien oder „nicht erkennbar“. + - Zu jedem Wechsel steht, ob ein Abschnitt wechselt oder eine Schicht dazukommt oder wegfällt. + - Bei mindestens einem Wechsel hast du ein Merkmal genannt, in dem die Naht hart oder glatt ist (Metrum, Klangfarbe, Tonhöhe, Lautstärke, Abruptheit). + explanation: | + Das ist Medina-Grays Methode im Kleinen: hören, wiederholen, rekonstruieren, ohne Zugriff auf den Code. „Nicht erkennbar“ ist eine gültige Antwort; oft hörst du erst nach mehreren Wiederholungen, ob ein Wechsel auf den Takt wartet. Die Notizen sind Material für deine eigenen Entscheidungen, keine Vorlage zum Kopieren. + resources: + - title: Elizabeth Medina-Gray · Analyzing Modular Smoothness (MTO 25.3) + url: https://www.mtosmt.org/issues/mto.19.25.3/mto.19.25.3.medina.gray.html + + - id: eigenes-diagramm + type: practice + title: Dein Zustandsdiagramm + prompt: | + Schreib ein Zustandsdiagramm für die Musik eines Abschnitts aus deinem eigenen Spiel in der Pfeil-Notation der Lektion, zwei bis vier Zustände. Ergänze zu jedem Pfeil Zeitpunkt, Ziel und Art des Übergangs und zu mindestens einem Ausstieg eine Regel gegen Flattern. Welche Zustände es gibt und wie sie klingen, entscheidest du; Zahlen sind Platzhalter. + lang: prose + starter: | + Abschnitt: + + Zustände: + + Pfeile (vorher --Auslöser--> danach | Zeitpunkt | Ziel | Art): + + Regel gegen Flattern: + checklist: + - Das Diagramm hat zwei bis vier Zustände, und jeder ist von mindestens einem Pfeil erreichbar. + - Jeder Pfeil nennt einen Auslöser, der im Spiel als Ereignis oder Messwert vorkommt. + - Zu jedem Pfeil stehen Zeitpunkt, Ziel und Art. + - Mindestens ein Ausstieg hat eine Hysterese oder einen Nachlauf mit Platzhalterwert. + - Du hast für einen Pfeil die längste Wartezeit ausgerechnet, mit Takt und Nachlauf zusammen. + explanation: | + Das Diagramm ist der Vertrag zwischen deiner Musik und dem Code, der sie umschaltet. Ein Auslöser, den das Spiel nicht kennt, lässt sich nicht einbauen; ein Pfeil ohne Zeitpunkt wird beim Einbauen nach Gefühl entschieden. Die ausgerechnete Wartezeit sagt dir vorher, was du im Test hören solltest. Aufbereitet wird die Musik in [Eigenes Stück aufbereiten](/sound/loops-stems), eingebaut in [Musik in Godot](/sound/godot-musik). + resources: + - title: Michael Booth · The AI Systems of Left 4 Dead (Valve, 2009), Folie 81 + url: https://cdn.akamai.steamstatic.com/apps/valve/2009/ai_systems_of_l4d_mike_booth.pdf#page=81 diff --git a/content/sound/modules/quellen-lizenzen.yaml b/content/sound/modules/quellen-lizenzen.yaml new file mode 100644 index 0000000..bc71a0d --- /dev/null +++ b/content/sound/modules/quellen-lizenzen.yaml @@ -0,0 +1,584 @@ +id: quellen-lizenzen +title: "Quellen und Lizenzen: fremde Klänge sauber dokumentieren" +level: 1 +minutes: 80 +summary: Welche Lizenzen fremder Klänge zu einem verkauften Spiel passen – CC0, CC BY, BY-SA, BY-NC, ND, Freesound, Sonniss, Splice und Library-EULAs –, wie eine Namensnennung nach TASL aussieht und wie eine Quellenliste jede Datei nachvollziehbar macht. Kein Rechtsrat, aber die Texte, auf die es ankommt. +place: unterwegs +goals: + - Du unterscheidest CC0, CC BY, BY-SA, BY-NC und BY-ND und begründest mit dem Lizenztext, welche für ein verkauftes Spiel in Frage kommen. + - Du schreibst eine Namensnennung nach TASL und erkennst unvollständige Credit-Zeilen. + - Du liest die Bedingungen von Freesound, Sonniss, Splice und Native Instruments auf drei Fragen hin – Spiel erlaubt, Namensnennung nötig, Weitergabe als Datei verboten – und benennst, was offen bleibt. + - Du führst eine Quellenliste mit Lizenzversion, Abrufdatum und Nachweis und erklärst, warum das Abrufdatum zählt. + - Du hältst KI-generiertes Material aus deinem Projekt und lässt KI höchstens technische Hilfen liefern. +resources: + - title: Creative Commons · Über die CC-Lizenzen + url: https://creativecommons.org/about/cclicenses/ + - title: Creative Commons Wiki · Recommended practices for attribution + url: https://wiki.creativecommons.org/wiki/Recommended_practices_for_attribution + - title: Freesound · FAQ, Licenses + url: https://freesound.org/help/faq/#licenses + - title: Sonniss · GDC Game Audio Bundle License + url: https://sonniss.com/gdc-bundle-license/ + - title: Splice · Terms of Use + url: https://splice.com/terms +lesson: | + In deinem Projektordner liegt `amb_regen_01.wav`. Du weißt noch, dass er gut klang. Du weißt nicht mehr, ob du ihn aufgenommen, bei Freesound geladen oder aus einem Splice-Pack gezogen hast. Vor einem Release musst du für jede Datei drei Fragen beantworten: Darf sie in ein verkauftes Spiel? Muss jemand genannt werden? Darfst du sie bearbeitet weitergeben? Ohne Notiz von damals beantwortest du keine davon. + + Dieses Modul zeigt, wo die Antworten stehen, zitiert die Texte und baut eine **Quellenliste**: eine Zeile pro Datei, die Herkunft, Lizenz und Nachweis festhält. Welche Klänge du verwendest, entscheidest du. + + > [!NOTE] Kein Rechtsrat + > Dieses Modul ersetzt keine Rechtsberatung. Es gibt die Texte wieder, die gelten, und sagt, wo sie Fragen offen lassen. Creative Commons und Freesound schreiben über ihre eigenen Zusammenfassungen dasselbe. Im Zweifel fragst du den Rechteinhaber und legst die Antwort zu den Nachweisen. + + ## Eigenes Material zuerst + + Am wenigsten Fragen stellen Klänge, die du selbst gemacht hast: Aufnahmen aus [Feldaufnahme mit dem Handy](/sound/feldaufnahme), Foley, eigene Synthese. Auch sie bekommen eine Zeile in der Quellenliste, mit Datum und Verweis aufs Aufnahmeprotokoll. Dann ist jede Datei im Projekt erklärt, die eigenen wie die fremden. + + Warum fremde Klänge fast nie frei sind: § 85 Abs. 1 UrhG gibt dem Hersteller eines Tonträgers „das ausschließliche Recht, den Tonträger zu vervielfältigen, zu verbreiten und öffentlich zugänglich zu machen“ ([§ 85 UrhG](https://www.gesetze-im-internet.de/urhg/__85.html)). Der Wortlaut knüpft an die Aufnahme an, nicht an ein Kunstwerk; die Rechte an der Aufnahme einer Tür liegen also beim Hersteller der Aufnahme, bei eigenen Aufnahmen bei dir. Freesound warnt in seiner FAQ: „‚free‘ is a tricky concept! Free to do what exactly?“ ([Freesound FAQ](https://freesound.org/help/faq/#licenses)). + + ## Creative Commons: vier Bausteine, sechs Lizenzen + + Creative Commons setzt seine Lizenzen aus Bausteinen zusammen: **BY** (Namensnennung), **SA** (Weitergabe unter gleichen Bedingungen), **NC** (nicht kommerziell) und **ND** (keine Bearbeitungen). Daraus entstehen sechs Lizenzen von CC BY bis CC BY-NC-ND ([Über die CC-Lizenzen](https://creativecommons.org/about/cclicenses/)). **CC0** ist keine dieser Lizenzen, sondern ein Verzicht auf alle Rechte, soweit das gesetzlich möglich ist. Freesound fasst CC0 so zusammen: „you can do pretty much what you want with the sound. You could even sell the sound, ... but you can't claim you are the author!“ + + Was die Bausteine für ein verkauftes Spiel heißen, laut den deutschen Kurzfassungen und Lizenztexten: + + | Lizenz | Verkauftes Spiel | Pflicht | + | --- | --- | --- | + | CC0 | ja | keine, Herkunft notieren | + | CC BY | ja | Namensnennung | + | CC BY-SA | ja, Folgen vorher klären | Nennung, gleiche Lizenz für Bearbeitungen | + | CC BY-ND | nein bzw. vorher klären | Nennung; Vertonung gilt als Bearbeitung (1a) | + | alle mit NC | nein | – | + + **BY** verlangt laut [Kurzfassung](https://creativecommons.org/licenses/by/4.0/deed.de): „Sie müssen angemessene Urheber- und Rechteangaben machen, einen Link zur Lizenz beifügen und angeben, ob Änderungen vorgenommen wurden.“ Was genau dazugehört, regelt Abschnitt 3(a) des Lizenztexts. + + **SA** verlangt für Bearbeitungen dieselbe Lizenz. Für Klänge im Spiel ist dabei ein Satz aus Abschnitt 1 der 4.0-Lizenzen wichtig, der für SA und ND gilt: „Im Sinne der vorliegenden Public License entsteht immer abgewandeltes Material, wenn das lizenzierte Material ein Musikwerk, eine Darbietung oder eine Tonaufnahme ist und zur Vertonung von Bewegtbildern verwendet wird“ ([CC BY 4.0, Abschnitt 1a](https://creativecommons.org/licenses/by/4.0/legalcode.de#s1a_adapted_material)). Wie weit „abgewandeltes Material“ in einem Spiel reicht, ist eine Rechtsfrage, die dieser Kurs nicht beantwortet. Bei BY-SA in einem verkauften Spiel klärst du sie vorher oder nimmst einen anderen Klang. + + **NC** definiert der Lizenztext so: „Nicht kommerziell meint nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet“ ([CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode.de#s1i_noncommercial)). Ein Spiel, das du verkaufst, ist auf Vergütung gerichtet. Freesound fasst es knapper: „you can't earn any money with the piece of work you create!“ + + **ND** verbietet, bearbeitete Fassungen zu verbreiten: „Es sei klargestellt, dass Sie gemäß der vorliegenden Public License keine Erlaubnis haben, abgewandeltes Material weiterzugeben“ ([CC BY-ND 4.0, Abschnitt 3a](https://creativecommons.org/licenses/by-nd/4.0/legalcode.de#s3a)). Schneiden, Schichten, Tonhöhe ändern ist Bearbeitung. Und der Satz aus Abschnitt 1a steht auch in dieser Lizenz: Wer eine Tonaufnahme zur Vertonung von Bewegtbildern verwendet, erzeugt immer abgewandeltes Material. Auch ein unbearbeiteter ND-Klang im Spiel kann deshalb als Bearbeitung gelten. Das Creative-Commons-Wiki warnt bei Musik: „Your use of CC-licensed music is often an adaptation!“ Bei BY-ND klärst du die Frage vorher oder nimmst einen anderen Klang. + + Zwei Details gehören in jede Zeile der Quellenliste. Die **Version**: CC BY 3.0 und 4.0 sind verschiedene Texte, auch wenn beide CC BY heißen. Und der **Zeitpunkt**: Die Lizenzen sind laut CC-FAQ unwiderruflich – „once you receive material under a CC license, you will always have the right to use it under those license terms“ ([CC FAQ](https://creativecommons.org/faq/#what-happens-if-the-author-decides-to-revoke-the-cc-license-to-material-i-am-using)). Ändert jemand später die Lizenz seiner Datei, gilt für dich die von damals. Beweisen musst du sie selbst. + + ## Namensnennung nach TASL + + Das Creative-Commons-Wiki empfiehlt, dass eine Namensnennung „the Title, Author, Source, and License“ enthält, kurz **TASL** ([Recommended practices](https://wiki.creativecommons.org/wiki/Recommended_practices_for_attribution)). Für einen Klang von Freesound heißt das: + + ```text + „“ von + freesound.org/s// + Lizenz: CC BY 4.0, + geändert: geschnitten, Tonhöhe + ``` + + `` ist die Adresse des Lizenztexts, bei CC BY 4.0 `https://creativecommons.org/licenses/by/4.0/`. Abschnitt 3(a)(1)(C) verlangt, den Text der Lizenz oder einen Link darauf beizufügen ([CC BY 4.0, Abschnitt 3a](https://creativecommons.org/licenses/by/4.0/legalcode.de#s3a1C)). Der Link zur Quelle ersetzt ihn nicht. + + Zwei Fallen nennt das Wiki unter „Common pitfalls“. Die Plattform zu nennen statt der Person: „crediting the site where you have found the material cannot substitute crediting the author“. Und den echten Namen zu suchen: Genannt wird der Name, den die Person gewählt hat, auch ein Pseudonym. Bei Freesound ist das der Nutzername. + + Wo die Nennung im Spiel steht, entscheidest du. Das Wiki schlägt für Medien wie Audio und Video eine eigene Webseite mit allen Angaben vor, und Freesound zeigt eine kurze Form für wenig Platz: „uses many sounds from freesound, for the full list see here“ mit Link. + + > [!PRAXIS] Credits in einem veröffentlichten Spiel + > Freesound verlinkt in seiner FAQ als Beispiel die Seite [Sound Attributions](https://www.minecraft.net/en-us/attribution/sound) von Minecraft. Dort stehen im Oktober 2026 rund 200 Einträge, je einer pro Klang: Titel in Anführungszeichen, „by“ und der Nutzername, darunter die Lizenz mit Version, meist CC BY 3.0 oder CC0 1.0. Auch die CC0-Klänge sind aufgeführt, obwohl CC0 keine Nennung verlangt. So arbeitet auch eine Quellenliste: Jede Datei ist erklärt, auch die ohne Pflicht. + + ## Freesound: eine Lizenz pro Datei + + Auf Freesound wählt jede Person die Lizenz für jeden Klang selbst: CC0, CC BY 4.0 oder CC BY-NC 4.0. Ältere Klänge tragen noch die zurückgezogene Lizenz Sampling+, die Freesound in derselben FAQ so liest: „you can do pretty much what you could do with the attribution noncommercial license“. Freesound fügt hinzu: „We aren't lawyers so this isn't legal advice“. Lies deshalb die Lizenz auf der Seite jeder einzelnen Datei, nie die eines Packs oder Nutzers. + + Wer Klänge anderer bearbeitet und neu hochlädt, ist an deren Lizenz gebunden. Die FAQ hat dafür eine Tabelle: Aus einem CC-BY-Klang darf kein CC0-Klang werden, aus einem BY-NC-Klang kein BY-Klang. Wenn du einen Klang nutzt, den jemand aus einem fremden BY-Klang gebaut hat, nennst du laut FAQ beide. + + Freesound erlaubt auch KI-generierte Klänge: „AI generated audio/sounds created with generative AI models is permitted as long as it is marked with the GenAI tag and the model is indicated in the description.“ Solche Dateien kommen nicht in dein Projekt; prüf Tags und Beschreibung, bevor du lädst. + + ## Sonniss, Splice und Library-EULAs + + Außerhalb von Creative Commons hat jeder Anbieter seinen eigenen Text. Du liest ihn auf drei Fragen hin: Darf der Klang in ein Spiel? Muss jemand genannt werden? Was darfst du mit der Datei selbst nicht tun? + + ### Sonniss GDC Bundle + + Die Lizenz der jährlichen #GameAudioGDC-Bundles liegt im Oktober 2026 in Version 2.0 vor, gültig seit dem 27. August 2026 ([GDC Bundle License](https://sonniss.com/gdc-bundle-license/)). Sie erlaubt die Nutzung „for personal and commercial projects without attribution to the original creator“, synchronisiert im eigenen Projekt, und nennt Spiele ausdrücklich. Verboten ist, die Klänge als Klänge weiterzugeben, „whether the sound effects are unmodified, modified or re-designed“, auch als Teil einer Library, eines Asset-Packs oder einer Projektvorlage: „Re-designed and manipulated sounds remain licensed material under this Agreement.“ Bevor du ein Projekt mit solchen Dateien öffentlich machst, etwa als Repository oder Vorlage, prüfst du diesen Absatz. + + Für die Quellenliste zählt ein weiterer Absatz. Wer vor dem 27. August 2026 geladen hat, für den gilt „the version of this Agreement that was published on the day you downloaded“. Eine Kopie der Lizenz im Download entscheidet nicht, wenn sie von der veröffentlichten abweicht. Ohne Abrufdatum weißt du also nicht, welche Fassung für deine Datei gilt. + + ### Splice + + Die Nutzungsbedingungen von Splice (Stand 24. Juli 2026) erlauben in Abschnitt 3.1.1.2 Klänge in „video games, film or television projects“, auch „used either in isolation as sound effects, loops, and/or sound mixes“ ([Splice Terms](https://splice.com/terms)). Abschnitt 3.1.1.3 verbietet unter anderem, die Klänge „in isolation as sound effects, loops, or as source material for any other form of sample (even if you modify the Sounds)“ weiterzulizenzieren und sie „in new sample packs“ weiterzugeben. Im Spiel darf ein Splice-Klang also allein als Effekt laufen; weitergeben, damit andere ihn nutzen, darfst du ihn nicht. + + Anders als bei CC BY nennst du bei Splice niemanden ohne dessen Erlaubnis. Derselbe Abschnitt verbietet, den Namen eines Künstlers, der mit einem Klang verbunden ist, ohne dessen schriftliche Erlaubnis zu verwenden, ausdrücklich auch als Mitwirkenden. Als Nachweis bietet Splice eine „Certified License“ pro Klang an (Abschnitt 3.1.1); die legst du zu den Nachweisen. + + ### Native Instruments und andere Libraries + + Die EULA von Native Instruments (Fassung vom 1. Juli 2026) erlaubt in Abschnitt 3.7 Samples, Instrumente und Presets „for commercial or non-commercial music and audio productions“ ([NI EULA](https://www.native-instruments.com/en/eula/#gmbh)). Danach: „Individual samples, sound sets or audio loops may not be distributed (commercially or otherwise) standalone. Furthermore these samples, sound sets or audio may not be repackaged in whole or in part as audio samples, sound libraries or sound effects.“ + + Für Musik aus Kontakt ist das klar: ein Stück ist eine Produktion. Für einen einzelnen Effekt, den du aus einem Kontakt-Instrument renderst und als WAV ins Spiel legst, ist es offen: Ist das eine Produktion oder ein Sample, das als Soundeffekt weitergegeben wird? Der Text beantwortet das nicht. Frag beim Hersteller nach und leg die Antwort als Nachweis ab. Libraries anderer Hersteller, die in Kontakt laufen, haben eigene Lizenztexte; jeder wird einzeln gelesen. + + > [!MERKE] + > Eine Lizenz gilt für eine Datei und in der Fassung, unter der du sie bekommen hast. Deshalb stehen Datei, Lizenz mit Version, Abrufdatum und Nachweis in jeder Zeile. + + ## Die Quellenliste: eine Zeile pro Datei + + Die Quellenliste ist eine CSV-Datei neben deinen Audiodateien, zum Beispiel `quellen.csv`, eine Zeile pro Datei im Projekt. Die Spalten: + + | Spalte | Inhalt | + | --- | --- | + | datei | Dateiname im Projekt | + | herkunft | eigen, Freesound, Sonniss … | + | autor | Name, wie er genannt werden will | + | lizenz | mit Version, etwa CC BY 4.0 | + | url | Seite der einzelnen Datei | + | abgerufen | Datum des Downloads | + | nachweis | Pfad zur gesicherten Lizenzseite | + | aenderung | was du geändert hast | + | credit | fertiger Text für die Credits | + + Eine Zeile, untereinander geschrieben: + + ```text + datei: amb_regen_01.wav + herkunft: Freesound + autor: + lizenz: CC BY 4.0 + url: freesound.org/s// + abgerufen: 2026-10-20 + nachweis: nachweise/regen_01.pdf + aenderung: geschnitten + credit: + „“ von , + freesound.org/s//, + CC BY 4.0, , + geschnitten + ``` + + Den Nachweis sicherst du beim Download: die Seite der Datei mit Lizenz als PDF, die Certified License von Splice, die Lizenzseite von Sonniss am Tag des Downloads. Ein Skript prüft die Liste vor jedem Release auf Lücken. Das Modell unten kennt nur die Regeln aus diesem Modul: + + ```python run + PFLICHT = ("datei", "herkunft", + "lizenz", "abgerufen", + "nachweis") + BY_ZUSATZ = ("autor", "url", + "credit") + + + def pruefe(z): + liz = z.get("lizenz", "") + felder = PFLICHT + if "BY" in liz: + felder += BY_ZUSATZ + m = [] # Meldungen + for f in felder: + if not z.get(f): + m += ["fehlt " + f] + if "NC" in liz: + m += ["NC: kein Verkauf"] + if "ND" in liz: + m += ["ND: vorher klären"] + return m or ["ok"] + + + klick = dict( + datei="ui_klick_01.wav", + herkunft="eigen", + lizenz="eigen", + abgerufen="2026-10-24", + nachweis="protokoll.txt", + ) + regen = dict( + datei="amb_regen_01.wav", + herkunft="Freesound", + autor="nutzername", + lizenz="CC BY 4.0", + url="freesound.org/s//", + abgerufen="2026-10-20", + nachweis="regen_01.pdf", + aenderung="geschnitten", + credit="", + ) + holz = dict( + datei="hit_holz_01.wav", + herkunft="Freesound", + autor="nutzer2", + lizenz="CC BY-NC 4.0", + url="freesound.org/s//", + abgerufen="2026-10-20", + nachweis="", + credit="", + ) + + for z in [klick, regen, holz]: + print(z["datei"]) + for meldung in pruefe(z): + print("-", meldung) + ``` + + Das Skript ersetzt kein Lesen. Es findet nur, was fehlt oder einen Baustein trägt, den du für ein verkauftes Spiel ausschließt oder vorher klärst. Ob eine EULA dein Vorhaben erlaubt, steht in keiner Spalte. + + ### Die KI-Regel in der Quellenliste + + Jeder Klang im Projekt ist deiner oder sauber lizenziert. KI-generierte Klänge oder Musik gehören in keine Zeile, auch nicht als Platzhalter. Das betrifft Dateien mit GenAI-Tag auf Freesound ebenso wie KI-Werkzeuge, die Anbieter neben ihren Bibliotheken anbieten; Splice beschreibt sein „Create Tool“ als „AI-assisted music creation technology“. Umgekehrt verbieten Sonniss, Splice und Native Instruments, ihre Klänge zum Training von KI zu verwenden. Lade lizenzierte Klänge deshalb nicht in Dienste, die damit trainieren könnten. + + KI darf dir Technik abnehmen: ein Prüfskript wie das oben, Spalten umbenennen, eine CSV aus deinen Notizen formatieren. Die Einträge und die Entscheidungen bleiben deine. + + > [!INTERVIEW] + > „Kurz vor dem Release fragt die Produzentin, ob alle Klänge sauber lizenziert sind. Was antwortest du?“ Gute Antwort, etwa als Audio Designer: Ich zeige die Quellenliste. Jede Datei im Projekt hat eine Zeile mit Herkunft, Lizenz mit Version, Abrufdatum und Nachweis, auch die eigenen Aufnahmen. Ein Skript prüft vor jedem Build auf Lücken und auf NC oder ND. Die Credit-Texte stehen fertig in der Liste. Fälle, die ein Lizenztext offen lässt, habe ich beim Hersteller angefragt und die Antwort abgelegt; bis dahin sind sie markiert und nicht im Build. + + ## Häufige Fehler + + - **Plattform statt Person genannt.** „Klänge: Freesound“ ist keine Namensnennung. Titel, Autor, Quelle, Lizenz. + - **Lizenz ohne Version.** „CC BY“ reicht nicht; 3.0 und 4.0 sind verschiedene Texte. + - **Kein Abrufdatum.** Bei Sonniss entscheidet es, welche Fassung gilt; bei CC beweist es, was du bekommen hast. + - **NC im verkauften Spiel.** Nicht vorrangig auf Vergütung gerichtet – ein Verkauf ist es. + - **ND unterschätzt.** Schneiden und Pitchen ist Bearbeitung, und schon die Vertonung zählt laut Abschnitt 1a als abgewandeltes Material. Eine ND-Datei kommt nur nach Klärung ins Spiel. + - **Pack-Lizenz statt Datei-Lizenz.** Auf Freesound hat jede Datei ihre eigene. + - **Rohdateien veröffentlicht.** Sonniss, Splice und NI verbieten die Weitergabe der Klänge als Klänge, auch bearbeitet. + - **KI-Klang als Platzhalter.** Platzhalter landen im Release. Nimm einen eigenen Prüfklick. + + ## Zusammenfassung + + - Eigene Aufnahmen zuerst; auch sie bekommen eine Zeile in der Quellenliste. + - CC0 und CC BY passen laut Text zu einem verkauften Spiel, alles mit NC nicht. BY-SA und BY-ND klärst du vorher, weil Vertonung laut Abschnitt 1a immer abgewandeltes Material erzeugt. + - Namensnennung nach TASL: Titel, Autor mit gewähltem Namen, Quelle mit Link, Lizenz mit Version und Link, dazu die Änderungen. + - Freesound: Lizenz pro Datei lesen; Dateien mit GenAI-Tag bleiben draußen. + - Sonniss und Splice erlauben Spiele ohne Namensnennung und verbieten die Weitergabe der Klänge selbst; bei Splice nennst du ohne Erlaubnis niemanden. + - Die NI-EULA erlaubt Musik- und Audioproduktionen und verbietet einzelne Samples als Soundeffekte weiterzugeben; einzelne gerenderte Effekte klärst du mit dem Hersteller. + - Jede Zeile: Datei, Herkunft, Autor, Lizenz mit Version, URL, Abrufdatum, Nachweis, Änderungen, Credit. + - Kein KI-generiertes Material im Projekt; KI darf Skripte und Formatierung liefern. +exercises: + - id: was-darf-ins-spiel + type: choice + title: Was passt zu einem verkauften Spiel? + prompt: | + Du verkaufst dein Spiel. Bei welchen Dateien erlaubt der Lizenztext die Nutzung im Spiel, so wie beschrieben? Mehrere Antworten richtig. + options: + - text: Ein CC0-Klang von Freesound, geschnitten und in der Tonhöhe verändert. + correct: true + why: Laut CC0-Kurzfassung darfst du das Werk kopieren, verändern und verbreiten, „sogar zu kommerziellen Zwecken“. Die Herkunft notierst du trotzdem. + - text: Ein CC-BY-4.0-Klang, bearbeitet, mit TASL-Nennung und dem Hinweis auf die Änderung. + correct: true + why: BY erlaubt kommerzielle Nutzung und Bearbeitung, solange die Nennung mit Link zur Lizenz und Änderungshinweis da ist. + - text: Ein CC-BY-NC-4.0-Klang mit vollständiger Nennung. + correct: false + why: NC heißt „nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet“. Ein Verkauf ist auf Vergütung gerichtet; die Nennung ändert daran nichts. + - text: Ein CC-BY-ND-4.0-Klang, den du mit zwei anderen Klängen schichtest. + correct: false + why: Schichten ist Bearbeitung, und laut Abschnitt 1a gilt schon die Vertonung als Bearbeitung. ND verbietet, abgewandeltes Material weiterzugeben, und ein Spiel gibt es weiter. + - text: Ein Klang aus dem Sonniss-GDC-Bundle in einem eigenen Klang, ohne Namensnennung. + correct: true + why: Die Bundle-Lizenz erlaubt kommerzielle Projekte „without attribution to the original creator“, synchronisiert im eigenen Projekt. + explanation: | + Entscheidend sind zwei Bausteine: NC schließt den Verkauf aus, ND die Bearbeitung, und als Bearbeitung zählt schon die Vertonung. BY verlangt nur eine korrekte Nennung, CC0 nichts. Sonniss steht außerhalb von Creative Commons und erlaubt Spiele ohne Nennung, verbietet aber die Weitergabe der Klänge selbst. Ob ein Fall passt, hängt am Text der einzelnen Datei; diese Übung prüft das Lesen, keinen Rechtsrat. + resources: + - title: Creative Commons · Über die CC-Lizenzen + url: https://creativecommons.org/about/cclicenses/ + - title: Creative Commons · CC0 1.0, Kurzfassung + url: https://creativecommons.org/publicdomain/zero/1.0/deed.de + - title: Sonniss · GDC Game Audio Bundle License + url: https://sonniss.com/gdc-bundle-license/ + + - id: was-die-texte-verbieten + type: choice + title: Was die Texte ausdrücklich verbieten + prompt: | + Welche Handlungen verbieten die zitierten Texte von Splice und Native Instruments ausdrücklich? Mehrere Antworten richtig. + options: + - text: Splice-Klänge, auch bearbeitet, in einem eigenen Sample-Pack weitergeben. + correct: true + why: Abschnitt 3.1.1.3 verbietet, Klänge „in new sample packs“ weiterzugeben und sie als Ausgangsmaterial für andere Samples weiterzulizenzieren, „even if you modify the Sounds“. + - text: Einen Splice-Klang im Spiel allein als Soundeffekt abspielen. + correct: false + why: Abschnitt 3.1.1.2 erlaubt Videospiele ausdrücklich, auch „used either in isolation as sound effects“. + - text: Den Künstler eines Splice-Samples ohne schriftliche Erlaubnis in den Credits nennen. + correct: true + why: Splice verbietet, den Namen eines mit einem Klang verbundenen Künstlers ohne schriftliche Erlaubnis zu verwenden, ausdrücklich auch als Mitwirkenden. + - text: Einzelne Samples aus einer NI-Library als eigene Sound-Library weitergeben. + correct: true + why: Abschnitt 3.7 verbietet, Samples „standalone“ zu verbreiten oder als „audio samples, sound libraries or sound effects“ neu zu verpacken. + - text: Mit einem Kontakt-Instrument ein Musikstück für ein verkauftes Spiel produzieren. + correct: false + why: Abschnitt 3.7 erlaubt Samples, Instrumente und Presets „for commercial or non-commercial music and audio productions“. + explanation: | + Beide Texte trennen die Nutzung im eigenen Werk von der Weitergabe des Materials. Im Spiel oder Stück darf es klingen; als Datei, die andere wieder als Klang benutzen, darf es nicht weitergehen, auch bearbeitet nicht. Bei Splice kommt hinzu, dass eine gut gemeinte Nennung ohne Erlaubnis gegen die Bedingungen verstößt, das Gegenteil von CC BY. + resources: + - title: Splice · Terms of Use, Abschnitt 3.1.1 + url: https://splice.com/terms + - title: Native Instruments · EULA, Abschnitt 3.7 + url: https://www.native-instruments.com/en/eula/#gmbh + + - id: wen-nennen + type: choice + title: Wen nennst du? + prompt: | + Auf Freesound hat B einen Klang aus einem CC-BY-Klang von A gebaut und unter CC BY 4.0 hochgeladen. Du nutzt den Klang von B in deinem Spiel. Wen musst du laut Freesound-FAQ nennen? + lang: text + code: | + A CC BY → Klang a + B CC BY → Klang b (aus a gebaut) + du → nutzt b im Spiel + options: + - text: Nur A, weil von A das Ausgangsmaterial stammt. + correct: false + why: B hat den Klang bearbeitet und selbst unter CC BY hochgeladen. Auch B ist Lizenzgeber und verlangt eine Nennung. + - text: Nur B, weil du den Klang von B geladen hast. + correct: false + why: Die Nennung von A wandert mit dem Material. Die FAQ sagt ausdrücklich, dass du beide nennst. + - text: A und B. + correct: true + why: Laut FAQ nennt, wer den Klang von B nutzt, „both A and B“. + - text: Niemanden, weil B einen neuen Klang gebaut hat. + correct: false + why: Der neue Klang steht unter CC BY, und er bleibt an die Lizenz von A gebunden. Beides verlangt eine Nennung. + explanation: | + Die FAQ sagt: B muss A nennen, und wer den Klang von B nutzt, nennt „both A and B“. Die Namensnennung wandert mit dem Material. In deiner Credit-Zeile stehen also zwei Autoren mit ihren Klängen und Links. Steht auf der Seite von B kein Hinweis auf A, fragst du nach, bevor du den Klang nutzt. + resources: + - title: Freesound · FAQ, License restrictions + url: https://freesound.org/help/faq/#license-restrictions-when-publishing-new-sounds-that-includemodifyremix-other-sounds + + - id: bevor-datei-ins-projekt + type: order + title: Bevor eine fremde Datei ins Projekt kommt + prompt: | + Bring die Schritte in die Reihenfolge, in der du sie für eine fremde Datei erledigst. + lang: prose + lines: | + Die Lizenz auf der Seite der einzelnen Datei lesen, mit Version + Prüfen, ob sie zu Verkauf und Bearbeitung passt + Herunterladen, Abrufdatum notieren und die Lizenzseite als Nachweis sichern + Eine Zeile in der Quellenliste anlegen, mit fertigem Credit-Text + Die Datei bearbeiten und die Änderungen in der Zeile nachtragen + explanation: | + Erst lesen, dann laden: Passt die Lizenz nicht, sparst du dir Download und Aufräumen. Der Nachweis entsteht im selben Moment wie der Download, denn er soll zeigen, was an diesem Tag galt. Die Zeile kommt vor der Bearbeitung, damit keine Datei ohne Herkunft im Projekt liegt; die Änderungen trägst du nach, weil CC BY und viele Credits sie verlangen. + resources: + - title: Creative Commons FAQ · Unwiderruflichkeit + url: https://creativecommons.org/faq/#what-happens-if-the-author-decides-to-revoke-the-cc-license-to-material-i-am-using + + - id: pruefskript-ausgabe + type: output + title: Was meldet das Prüfskript? + prompt: | + Das Prüfskript aus der Lektion mit drei neuen Zeilen. Was gibt es aus? + lang: python + code: | + PFLICHT = ("datei", "herkunft", + "lizenz", "abgerufen", + "nachweis") + BY_ZUSATZ = ("autor", "url", + "credit") + + + def pruefe(z): + liz = z.get("lizenz", "") + felder = PFLICHT + if "BY" in liz: + felder += BY_ZUSATZ + m = [] # Meldungen + for f in felder: + if not z.get(f): + m += ["fehlt " + f] + if "NC" in liz: + m += ["NC: kein Verkauf"] + if "ND" in liz: + m += ["ND: vorher klären"] + return m or ["ok"] + + + wind = dict( + datei="amb_wind_01.wav", + herkunft="Freesound", + lizenz="CC0 1.0", + abgerufen="2026-10-21", + nachweis="wind_01.pdf", + ) + metall = dict( + datei="hit_metall_02.wav", + herkunft="Webseite", + autor="nutzer3", + lizenz="CC BY-ND 4.0", + url="", + abgerufen="2026-10-21", + nachweis="metall_02.pdf", + aenderung="Tonhöhe -2", + ) + whoosh = dict( + datei="whoosh_01.wav", + herkunft="Sonniss", + lizenz="Sonniss 2.0", + abgerufen="", + nachweis="sonniss.pdf", + ) + + for z in [wind, metall, whoosh]: + print(z["datei"]) + for meldung in pruefe(z): + print("-", meldung) + expected: | + amb_wind_01.wav + - ok + hit_metall_02.wav + - fehlt credit + - ND: vorher klären + whoosh_01.wav + - fehlt abgerufen + hints: + - CC0 enthält kein „BY“, also gelten nur die fünf Pflichtfelder. + - Bei der zweiten Zeile prüft das Skript erst die Felder, dann NC, dann ND. + explanation: | + Die CC0-Datei hat alle Pflichtfelder und braucht weder Autor noch Credit. Die ND-Datei trägt „BY“ im Namen ihrer Lizenz und braucht deshalb einen Credit-Text, der fehlt; dazu kommt die ND-Meldung, weil schon die Vertonung als Bearbeitung zählt. Die Sonniss-Zeile hat einen Nachweis, aber kein Abrufdatum. Gerade dort zählt es, weil die Fassung vom Tag des Downloads gilt. Das Skript zeigt Lücken; ob es die richtigen Regeln kennt, prüfst du selbst. + + - id: tasl-buchstaben + type: gap + title: Wofür TASL steht + prompt: | + Ergänze die vier Teile einer Namensnennung nach dem Creative-Commons-Wiki, auf Deutsch. + lang: prose + code: | + T steht für ⟦Titel⟧, A für ⟦Autor⟧, S für ⟦Quelle⟧ mit Link und L für ⟦Lizenz⟧ mit Version. + gaps: + - label: T + hint: Wie heißt der Klang? + accept: ["Title"] + - label: A + hint: Wer hat ihn gemacht, mit gewähltem Namen? + accept: ["Author", "Autorin", "Urheber", "Urheberin", "Urheberschaft"] + - label: S + hint: Wo findet man ihn? + accept: ["Source", "Herkunft", "Fundort", "Link", "URL"] + - label: L + hint: Unter welchen Bedingungen darfst du ihn nutzen? + accept: ["License", "Licence"] + explanation: | + Titel, Autor, Quelle, Lizenz – das Wiki empfiehlt alle vier, wo immer der Lizenzgeber sie angibt. Der Titel ist bei 4.0 nicht mehr Pflicht, bei 3.0 und älter schon. Der Autor ist der Name, den die Person gewählt hat, die Quelle die Seite der einzelnen Datei, die Lizenz steht mit Version und Link. Dazu kommt bei CC BY der Hinweis auf Änderungen. + resources: + - title: Creative Commons Wiki · Recommended practices for attribution + url: https://wiki.creativecommons.org/wiki/Recommended_practices_for_attribution + + - id: remix-tabelle + type: gap + title: Was aus einem fremden Klang werden darf + prompt: | + Person B baut aus dem Freesound-Klang von Person A einen neuen Klang und will ihn unter einer Lizenz hochladen. Ergänze nach der Tabelle in der Freesound-FAQ, ob das geht: `ja` oder `nein`. + lang: text + code: | + A: cc0 → B: by ⟦ja⟧ + A: by → B: cc0 ⟦nein⟧ + A: by → B: by-nc ⟦ja⟧ + A: by-nc → B: by ⟦nein⟧ + A: by-nc → B: cc0 ⟦nein⟧ + gaps: + - label: cc0 zu by + hint: CC0 verlangt nichts. + accept: ["Ja", "JA"] + - label: by zu cc0 + hint: CC0 hieße, auf die Nennung von A zu verzichten. + accept: ["Nein", "NEIN"] + - label: by zu by-nc + hint: Strenger darf B sein, solange A genannt wird. + accept: ["Ja", "JA"] + - label: by-nc zu by + hint: by würde kommerzielle Nutzung erlauben. + accept: ["Nein", "NEIN"] + - label: by-nc zu cc0 + hint: Beides fällt weg, NC und Nennung. + accept: ["Nein", "NEIN"] + explanation: | + Die Regel hinter der Tabelle: B darf nur Bedingungen hinzufügen, keine wegnehmen. Aus CC0 darf alles werden. Aus BY wird kein CC0, weil die Nennung von A wegfiele; aus BY-NC kein BY und kein CC0, weil der Ausschluss kommerzieller Nutzung wegfiele. Für dich als Nutzer heißt das: Die Lizenz eines bearbeiteten Klangs sagt nur etwas, wenn B sich an die Tabelle gehalten hat. Prüf bei Remixen auch die Quelle. + resources: + - title: Freesound · FAQ, License restrictions + url: https://freesound.org/help/faq/#license-restrictions-when-publishing-new-sounds-that-includemodifyremix-other-sounds + + - id: credit-zeile + type: bug + title: Die Credit-Zeile mit einer Lücke + prompt: | + Die Credits deines Spiels für einen bearbeiteten CC-BY-4.0-Klang. Die Person, die den Klang aufgenommen hat, schreibt dir: Ihr Name fehlt. Finde die Zeile. + lang: text + code: | + Credits – Klänge + „“ von Freesound + freesound.org/s// + Lizenz: CC BY 4.0, + geändert: geschnitten + lines: [2] + explanation: | + Zeile 2 nennt die Plattform statt der Person. Das Creative-Commons-Wiki führt das als Falle: Die Seite, auf der du den Klang gefunden hast, ersetzt nicht die Nennung des Autors. Richtig wäre „„“ von “, mit dem Namen, den die Person auf Freesound gewählt hat. Quelle mit Link, Lizenz mit Version und Link und der Änderungshinweis sind vorhanden. + resources: + - title: Creative Commons Wiki · Common pitfalls + url: https://wiki.creativecommons.org/wiki/Recommended_practices_for_attribution#Common_pitfalls + + - id: sonniss-zeile + type: bug + title: Welche Fassung gilt? + prompt: | + Eine Zeile deiner Quellenliste, untereinander geschrieben. Bei der Prüfung vor dem Release lässt sich für diese Datei nicht belegen, welche Fassung der Sonniss-Lizenz gilt. Finde die Zeile, die das verursacht. + lang: text + code: | + datei: whoosh_luft_01.wav + herkunft: Sonniss GDC Bundle + lizenz: GDC Bundle License 2.0 + abgerufen: – + nachweis: nachweise/sonniss.pdf + aenderung: Tonhöhe -3, geschnitten + credit: – + lines: [4] + explanation: | + Fassung 2.0 gilt laut Sonniss für Downloads ab dem 27. August 2026. Wer vorher geladen hat, für den gilt die Fassung, die am Tag des Downloads veröffentlicht war. Die Zeile nennt Version 2.0 und einen Nachweis, aber ob 2.0 beim Download schon galt, zeigt nur das Abrufdatum, und das fehlt. Der leere Credit ist kein Fehler, die Bundle-Lizenz verlangt keine Nennung. + resources: + - title: Sonniss · GDC Bundle License, Which version applies + url: https://sonniss.com/gdc-bundle-license/ + + - id: abrufdatum-erklaeren + type: explain + title: Warum das Datum in jede Zeile gehört + prompt: | + Ein Teammitglied hält die Spalte `abgerufen` für überflüssig: „Die Lizenz steht doch auf der Seite.“ Erkläre mit Creative Commons und Sonniss, warum das Datum trotzdem zählt. + lang: prose + code: | + CC FAQ: „once you receive material under a CC license, you will always have the right to use it under those license terms“ + Sonniss: „the version of this Agreement that was published on the day you downloaded is the version that governs your use of those files“ + points: + - Seiten ändern sich; die Lizenz, die heute dort steht, muss nicht die vom Tag des Downloads sein. + - Bei CC gilt die Lizenz, unter der du das Material bekommen hast, auch wenn der Autor sie später ändert. + - Bei Sonniss bestimmt der Tag des Downloads, welche Fassung des Vertrags gilt. + - Das Datum allein beweist nichts; zusammen mit dem gesicherten Nachweis zeigt es, was damals galt. + explanation: | + Die Seite zeigt den Stand von heute. Creative Commons sagt, dass du die Lizenz behältst, unter der du das Material bekommen hast, selbst wenn die Person sie später ändert; dafür musst du wissen, wann das war und was dort stand. Sonniss macht den Tag des Downloads ausdrücklich zur Regel für die Fassung. Abrufdatum und Nachweis gehören deshalb zusammen: Das Datum sagt, welcher Stand zählt, der Nachweis zeigt ihn. + resources: + - title: Creative Commons FAQ · Unwiderruflichkeit + url: https://creativecommons.org/faq/#what-happens-if-the-author-decides-to-revoke-the-cc-license-to-material-i-am-using + - title: Sonniss · GDC Game Audio Bundle License + url: https://sonniss.com/gdc-bundle-license/ + + - id: eula-offen + type: explain + title: Ein Kontakt-Effekt im Spiel + prompt: | + Du renderst aus einem Kontakt-Instrument einen kurzen Effekt und willst ihn als WAV ins Spiel legen. Erkläre anhand des Auszugs aus Abschnitt 3.7 der NI-EULA, was klar ist, was offen bleibt und was du tust. + lang: prose + code: | + The provided samples, instruments and presets can be used for commercial or non-commercial music and audio productions […]. Individual samples, sound sets or audio loops may not be distributed (commercially or otherwise) standalone. Furthermore these samples, sound sets or audio may not be repackaged in whole or in part as audio samples, sound libraries or sound effects. + points: + - Klar erlaubt sind Musik- und Audioproduktionen, auch kommerziell. + - Klar verboten ist, Samples einzeln oder als Library oder Soundeffekte weiterzugeben. + - Offen ist, ob ein einzelner gerenderter Effekt im Spiel eine Produktion ist oder ein weitergegebener Soundeffekt. + - Du fragst beim Hersteller nach, legst die Antwort als Nachweis ab und markierst die Datei bis dahin in der Quellenliste. + explanation: | + Ein Stück, das mit Kontakt entstanden ist, ist eine Produktion und fällt unter die Erlaubnis. Ein Ordner mit einzelnen Samples fällt klar unter das Verbot. Ein einzelner Effekt, der als WAV im Spielpaket liegt, steht dazwischen: Er ist Teil des Spiels, aber er ist auch eine einzelne Datei, die man herausholen kann. Der Text entscheidet das nicht, also entscheidest du es nicht allein. Eine schriftliche Antwort des Herstellers ist der Nachweis; bis sie da ist, bleibt die Datei markiert. + resources: + - title: Native Instruments · EULA, Abschnitt 3.7 + url: https://www.native-instruments.com/en/eula/#gmbh + + - id: eigene-quellenliste + type: practice + title: Deine Quellenliste beginnen + prompt: | + Leg deine Quellenliste an, am Handy reicht eine Notiz. Trag mindestens drei Zeilen ein: deine Aufnahmen aus [Feldaufnahme mit dem Handy](/sound/feldaufnahme) und eine fremde Datei, die du verwenden möchtest oder schon verwendest. Welche Dateien, entscheidest du. + lang: prose + hints: + - Spalten aus der Lektion, untereinander geschrieben, sind am Handy gut lesbar. + - Für eine Datei, die du noch nicht geladen hast, fehlt das Abrufdatum. Das ist der Moment, den Nachweis zu sichern. + checklist: + - Jede Zeile hat Datei, Herkunft und Lizenz; das Abrufdatum steht da oder ist als offen markiert. + - Die Lizenz steht mit Version, oder bei eigenem Material steht „eigen“ mit Verweis aufs Protokoll. + - Für die fremde Datei hast du notiert, wo der Nachweis liegt oder dass du ihn noch sichern musst. + - Bei einer CC-BY-Datei steht ein fertiger Credit-Text nach TASL in der Zeile. + - Keine Zeile enthält eine KI-generierte Datei. + explanation: | + Die Liste wächst mit dem Projekt. Wer sie ab der ersten Datei führt, muss vor dem Release nichts rekonstruieren. Eigene Aufnahmen gehören hinein, damit jede Datei im Projekt erklärt ist. Wie du die Liste später als CSV neben die Audiodateien legst und prüfst, zeigt das Skript in der Lektion; in der [Werkstatt](/sound/werkstatt-audioabschnitt) ist sie Teil der Abgabe. + resources: + - title: Freesound · FAQ, How do I credit + url: https://freesound.org/help/faq/#how-do-i-creditattribute diff --git a/content/sound/modules/reaper-werkbank.yaml b/content/sound/modules/reaper-werkbank.yaml new file mode 100644 index 0000000..7028685 --- /dev/null +++ b/content/sound/modules/reaper-werkbank.yaml @@ -0,0 +1,545 @@ +id: reaper-werkbank +title: REAPER als Werkbank für Spielgeräusche +level: 1 +minutes: 130 +summary: Wie du in REAPER aus einer langen Handy-Aufnahme viele kurze, saubere und gleich benannte Dateien machst – Projekt für Geräusche, Schnitt und Fades, Säubern mit Bordmitteln, je Klang eine Region und ein Render, den du nach jeder Änderung mit einem Klick wiederholst. +place: pc +goals: + - Du legst ein REAPER-Projekt für Geräusche an und begründest Abtastrate und Bittiefe mit dem, was Godot 4.7.2 beim Import daraus macht. + - Du schneidest Takes mit Fades und Nulldurchgängen und misst die Stille vor der Transiente. + - Du säuberst Aufnahmen mit Hochpass, ReaFIR-Rauschprofil und ReaGate in einer begründeten Reihenfolge. + - Du legst je Klang eine Region an, renderst alle über die Region Render Matrix und sagst aus Wildcards die Dateinamen voraus. + - Du findest Render-Einstellungen, die Hallfahnen abschneiden oder Godot eine alte Fassung spielen lassen, und speicherst deine eigenen als Preset. +resources: + - title: REAPER User Guide 7.81 · Rendering Regions (S. 419) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=419 + - title: REAPER Effects Guide · ReaFir Subtract Mode (S. 22) + url: https://www.reaper.fm/userguide/REAPEREffectsGuide2021.pdf#page=22 + - title: Javier Zumer · REAPER for Game Audio – Getting Started & Rendering (A Sound Effect) + url: https://www.asoundeffect.com/reaper-for-game-audio-getting-started-rendering/ + - title: Godot Docs · Importing audio samples, Best practices + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices +lesson: | + Auf dem Handy liegt eine Aufnahme von sieben Minuten: eine Ansage, 30 Sekunden Room Tone, dann zwanzig Klatscher, dazwischen Rascheln und ein vorbeifahrendes Auto. Für das Spiel brauchst du daraus zwanzig Dateien, jede kurz, ohne Stille am Anfang, ohne Rauschen, nach einem Schema benannt. Und wenn du morgen den Hochpass änderst, willst du alle zwanzig neu haben, ohne zwanzigmal zu rendern. + + Aus der Musik kennst du REAPER als Ort, an dem ein Stück entsteht und am Ende als eine Datei herauskommt. Im Spiel kommt hinzu: Aus einem Projekt werden viele kurze Dateien, jede mit einem festen Namen, unter dem die Engine sie findet. Dieses Modul baut REAPER zu einer Werkbank dafür um. Als Material nimmst du deine Aufnahmen aus [Feldaufnahme mit dem Handy](/sound/feldaufnahme); zum Üben reicht ein Take mit zwanzig Klatschern als Prüfmuster. Alle Werte hier sind Platzhalter. Was gut klingt, entscheidest du. + + > [!NOTE] Seitenangaben + > „S. 128“ meint die PDF-Seite im [REAPER User Guide 7.81](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf), „Effects Guide, S. 17“ die Seite im [REAPER Effects Guide](https://www.reaper.fm/userguide/REAPEREffectsGuide2021.pdf). Menünamen stehen englisch, wie REAPER sie zeigt. + + ## Ein Projekt für Geräusche: Abtastrate, Bittiefe, Ordner + + Unter *File → Project Settings* (Alt+Enter) stehen die *Project Sample Rate*, die Zeitbasis und auf der Seite *Media* das Aufnahmeformat mit Bittiefe sowie das Format für alles, was REAPER beim Anwenden von FX oder beim Glue neu schreibt (S. 32). Auf derselben Seite legst du fest, dass importierte Dateien in den Projektordner kopiert werden. Javier Zumer stellt für Geräusche die Zeitbasis auf *Time* statt *Beats*: „Since I don't usually work with music, I have the timebases set to ‚Time‘ instead of ‚Beat.‘“ ([A Sound Effect](https://www.asoundeffect.com/reaper-for-game-audio-getting-started-rendering/)). + + Welche Abtastrate und Bittiefe? Die Antwort hängt daran, was Godot mit deiner Datei macht. Gemessen mit Godot 4.7.2 (Import headless, Datei danach geladen): + + | Datei | Import | Ergebnis in Godot | + | --- | --- | --- | + | WAV 24 Bit, 48 kHz | Standard | Quite OK Audio, 48 000 Hz | + | WAV 24 Bit, 48 kHz | Kompression *Disabled* | 16 Bit, 48 000 Hz | + | WAV 16 Bit, 44,1 kHz | Standard | Quite OK Audio, 44 100 Hz | + + Godot speichert WAV höchstens mit 16 Bit; standardmäßig komprimiert es mit Quite OK Audio ([Compress > Mode](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#compress-mode)). Die Abtastrate der Datei bleibt erhalten. Gemischt wird mit `audio/driver/mix_rate`, Standard 44100; laut Doku lässt man das besser dem Betriebssystem, die tatsächliche Rate liefert `AudioServer.get_mix_rate()` ([mix_rate](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-property-audio-driver-mix-rate)). Weicht die Datei ab, rechnet Godot um: Ein 1-kHz-Sinus von einer Sekunde als 48-kHz-WAV kam in einem 44,1-kHz-Mix gemessen mit etwa 1000 Hz und 1,000 Sekunden heraus, Tonhöhe und Länge blieben also gleich. + + Die Godot-Doku rät unter *Best practices*: Im Spiel bringt 24 Bit keinen hörbaren Vorteil, und über 48 kHz nur, wenn du den Klang zur Laufzeit verlangsamst; die Rohfassung für die Bearbeitung darfst du trotzdem in voller Qualität behalten ([Best practices](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#best-practices)). Ein Startpunkt, kein Gesetz: Projekt in der Rate deiner Aufnahmen, Rohmaterial unverändert, Export in einer Rate für alle Geräusche. + + ```text + ~/sound/ (Platzhalter) + ├── roh/ Handy-Dateien, nur lesen + │ └── 2026-10-24_klatsch/ + ├── reaper/werkbank/ + │ └── werkbank.rpp Projekt, kopierte Medien + └── export/sfx/ Ziel des Renders + ``` + + > [!WARNING] Rohaufnahmen nur lesen + > Mach den Rohordner schreibgeschützt, etwa mit `chmod -R a-w ~/sound/roh/2026-10-24_klatsch`. Dann scheitert jeder Versuch, ein Original zu ändern, mit einer Meldung statt still. Das Projekt arbeitet mit Kopien, wenn unter *Project Settings → Media* das Kopieren beim Import eingestellt ist (S. 32). + + ## Vom Take zum Klang: Schnitt, Fades, Nulldurchgänge + + Zieh die Aufnahme auf eine Spur. Ansage und Room Tone schneidest du nicht weg, sondern legst sie als eigene Items ab: Den Room Tone brauchst du gleich für das Rauschprofil. + + Zwanzig Klatscher von Hand zu schneiden, kostet Zeit. *Item processing → Dynamic split items…* trennt ein Item an den Stellen, an denen ein Gate öffnet und schließt. Für das Entfernen von Stille empfiehlt der Guide: *At transients* aus, *When gate opens* und *When gate closes* an, die Schwelle bei etwa −50 dB beginnen und von dort anpassen, *Remove silent areas* an; *Fade pads* glätten die Übergänge (S. 150–152). Die Aktion *Item: Auto trim/split items (remove silence)* macht dasselbe mit weiteren Optionen, darunter *Use zero crossings when splitting*, das laut Guide Klicks beim Abspielen verhindern kann (S. 128). + + Gegen Klicks an der Schnittkante hilft wie in der Musik ein Schnitt am Nulldurchgang oder ein kurzer Fade; Länge und Form stellst du je Item in den *Media Item Properties* (F2) ein, Vorgaben für neue Items unter *Preferences → Project → Item Fade Defaults* (S. 66, S. 134). Im Spiel klickt ein vergessener Schnitt bei jedem Auslösen. Rechne Fades in Samples, wenn es eng wird: 5 ms sind bei 48 kHz 240 Samples, ein Frame bei 60 Frames pro Sekunde 800. + + Am Anfang des Klangs gilt eine eigene Regel: Jede Millisekunde Stille vor der Transiente ist eine Millisekunde Verzögerung im Spiel. Godot kann beim Import beschneiden ([Audio zuordnen](/godot/audio)), aber dann entscheidet Godot, wo der Schnitt liegt. Schneide lieber in REAPER direkt vor die Transiente und lass den Fade-in so kurz, dass er sie nicht abflacht. Ob deine Exporte das einhalten, misst ein kleines Skript. Es liest 16- und 24-Bit-WAVs mit dem Modul `wave` und meldet die Zeit bis zum ersten Sample über der Schwelle. Hier legt es zum Vorführen eine Prüfdatei mit 30 ms Stille an: + + ```python run + import array, math, wave + from pathlib import Path + + def stille_vorn_ms(pfad, schwelle_db=-50.0): + with wave.open(str(pfad)) as w: + rate, kanaele, breite = w.getframerate(), w.getnchannels(), w.getsampwidth() + daten = w.readframes(w.getnframes()) + grenze = 2 ** (8 * breite - 1) * 10 ** (schwelle_db / 20) + for pos in range(0, len(daten), breite): + wert = int.from_bytes(daten[pos:pos + breite], "little", signed=True) + if abs(wert) > grenze: + return 1000 * (pos // breite // kanaele) / rate + return None + + # Prüfdatei: 30 ms Stille, dann ein abklingender 1-kHz-Klick + rate = 48000 + werte = [0] * int(0.030 * rate) + [ + round(20000 * math.exp(-i / 800) * math.sin(2 * math.pi * 1000 * i / rate)) + for i in range(4800) + ] + with wave.open("pruef_klick.wav", "wb") as w: + w.setnchannels(1); w.setsampwidth(2); w.setframerate(rate) + w.writeframes(array.array("h", werte).tobytes()) + + for datei in sorted(Path(".").glob("*.wav")): + print(datei.name, round(stille_vorn_ms(datei), 2), "ms") + ``` + + Auf deinem Rechner ersetzt du `Path(".")` durch deinen Exportordner und lässt den Teil mit der Prüfdatei weg. Fließkomma-WAVs (32 Bit float) liest `wave` nicht; rendere für die Prüfung in PCM. + + ## Säubern mit Bordmitteln: Hochpass, Rauschprofil, Gate + + Aus der Musik kennst du EQ und Gate. Im Spiel kommt hinzu: Ein Fehler in einem Klang, der 30-mal pro Minute spielt, ist 30-mal pro Minute zu hören. Und jede Datei bringt ihr eigenes Grundrauschen mit, das mit dem Klang kommt und mit ihm verschwindet. Darum säuberst du vor dem Export, und zwar mit Plugins, die REAPER mitbringt und die unter Linux nativ laufen: + + ```text + vorher Items geschnitten, mit Fades + Spur „sauber“ (Track FX, alle Takes einer Aufnahme) + 1 ReaEQ Band-Typ High pass Rumpeln, Trittschall + 2 ReaFIR Subtract, Profil gleichmäßiges Rauschen + aus dem Room Tone + 3 ReaGate Schwelle, Hold, Reste zwischen den + Release, Hysteresis Klängen + danach Render, Postprocess: Normalisieren + ``` + + **ReaEQ** mit dem Band-Typ *High pass* (Effects Guide, S. 17) nimmt Rumpeln und Trittschall heraus; wo die Grenze liegt, hörst du an deinem Material. + + **ReaFIR** im Modus *Subtract* zieht ein Rauschprofil ab. Der Ablauf laut Guide: ReaFIR auf die Spur, Modus *Subtract*, *Edit mode* auf *Precise*, *Automatically build noise profile* einschalten, nur die Stelle mit dem reinen Rauschen abspielen, stoppen, das Häkchen wieder entfernen. Danach wird das Profil beim Abspielen abgezogen ([User Guide, S. 333–334](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=333)). Mit Strg und Ziehen hebst oder senkst du das ganze Profil; je höher, desto mehr Artefakte (Effects Guide, S. 23). Dafür hast du den Room Tone aufgenommen. Der Guide grenzt ab: ReaFIR passt zu gleichmäßigem Rauschen wie Brummen, ein Gate zu gelegentlichen Geräuschen wie Atmern. + + **ReaGate** kennst du aus der Musik (Effects Guide, S. 24). Für eine Spieldatei zählen *Hold* und *Release*: Sie entscheiden, wie viel Ausklang in der Datei bleibt. Schließt das Gate zu früh, fehlt der Fahne das Ende; schließt es erst nach dem Ende der Region, endet die Datei im Restrauschen statt in Stille. *Hysteresis* hält das Gate ruhig, wenn der Ausklang um die Schwelle pendelt. + + Die Reihenfolge hat Gründe. Der Hochpass steht vor ReaFIR, weil das Profil das Signal lernen soll, das es später bearbeitet; änderst du den Hochpass, baust du das Profil neu. Das Gate steht hinter ReaFIR, weil der Abstand zwischen Klang und Rest dort größer ist und sich eine Schwelle leichter findet. + + Normalisiert wird am Ende, im Render. *Item processing → Normalize items* ändert die Lautstärke der Items (S. 141), und die liegt in REAPERs Signalfluss vor den Spur-FX: Item-Lautstärke und Fades kommen zuerst, dann die *Track FX Chain* (Signalfluss-Diagramm, S. 122). Normalisierst du die Items, nachdem die Kette steht, bekommen ReaFIR und ReaGate einen anderen Pegel: Das Profil wurde beim alten Pegel gelernt, die Schwelle passt nicht mehr. Im Render-Dialog öffnet *Postprocess…* dagegen eine Nachbearbeitung der fertigen Datei, nach allen FX. Sie normalisiert auf Peak, True Peak, LUFS-I und weitere Maße, „each file separately, to loudest file, or as if files play together“ (S. 414). Ob zwanzig Varianten gleich laut sein sollen oder ihre Unterschiede behalten, ist deine Entscheidung; mehr dazu in [Layering und Varianten](/sound/layering-varianten), Lautheit und Zielwerte in [Lautheit, Export, Import und Hörtest im Spiel](/sound/export-im-spiel). + + Die Kette sitzt auf der Spur, nicht in den Dateien. Eine Spur je Aufnahme-Sitzung, denn das Rauschprofil gehört zu Ort und Gerät. So bleibt jede Entscheidung änderbar, bis du renderst. + + ## Je Klang eine Region + + Eine **Region** ist ein benannter Zeitabschnitt im Projekt. Für die Werkbank heißt das: eine Region je Datei, ihr Name wird der Dateiname. Aus einer Zeitauswahl entsteht sie mit Shift+R (S. 179). Für viele Items auf einmal gibt es die Aktion *Markers: Insert separate regions for each selected item* (S. 182). Im *Region/Marker Manager* benennst du Regionen per Doppelklick, filterst die Liste und exportierst oder importierst sie als Datei, etwa als `.csv` (S. 180–181). + + Benenne nach einem Schema, das du einmal festlegst. Ein Platzhalter: `___`, also `pruef_klatsch_hand_01`. Die Grenzen der Region sind die Grenzen der Datei: Start direkt vor der Transiente, Ende dort, wo der Klang im Rauschen verschwunden ist. + + ## Region Render Matrix und Wildcards + + Die **Region Render Matrix** zeigt Regionen als Spalten und Spuren als Zeilen. Ein Kreuz heißt: diese Spur für diese Region rendern; die Zeile *Master mix* rendert den Mix. Du öffnest sie über *File → Region Render Matrix*, über den Knopf im Region/Marker Manager oder aus dem Render-Dialog. Gerendert wird mit *File → Render…*, Source *Region render matrix*, Bounds *All project regions*. Das Kontextmenü der Matrix kann eine Spur für alle Regionen ankreuzen und einzelne Felder in Mono, Stereo oder mehrkanalig erzwingen ([S. 419–420](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=419)). Der Render einer Spur enthält ihre FX, Fader und alles, was in sie geroutet ist (S. 417). *File → Consolidate/Export Tracks* dagegen gibt Spuren ohne Spur-FX aus (S. 411); deine Säuberungskette fehlte dann. + + Den Dateinamen setzt du aus **Wildcards** zusammen. Die Wildcard-Hilfe im Render-Dialog, im Guide als Bild abgedruckt, nennt die Regeln ([S. 411](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=411)): + + ```text + $region Name der Region + $project Name des Projekts + $track Name der Spur + / Unterordner (unter Linux wie auf dem Mac) + $Region erster Buchstabe groß + $REGION alles groß + $rEGION alles klein + $region< > Leerzeichen entfernen + $region< _> Leerzeichen durch _ ersetzen + leere Wildcard angrenzendes Leerzeichen, -, _ oder . fällt weg + $filenumber[01] Zählung 01, 02 … + ``` + + Zumer zeigt in seinem Artikel, wie weit das trägt: `$project_$parenttrack_$track_$region` wird zu „AwesomeEnemy_Cannon_Impacts_A“, und `$project_$track_$timelineorder[00]` nummeriert ausgewählte Items in der Reihenfolge auf der Zeitleiste ab 00. Am robustesten bleibt: Die Nummer steht im Regionsnamen, der Dateiname ist `$region`. Dann ändert ein neuer Klang in der Mitte keine anderen Namen. + + Drei Einstellungen entscheiden, ob der Export im Spiel funktioniert: + + - **Tail.** Unter den Time bounds steht *Tail* mit einer Länge in Millisekunden; im Screenshot des Guides ist sie an, mit 1000 ms (S. 413). Für welche Bounds die Fahne gilt, stellst du unter *Preferences → Audio → Rendering* ein, getrennt für ganzes Projekt, Zeitauswahl und Regionen sowie Items (S. 437). Zumer warnt: „Careful with this one. It is on by default and you may not need it.“ Ohne Tail endet die Datei am Ende der Region, und ein Hall auf der Spur bricht dort ab. Mit Tail hängt jede Datei Zeit an. + - **Silently increment filenames.** Diese Option hängt eine Nummer an, statt eine vorhandene Datei zu überschreiben (S. 415). Für Spiele ist das falsch herum: Godot importiert eine Datei neu, wenn sich ihr Inhalt ändert ([Automatic reimport](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport)), aber nur unter dem Namen, den die Szene kennt. + - **Channels.** Mono oder Stereo legst du für alle fest oder je Feld in der Matrix. Warum Klänge mit Ort meist mono sind, steht in [Hüllkurve, Frequenz, Dynamik und Raum](/sound/klangformung). + + Speichere das Ganze mit *Presets* als Preset; der Guide unterscheidet *Bounds and output*, *Options and format* und *All settings* (S. 414). *Dry run (no output)* rendert zur Probe, zeigt Wellenform, Lautheit und markiert Übersteuerungen (S. 416). + + > [!PRAXIS] Viele kleine Dateien aus einem Projekt + > Zumer kommt von Pro Tools und nennt die Region Render Matrix für Spiele „quite powerful“, weil man dort „many small files“ braucht. Er rendert die Schichten eines Klangs auch einzeln, „so you can manage variations on the middleware side“, und speichert Render-Einstellungen als Presets, um Dateien immer gleich zu erzeugen. Daraus folgt: Das Projekt ist die Quelle, die WAVs sind ein Ergebnis, das du jederzeit neu erzeugst. + + ## Linux: REAPER nativ, fremde Plugins über yabridge + + REAPER 7.81 läuft bei dir nativ, gestartet mit `pw-jack reaper`. `pw-jack` lässt ein Programm die JACK-Bibliotheken von PipeWire laden, so landet REAPERs JACK-Verbindung bei PipeWire ([pw-jack(1)](https://docs.pipewire.org/page_man_pw-jack_1.html)). Der Guide nennt VST2, VST3, CLAP und LV2 als unterstützte Formate und hebt für Linux hervor, dass REAPER LV2-Plugins automatisch sucht (S. 25, S. 116); dass ein Linux-VST3 läuft, zeigt dein Serum 2 auf dem PC. Windows-Plugins brauchen [yabridge](https://github.com/robbert-vdh/yabridge). Dein Stand: Kontakt 8 ließ sich auf dem Laptop nicht scannen, REAPER fror dabei ein; die übrigen Windows-Plugins sind unter Linux ungeprüft, die Windows-Partition bleibt der Rückweg. Jeder Schritt in diesem Kurs geht deshalb mit den Cockos-Plugins. + + > [!INTERVIEW] + > „Wir brauchen alle 60 Schritt-Varianten mit einem anderen Hochpass. Wie lange dauert das?“ Gute Antwort, etwa als Sound Designer: Kurz, wenn das Projekt so gebaut ist. Die Kette liegt auf der Spur, nicht in den Dateien; jede Variante ist eine Region mit ihrem Dateinamen; der Export ist ein Preset über die Region Render Matrix. Ich ändere den Filter einmal, höre drei Varianten im Spiel gegen, rendere alles neu, die Namen bleiben gleich, die Engine importiert neu. Die Rohaufnahmen fasse ich nicht an. Steckt die Bearbeitung dagegen in sechzig einzeln exportierten Dateien, muss ich jede einzeln anfassen. + + ## Häufige Fehler + + - **Im Rohordner gearbeitet.** Wer die Originale bearbeitet statt Kopien, hat irgendwann keine unbearbeitete Fassung mehr. Rohmaterial bleibt schreibgeschützt in `roh/`, das Projekt arbeitet mit Kopien. + - **Stille vor der Transiente.** Jede Millisekunde verzögert den Klang im Spiel. Miss sie im Export. + - **Schnitt ohne Fade.** Ein Schnitt mitten in der Welle klickt, bei jeder Wiedergabe. + - **ReaFIR-Profil aus Klang statt Rauschen gelernt.** Dann zieht es Teile des Klangs ab. Profil nur aus dem Room Tone bauen. + - **Hochpass nach dem Profil geändert.** Das Profil passt nicht mehr; neu lernen lassen. + - **Items nach der Kette normalisiert.** ReaFIR und ReaGate bekommen einen anderen Pegel, Profil und Schwelle passen nicht mehr. Normalisiert wird im Render unter *Postprocess*. + - **Consolidate statt Render.** Die Spur-FX fehlen in den Dateien. + - **Tail aus bei Klängen mit Hall auf der Spur.** Die Fahne bricht am Regionsende ab. + - **Silently increment filenames an.** Neue Fassungen bekommen neue Namen, das Spiel spielt die alte. + + ## Zusammenfassung + + - Godot 4.7.2 importiert WAV standardmäßig als Quite OK Audio, ohne Kompression mit 16 Bit; die Abtastrate bleibt, beim Mischen rechnet Godot um. + - Rohmaterial bleibt unverändert, das Projekt kopiert, der Export ist ein Ergebnis. + - Schnitte an Nulldurchgängen oder mit Fades, keine Stille vor der Transiente. + - Säubern mit Bordmitteln: Hochpass, ReaFIR-Profil aus dem Room Tone, Gate. Normalisiert wird im Render unter *Postprocess*, nach allen FX, nicht an den Items davor. + - Eine Region je Datei, ihr Name ist der Dateiname; die Region Render Matrix rendert alle in einem Durchgang. + - Wildcards bauen Namen und Ordner; `$region` mit der Nummer im Regionsnamen ist die robusteste Form. + - Tail und *Silently increment filenames* entscheiden, ob die Dateien im Spiel stimmen; alles als Preset speichern. +exercises: + - id: render-weg + type: choice + title: Eine Datei je Klang, mit der Kette + prompt: | + Zwanzig Klatscher liegen auf der Spur „sauber“ mit ReaEQ, ReaFIR und ReaGate, jeder in seiner Region. Welcher Weg liefert eine Datei je Klang, mit der Säuberungskette? + options: + - text: "*File → Render…*, Source *Region render matrix*, Bounds *All project regions*, in der Matrix „sauber“ für alle Regionen angekreuzt" + correct: true + why: Die Matrix rendert je angekreuztem Feld eine Datei, und der Render einer Spur enthält ihre FX. + - text: "*File → Render…*, Source *Master mix*, Bounds *Entire project*, Dateiname `$region`" + correct: false + why: Das ergibt eine einzige Datei über das ganze Projekt. `$region` ändert daran nichts. + - text: "*File → Consolidate/Export Tracks*, alle Spuren" + correct: false + why: Consolidate gibt Spuren vor der Bearbeitung aus, ohne Spur-FX. Die Kette fehlte, und es entsteht eine Datei je Spur, nicht je Klang. + - text: "*File → Render…*, Source *Stems (selected tracks)*, Bounds *Time selection* über alle zwanzig Klänge" + correct: false + why: Stems liefern eine Datei je ausgewählter Spur, hier also eine lange Datei mit allen zwanzig Klängen. + explanation: | + Im Spiel ist jede Datei ein Klang. Die Region Render Matrix verbindet beides: Regionen geben die Grenzen und Namen, Spuren den Klang samt FX. Consolidate ist für die Weitergabe an andere DAWs gedacht und lässt Spur-FX bewusst weg. Für Geräusche ist es deshalb fast nie der richtige Weg. + resources: + - title: REAPER User Guide 7.81 · Render Source Options (S. 417) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=417 + - title: REAPER User Guide 7.81 · Rendering Regions (S. 419) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=419 + + - id: kette-ordnen + type: order + title: Von der Handy-Datei zur Spieldatei + prompt: | + Bring die Schritte in eine Reihenfolge, in der du keinen Schritt wiederholen musst, weil ein späterer seine Grundlage ändert. + lang: prose + lines: | + Die Handy-Datei liegt unverändert in roh/, das Projekt arbeitet mit einer Kopie. + Ansage und Room Tone werden eigene Items, die Klatscher werden geschnitten und bekommen kurze Fades. + ReaEQ mit Hochpass kommt als erstes FX auf die Spur. + ReaFIR lernt das Rauschprofil aus dem Room Tone, danach wird das Lernen abgeschaltet. + ReaGate kommt hinter ReaFIR, die Schwelle wird eingestellt. + Je Klang eine Region nach dem Schema, ihr Ende dort, wo der gesäuberte Klang verklungen ist. + Render über die Region Render Matrix mit dem Preset, das unter Postprocess normalisiert. + alternatives: + - [1, 3, 2, 4, 5, 6, 7] + explanation: | + Ohne Kopie gibt es nichts zu schneiden, und erst der Room Tone als eigenes Item liefert das Material für das Profil. Der Hochpass steht vor ReaFIR, weil das Profil das Signal lernen soll, das ReaFIR später bekommt; ob du ihn vor oder nach dem Schneiden auf die Spur legst, ist gleich. Die Schwelle des Gates stellst du erst hinter ReaFIR ein, denn sie muss zum entrauschten Signal passen. Wo ein Klang verklungen ist, hörst du erst mit der fertigen Kette, deshalb folgen die Regionen danach. Normalisiert wird im Render unter *Postprocess*, nach allen FX. *Normalize items* würde die Lautstärke der Items ändern, die im Signalfluss vor den Spur-FX liegt: ReaFIR und ReaGate bekämen einen anderen Pegel, und Profil und Schwelle müsstest du neu einstellen. + resources: + - title: REAPER User Guide 7.81 · Noise Reduction with ReaFir (S. 333) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=333 + - title: REAPER User Guide 7.81 · Audio Signal Flow Chart, Tracks (S. 122) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=122 + - title: REAPER User Guide 7.81 · Render, Postprocess (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: fade-in-samples + type: output + title: Wie viele Samples hat ein Fade? + prompt: | + Was gibt dieses Programm aus? + lang: python + code: | + rate = 48000 + for ms in (2, 5, 10): + print(ms, "ms =", round(ms * rate / 1000), "Samples") + print("1 Frame bei 60 fps =", rate // 60, "Samples") + expected: | + 2 ms = 96 Samples + 5 ms = 240 Samples + 10 ms = 480 Samples + 1 Frame bei 60 fps = 800 Samples + explanation: | + Bei 48 kHz sind es 48 Samples je Millisekunde. Ein Frame bei 60 Frames pro Sekunde dauert 16,7 ms und umfasst 800 Samples, bei 44,1 kHz wären es 735. Die Rechnung hilft beim Abwägen: Ein Fade-in von 10 ms ist über die Hälfte eines Frames lang und kann eine scharfe Transiente schon merklich abflachen. Wie lang deine Fades sind, entscheidest du am Material. + resources: + - title: REAPER User Guide 7.81 · Media Item Properties (S. 134) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=134 + + - id: dateinamen-vorhersagen + type: output + title: Was REAPER aus den Wildcards macht + prompt: | + Das Projekt heißt `werkbank.rpp`, die Region „Klatsch Hand 01“, das Feld *Author* in den Projekt-Notizen ist leer. Gerendert wird als WAV. Welche Dateinamen ergeben die vier Muster? Eine Zeile je Muster, mit Endung. Die Regeln stehen in der Lektion. + lang: text + code: | + 1 $project/$region + 2 $region< _> + 3 $rEGION + 4 $author_$region< > + expected: | + werkbank/Klatsch Hand 01.wav + Klatsch_Hand_01.wav + klatsch hand 01.wav + KlatschHand01.wav + hints: + - Der Schrägstrich legt einen Unterordner an. + - Kleiner erster und großer zweiter Buchstabe der Wildcard machen alles klein. + - Eine leere Wildcard nimmt ein angrenzendes `_` mit. + explanation: | + `$project` ist der Projektname ohne Endung, der Schrägstrich macht daraus einen Ordner. `< _>` ersetzt jedes Leerzeichen durch einen Unterstrich, `< >` entfernt Leerzeichen. `$rEGION` schreibt alles klein. `$author` ist leer, und weil daneben ein `_` steht, fällt es weg. Leerzeichen in Dateinamen sind erlaubt, aber in Godot-Pfaden und Skripten lästig. Deshalb lohnt `< _>` oder ein Regionsname ohne Leerzeichen. + resources: + - title: REAPER User Guide 7.81 · Wildcards (S. 411) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=411 + + - id: exportgroesse + type: output + title: Wie groß wird der Exportordner? + prompt: | + Zwanzig Klänge zu je 0,8 Sekunden, als WAV ohne BWF-Metadaten (44 Byte Kopf). Was gibt das Programm aus? + lang: python + code: | + def wav_bytes(sekunden, rate, bit, kanaele): + return round(sekunden * rate) * kanaele * bit // 8 + 44 + + klaenge, laenge = 20, 0.8 + print(klaenge * wav_bytes(laenge, 48000, 24, 1)) + print(klaenge * wav_bytes(laenge, 48000, 16, 1)) + print(klaenge * wav_bytes(laenge, 48000, 24, 2)) + expected: | + 2304880 + 1536880 + 4608880 + explanation: | + 0,8 Sekunden sind bei 48 kHz 38 400 Samples, mit 3 Byte je Sample 115 200 Byte, plus Kopf. 16 Bit spart ein Drittel, Stereo verdoppelt. Für den Export ist die Größe selten ein Problem. Wichtiger ist, was Godot daraus macht: Gemessen in 4.7.2 speichert es WAV höchstens mit 16 Bit oder komprimiert mit Quite OK Audio. 24 Bit im Export kostet also Platz im Ordner, aber nichts im Spiel; im Archiv der Rohdateien ist es richtig aufgehoben. + resources: + - title: Godot Docs · Supported audio formats + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html#supported-audio-formats + + - id: render-luecken + type: gap + title: Ein Render-Preset für Regionen + prompt: | + Ergänze die Einstellungen so, dass je Region eine Datei im Ordner `export/sfx` entsteht, die genau wie die Region heißt. + lang: text + code: | + Source: ⟦Region render matrix⟧ + Bounds: ⟦All project regions⟧ + Directory: ~/sound/export/sfx + File name: ⟦$region⟧ + Silently increment filenames: ⟦aus⟧ + gaps: + - label: Source + hint: Die Quelle, die Spuren und Regionen kombiniert. + accept: ["Region Render Matrix", "region render matrix"] + - label: Bounds + hint: Alle Regionen des Projekts. + accept: ["All Project Regions", "all project regions", "Project regions", "Project Regions", "project regions", "All regions", "All Regions", "all regions"] + - label: Dateiname + hint: Die Wildcard für den Namen der Region. + - label: Hochzählen + hint: Godot soll die neue Fassung unter dem alten Namen finden. + accept: ["Aus", "AUS", "off", "Off", "OFF", "nein", "Nein", "deaktiviert", "Deaktiviert", "disabled", "Disabled"] + explanation: | + Die Matrix bestimmt, welche Spur für welche Region gerendert wird, *All project regions* nimmt alle Regionen. `$region` macht den Regionsnamen zum Dateinamen. Hochzählen bleibt aus, damit ein neuer Render die Datei ersetzt und Godot sie neu importiert. Speichere das als Preset, dann ist jeder weitere Export ein Klick. + resources: + - title: REAPER User Guide 7.81 · Render to File options (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: fahne-abgeschnitten + type: bug + title: Der Hall endet hart + prompt: | + Auf der Spur „sauber“ liegt zum Test ein ReaVerbate. Im Projekt klingt jeder Klatscher sauber aus. In den gerenderten Dateien bricht der Hall am Ende abrupt ab. Welche Einstellung im Render-Dialog ist schuld? + lang: text + code: | + Source: Region render matrix + Bounds: All project regions + Tail: aus + Regionen: enden 20 ms nach dem trockenen Klatscher + File name: $region + Format: WAV, 24 bit PCM + lines: [3] + explanation: | + Ohne Tail endet jede Datei am Ende ihrer Region. Die Region endet kurz nach dem trockenen Klang, der Hall auf der Spur klingt aber länger. Im Render-Dialog ist Tail die Stelle; die Regionen selbst legst du in der Zeitleiste fest, nicht im Dialog. Zwei Wege: Tail einschalten und unter *Preferences → Audio → Rendering* für Regionen aktivieren, oder die Regionen bis zum Ende der Fahne verlängern. Ob ein Hall überhaupt in die Datei gehört oder im Spiel auf einem Bus liegt, ist eine eigene Entscheidung (siehe [Hüllkurve, Frequenz, Dynamik und Raum](/sound/klangformung)). + resources: + - title: REAPER User Guide 7.81 · Render-Dialog mit Tail (S. 413) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=413 + - title: REAPER User Guide 7.81 · Preferences, Audio, Rendering (S. 437) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=437 + + - id: alte-fassung-im-spiel + type: bug + title: Godot spielt die alte Fassung + prompt: | + Du hast den Hochpass geändert und neu gerendert. In Godot klingt alles wie vorher. Im Exportordner liegen neben `pruef_klatsch_hand_01.wav` jetzt weitere Dateien mit angehängten Nummern. Welche Zeile ist schuld? + lang: text + code: | + Source: Region render matrix + Bounds: All project regions + Directory: ~/projekte/spiel/audio/sfx + File name: $region + Silently increment filenames to avoid overwriting: an + Format: WAV, 24 bit PCM + lines: [5] + explanation: | + Die Option schützt vor dem Überschreiben, indem sie eine Nummer anhängt. Für Musik-Exporte ist das bequem, für ein Spiel falsch: Die Szene verweist auf den alten Namen, und die neue Datei liegt daneben, ohne dass ein Node sie nutzt. Ist die Option aus, ersetzt der Render die Datei; ändert sich ihr Inhalt, importiert Godot sie neu. Die angehängten Kopien löschst du von Hand. + resources: + - title: REAPER User Guide 7.81 · Silently increment filenames (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 + - title: Godot Docs · Automatic reimport + url: https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport + + - id: stille-falsch-gemessen + type: bug + title: Das Skript misst zu viel Stille + prompt: | + Das Messskript meldet für eine 24-Bit-Monodatei mit 30 ms Stille vor dem Klick 90 ms, für eine 16-Bit-Stereodatei mit derselben Stille 120 ms. Finde die Zeile und korrigiere sie. + lang: python + code: | + import wave + + def stille_vorn_ms(pfad, schwelle_db=-50.0): + with wave.open(pfad) as w: + rate = w.getframerate() + kanaele = w.getnchannels() + breite = w.getsampwidth() + daten = w.readframes(w.getnframes()) + grenze = 2 ** (8 * breite - 1) * 10 ** (schwelle_db / 20) + for pos in range(0, len(daten), breite): + wert = int.from_bytes(daten[pos:pos + breite], "little", signed=True) + if abs(wert) > grenze: + return 1000 * pos / rate + return None + lines: [13] + fix: + - line: 13 + answers: + - return 1000 * (pos // breite // kanaele) / rate + - return 1000 * (pos // (breite * kanaele)) / rate + - return 1000 * (pos // (kanaele * breite)) / rate + - return 1000 * (pos // kanaele // breite) / rate + - return pos // breite // kanaele * 1000 / rate + - return pos // (breite * kanaele) * 1000 / rate + - return 1000 * pos / (breite * kanaele) / rate + - return 1000 * pos / (kanaele * breite) / rate + - return 1000 * pos / breite / kanaele / rate + - return 1000 * (pos / (breite * kanaele)) / rate + - return 1000 * (pos / breite / kanaele) / rate + - return pos / (breite * kanaele) / rate * 1000 + - return pos / breite / kanaele / rate * 1000 + explanation: | + `pos` zählt Bytes, nicht Zeitpunkte. Ein Zeitpunkt (in `wave` ein Frame) umfasst `breite` Bytes je Kanal und `kanaele` Kanäle, bei 24 Bit mono also 3 Byte, bei 16 Bit stereo 4 Byte. Deshalb kommt das Dreifache und das Vierfache heraus. Erst die Teilung durch Breite und Kanäle ergibt den Zeitpunkt, und der durch die Rate die Zeit. Ob du mit `//` auf ganze Zeitpunkte abrundest wie das Lektionsskript oder mit `/` teilst, ändert das Ergebnis um weniger als ein Sample. + resources: + - title: Python Docs · wave + url: https://docs.python.org/3/library/wave.html + + - id: reafir-erklaeren + type: explain + title: Warum das Rauschprofil aus dem Room Tone kommt + prompt: | + Erkläre jemandem, der ReaFIR nicht kennt, was diese Notiz bewirkt und warum sie so aufgebaut ist. + lang: text + code: | + Spur „sauber“, Sitzung 2026-10-24 + 1 ReaEQ High pass + 2 ReaFIR Subtract, Edit mode Precise + Profil: nur Room Tone (0:12–0:40) + abgespielt, Lernen danach aus + 3 ReaGate hinter ReaFIR + neue Sitzung = neue Spur, neues Profil + points: + - ReaFIR zieht im Modus Subtract ein gelerntes Rauschspektrum vom Signal ab. + - Das Profil darf nur Rauschen enthalten, sonst zieht es Teile des Klangs ab; dafür ist der Room Tone da. + - Das Lernen wird abgeschaltet, damit das Profil beim Abspielen der Klänge nicht weiterlernt. + - Der Hochpass steht davor, weil das Profil das Signal lernen soll, das ReaFIR später bekommt. + - Rauschen hängt an Ort und Gerät, deshalb gehört zu jeder Sitzung ein eigenes Profil. + explanation: | + ReaFIR im Modus *Subtract* lernt mit *Automatically build noise profile*, wie das Rauschen klingt, und zieht dieses Spektrum danach ab. Damit es nur das Rauschen lernt, spielst du beim Lernen nur den Room Tone ab und schaltest das Lernen danach aus; sonst würde es auch die Klatscher als Rauschen begreifen. Der Hochpass steht davor, weil das Profil zu dem Signal passen muss, das ReaFIR später bekommt. Für gleichmäßiges Rauschen ist ReaFIR das richtige Werkzeug, für gelegentliche Geräusche das Gate dahinter. Weil das Rauschen vom Ort und vom Gerät abhängt, bekommt jede Aufnahmesitzung eine eigene Spur mit eigenem Profil. + resources: + - title: REAPER User Guide 7.81 · Noise Reduction with ReaFir (S. 333) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=333 + - title: REAPER Effects Guide · ReaFir Subtract Mode (S. 22) + url: https://www.reaper.fm/userguide/REAPEREffectsGuide2021.pdf#page=22 + + - id: matrix-erklaeren + type: explain + title: Was diese Matrix rendert + prompt: | + Erkläre, welche Dateien diese Region Render Matrix mit dem Dateinamen `$region-$track` erzeugt und warum der Room Tone aus einer anderen Spur kommt. Die Spur „roh“ hat keine FX, „sauber“ die Säuberungskette. Tail ist aus. + lang: text + code: | + Spur klatsch_01 klatsch_02 room_tone + Master mix + roh x + sauber x x + points: + - "Es entstehen drei Dateien: `klatsch_01-sauber.wav`, `klatsch_02-sauber.wav` und `room_tone-roh.wav`." + - Jede Datei ist so lang wie ihre Region und enthält die FX ihrer Spur. + - Der Room Tone kommt unbearbeitet aus „roh“, damit sein Rauschen erhalten bleibt, etwa für ein späteres Profil oder als Grund unter Stille. + - Die leere Zeile Master mix erzeugt keine zusätzlichen Mischdateien. + explanation: | + Jedes Kreuz ist eine Datei, benannt aus Region und Spur: zweimal die Klatscher aus „sauber“, mit Hochpass, Rauschprofil und Gate, einmal der Room Tone aus „roh“. Den Room Tone willst du gerade nicht entrauscht haben, denn er ist das Rauschen; du brauchst ihn für Profile oder als Grundton, der Lücken füllt. Grenzen der Dateien sind die Grenzen der Regionen, weil Tail aus ist. Der Master mix bleibt leer, sonst kämen je Region weitere Dateien mit dem Mix aller Spuren dazu. + resources: + - title: REAPER User Guide 7.81 · Region Render Matrix (S. 420) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=420 + + - id: render-vorlage + type: practice + title: Ordner und Render-Preset anlegen + prompt: | + Lege die Ordner für deine Geräusche an und in REAPER ein Render-Preset, das je Region eine Datei in deinen Exportordner schreibt. Mach die Rohaufnahmen schreibgeschützt. Prüfe das Preset mit *Dry run* an einem Testprojekt mit zwei Regionen, dann mit einem echten Render. Ordnernamen, Abtastrate und Format sind deine Wahl; die Musterlösung zeigt Platzhalter. + lang: text + solution: | + Ordner (Platzhalter) + ~/sound/roh/ Handy-Dateien + ~/sound/reaper/werkbank/werkbank.rpp + ~/sound/export/sfx/ + + Rohdateien schreibgeschützt: + chmod -R a-w ~/sound/roh/2026-10-24_klatsch + + Render-Preset „Spiel-SFX“ (Presets → All settings) + Source Region render matrix + Bounds All project regions + Tail aus (an nur, wenn FX nachklingen) + Directory ~/sound/export/sfx + File name $region + Sample rate 48000 (Platzhalter) + Channels Mono (Platzhalter) + Format WAV, 24 bit PCM + Postprocess Normalize nach deiner Wahl + Silently increment filenames aus + + Test: zwei Regionen pruef_a, pruef_b + Dry run zeigt 2 Dateien, keine Übersteuerung + Render: export/sfx/pruef_a.wav, pruef_b.wav + Zweiter Render ersetzt beide, keine Kopien mit Nummer + checklist: + - "In deinem Rohordner lässt sich keine Datei ändern oder anlegen (Test: `echo x >> datei.wav` oder `touch neu.wav` im Ordner schlägt mit „Keine Berechtigung“ fehl)." + - Das Preset steht unter *Presets* mit einem Namen, den du wiedererkennst, und lädt Source, Bounds, Dateiname und Format. + - Ein Dry run mit zwei Regionen zeigt genau zwei Dateien. + - Nach dem zweiten Render liegen im Exportordner dieselben zwei Dateinamen, ohne angehängte Nummern. + explanation: | + Ordner und Preset sind Technik, keine Gestaltung, deshalb gibt es hier eine Musterlösung. Der Schreibschutz verhindert, dass ein Glue oder ein Editor die einzige Kopie einer Aufnahme verändert. Das Preset sorgt dafür, dass jeder Export gleich entsteht. Der zweite Render ist der entscheidende Test: Er muss die Dateien ersetzen, sonst sieht Godot deine Änderungen nie. + resources: + - title: REAPER User Guide 7.81 · Render-Presets (S. 414) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=414 + + - id: eigene-aufnahme-werkbank + type: practice + title: Deine Aufnahme durch die Werkbank + prompt: | + Nimm eine eigene Aufnahme aus [Feldaufnahme mit dem Handy](/sound/feldaufnahme) mit mehreren Takes und Room Tone. Schneide, säubere und rendere sie mit deinem Preset. Halte in einer kurzen Notiz fest, was du in welcher Reihenfolge eingestellt hast und warum. Wie stark du entrauschst und wie die Klänge klingen sollen, entscheidest du. + lang: prose + checklist: + - Die Rohdatei ist unverändert, das Projekt arbeitet mit einer Kopie. + - Room Tone und Ansage liegen als eigene Items im Projekt. + - Das ReaFIR-Profil ist nur aus dem Room Tone gelernt, und das Lernen ist abgeschaltet. + - Jede exportierte Datei heißt nach deinem Schema, und ihr Name steht so im Region/Marker Manager. + - Das Messskript meldet für jede Datei die Stille vor der Transiente, und du hast notiert, welche Werte du akzeptierst. + - Deine Notiz nennt die Reihenfolge der FX und einen Grund für jede Stelle. + explanation: | + Die Übung prüft die Werkbank, nicht deinen Geschmack. Ob die Klänge gelingen, hörst du später im Spiel. Ob die Werkbank trägt, siehst du daran, dass du eine Entscheidung ändern und alles in Minuten neu erzeugen kannst. Die Notiz brauchst du, wenn du in drei Wochen wissen willst, warum ein Klang so klingt. + resources: + - title: REAPER User Guide 7.81 · Dynamic Splitting (S. 150) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=150 diff --git a/content/sound/modules/werkstatt-audioabschnitt.yaml b/content/sound/modules/werkstatt-audioabschnitt.yaml new file mode 100644 index 0000000..a8fc2a1 --- /dev/null +++ b/content/sound/modules/werkstatt-audioabschnitt.yaml @@ -0,0 +1,553 @@ +id: werkstatt-audioabschnitt +title: "Werkstatt: ein kleiner Audioabschnitt im Spiel" +level: 3 +minutes: 240 +summary: Du baust einen kleinen Abschnitt deines Spiels mit eigener Aktion und Varianten, eigenem Stück in mindestens zwei Zuständen, Atmo, Mischung und Lautheit, hältst Originale, Exporte und Sicherung getrennt und überarbeitest in Fassungen nach Hörtests, mit Entscheidungsprotokoll und Quellenliste. +place: pc +goals: + - Du begrenzt vor der Arbeit den Umfang und legst einen Abschnittsplan mit Cue-Liste, Zustandsdiagramm, Busplan und Prioritäten an. + - Du hältst REAPER-Projekte und Rohaufnahmen getrennt von den Exporten im Godot-Projekt und sicherst beides unabhängig von Git, mit geprüfter Wiederherstellung. + - Du baust eine selbst gewählte Aktion mit eigenen Varianten, ein eigenes Stück mit mindestens zwei Zuständen und getesteten Übergängen und eine Atmo ins Spiel ein und mischst sie nach Priorität. + - Du überarbeitest in Fassungen nach Hörtests, eine Änderung je Protokolleintrag, und führst ein Entscheidungsprotokoll mit Entscheidung, Grund, Alternative, Test und Ergebnis. + - Du begründest, warum KI in dieser Werkstatt nur Technik beschleunigt und keinen Klang, keine Musik und keine Mischentscheidung liefert. +resources: + - title: REAPER User Guide 7.81 · Render to File, Save copy of project (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 + - title: REAPER User Guide 7.81 · Multiple Project Backup Versions (S. 234) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=234 + - title: Godot Docs · Project organization, Ignoring specific folders + url: https://docs.godotengine.org/en/stable/tutorials/best_practices/project_organization.html#ignoring-specific-folders + - title: Godot Docs · Version control systems, Git LFS + url: https://docs.godotengine.org/en/stable/tutorials/best_practices/version_control_systems.html#git-lfs + - title: Godot Docs · Audio buses + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html +lesson: | + Du hast in den vorigen Modulen einzelne Teile gebaut: eine Cue-Liste, Aufnahmen, geschichtete Varianten, eine Atmo, ein Stück in Loops und Stems, Busse in Godot. Jeder Teil klang für sich richtig. Im Spiel klingt alles zugleich, und plötzlich überdeckt die Atmo die Schritte, die Musik wechselt zu spät, und nach der dritten Änderung weißt du nicht mehr, welche den Unterschied gemacht hat. + + In dieser Werkstatt baust du einen kleinen Abschnitt deines Spiels, zwei bis vier Bildschirme groß, mit allem, was dazugehört, und überarbeitest ihn in Fassungen. Die Lektion erklärt den Ablauf und warum er so gebaut ist; die Übungen sind die Arbeitsschritte. Es gibt keine Vorschläge für Klänge, Stile, Instrumente, Tempi, Zustände oder Lautstärken. Was in deinem Abschnitt klingt und wie, entscheidest du. Jede Zahl, die hier steht, ist ein Platzhalter. + + > [!NOTE] Zeit einplanen + > Plan mehrere Sitzungen am PC ein, mit mindestens einer Nacht zwischen Fassung und Hörtest. Die ersten Übungen gehen schnell, die Übungen ab „Fassung 1“ sind die Werkstatt selbst. Als Testumgebung dient die Combat-Testszene ([Testszene: Angriff](/combat/testszene-angriff)) oder das Godot-Testgelände ([Projekt erkunden](/godot/projekt-erkunden)). + + ## Der Ablauf im Überblick + + 1. **Abschnittsplan** – Umfang, Cue-Liste, Zustandsdiagramm, Busplan und Prioritäten. + 2. **Ordner und Sicherung** – Originale getrennt von Exporten, Sicherung mit geprüfter Wiederherstellung. + 3. **Fassung 1** – Aktion mit Varianten, Stück in mindestens zwei Zuständen, Atmo, Mischung und Lautheit, im Spiel. + 4. **Hörtest 1**, dann **Überarbeitung 1** – Fassung 2. + 5. **Hörtest 2**, dann **Überarbeitung 2** – Fassung 3. + 6. **Abschluss** – Quellenliste, Entscheidungsprotokoll, Sicherung, Reflexion. + + Jeder Schritt stützt sich auf ein Modul dieses Kurses. Die Cue-Liste kommt aus [Vom Ereignis zur Cue-Liste](/sound/klangplan), das Zustandsdiagramm aus [Musik für Zustände](/sound/musik-zustaende), Busplan und Prioritäten aus [Mischung und Stille](/sound/mischung-stille). Gebaut wird wie in [Layering und Varianten](/sound/layering-varianten), [Foley und Atmo](/sound/foley-umgebung) und [Eigenes Stück aufbereiten](/sound/loops-stems), eingebaut wie in [Musik in Godot](/sound/godot-musik) und [Lautheit, Export, Import](/sound/export-im-spiel). Die Herkunft fremder Dateien dokumentierst du wie in [Quellen und Lizenzen](/sound/quellen-lizenzen). Hier geht es darum, alles zusammenzubringen und in Runden besser zu machen. + + ## Der Abschnittsplan: Umfang festlegen, bevor du baust + + Ein Abschnitt wird schnell zu groß. Begrenz ihn deshalb vorher: **eine** selbst gewählte Aktion mit ihren Varianten, **ein** eigenes Stück mit mindestens zwei Zuständen, **eine** Atmo aus Bett und Spot-Klängen und die Stille, die du bewusst setzt. Alles andere bleibt für später. + + Der Plan besteht aus Teilen, die du schon kennst, auf den Abschnitt zugeschnitten. Die Form, ohne Inhalt: + + ```text + Abschnitt: , + Aktion: , Auslöser + Cue-Liste: + Musik: , Diagramm mit + Auslösern und Nachlauf + Atmo: Bett , Spots + Busse: , Ducking + Prioritäten: + Lautheit: Ziel LUFS-I, + True Peak + ``` + + Der Plan ist der Maßstab für jeden Hörtest. Ohne ihn hörst du gegen ein Gefühl, mit ihm gegen deine eigene Absicht. Er darf sich ändern. Wenn du beim Bauen merkst, dass der Abschnitt etwas anderes braucht, notier die Änderung im Entscheidungsprotokoll, statt den Plan still umzuschreiben. + + ## Originale, Exporte und Sicherung + + Halte zwei Welten getrennt. Die **Originale**: REAPER-Projekte, Rohaufnahmen, Aufnahmeprotokolle, die Lieferliste. Sie werden bearbeitet, aber nie durch einen Export ersetzt. Die **Exporte**: WAV- und Ogg-Dateien im Godot-Projekt, die jederzeit aus den Originalen neu entstehen können. Ein Platzhalter-Aufbau: + + ```text + audio-quellen/ (außerhalb des Godot-Projekts) + reaper/abschnitt-1.rpp + roh/2026-10-24_ort_takes/ + protokolle/ + spiel/ (Godot-Projekt) + project.godot + audio/sfx/ + audio/musik/ + audio/atmo/ + ``` + + Liegen die Originale im Godot-Projekt, importiert Godot jede Rohaufnahme mit. Willst du sie trotzdem dort haben, verhindert eine leere Datei `.gdignore` im Ordner den Import ([Ignoring specific folders](https://docs.godotengine.org/en/stable/tutorials/best_practices/project_organization.html#ignoring-specific-folders)). + + REAPER hilft dir, Fassungen nachzuvollziehen. Beim Speichern wird die vorige Fassung des Projekts als `.RPP-bak` aufgehoben ([S. 29](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=29)); mit „Keep multiple versions“ entsteht bei jedem Speichern eine neue Fassung mit Datum und Uhrzeit ([S. 234](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=234)). Im Render-Dialog legt „Save copy of project to outfile.xxx.RPP“ zu jedem Export eine Kopie des Projekts in dem Zustand ab, in dem gerendert wurde ([S. 415](https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415)). Damit weißt du bei jeder Datei, aus welchem Projektstand sie stammt. Vorsicht mit „Silently increment filenames“ auf derselben Seite: Die Option hängt bei vorhandenen Namen eine Nummer an. Für das Spiel heißt das, dass eine neue Datei neben der alten landet und das Spiel weiter die alte spielt. Ersetzt du eine Datei unter gleichem Namen, importiert Godot sie automatisch neu ([Automatic reimport](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/import_process.html#automatic-reimport)); die Render-Einstellungen dafür stehen in [REAPER als Werkbank](/sound/reaper-werkbank). + + Für das Godot-Projekt nimmst du Git, Audiodateien gern mit Git LFS ([Git LFS](https://docs.godotengine.org/en/stable/tutorials/best_practices/version_control_systems.html#git-lfs)). Git hebt Fassungen auf. Ein Backup ist es nicht. + + > [!WARNING] Git ersetzt kein Backup + > Ein lokaler Commit liegt auf derselben Platte wie das Projekt. Ein Push kopiert nur das Godot-Projekt, nicht die Originale außerhalb davon. Ein Backup ist eine Kopie von Originalen und Projekt auf einem anderen Datenträger. Ob es eines ist, weißt du erst, wenn du einmal daraus wiederhergestellt hast: Projekt aus der Kopie öffnen, ein Export neu rendern, im Spiel hören. + + ## Fassung 1: Aktion, Stück, Atmo und Mischung im Spiel + + Bau Fassung 1 in derselben Reihenfolge, in der du später Fehler suchst: erst mit Prüfmustern, dann mit deinem Material. Ein Klick auf dem Kontaktbild zeigt, ob der Auslöser im richtigen Frame sitzt ([Animation im Spiel](/animation/animation-im-spiel), [Testszene](/animation/testszene)). Ein Prüfloop aus Metronom-Klicks zeigt, ob Loop-Länge und Übergänge stimmen ([Eigenes Stück aufbereiten](/sound/loops-stems)). Erst wenn die Prüfmuster stimmen, kommen deine Varianten, dein Stück und deine Atmo dazu. Dann weißt du bei einem Fehler, ob er im Material oder im Einbau liegt. + + Für jeden Übergang deines Diagramms hältst du im Log fest, wann das Spiel ihn anfragt und wann die Musik wechselt, und vergleichst beides mit deiner Rechnung. Wie das Log entsteht, zeigt [Musik in Godot](/sound/godot-musik). + + Gemischt wird im Spiel, nicht in REAPER. Jeder Player hängt an einem Bus deines Busplans, und die Lautstärke eines Klangs ist die Summe aus Player und allen Bussen auf seinem Weg ([Audio buses](https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html), [Audio zuordnen](/godot/audio)). Prüf die Prioritäten aus deinem Plan: Ist das Wichtigste in jedem Moment hörbar? Setz die Stille, die du geplant hast, und hör, ob sie als Stille ankommt oder als Lücke. Zum Schluss misst du die Lautheit des Abschnitts und vergleichst sie mit deinem Platzhalterwert ([Lautheit, Export, Import](/sound/export-im-spiel)). Der Wert ist ein Startpunkt, kein Urteil. + + ## Hörtest, Überarbeitung und Entscheidungsprotokoll + + Zwischen Fassung und Hörtest liegt mindestens eine Nacht. Direkt danach hörst du, was du gemeint hast; mit Abstand eher, was klingt. Hör den Abschnitt im Spiel, mit Kopfhörern und über den Lautsprecher des Handys oder was du sonst hast, laut und sehr leise. Leise hörst du, was als Erstes verschwindet. Notier jede Stelle, an der etwas nicht so ankommt, wie dein Plan es vorsieht, und korrigier beim Hören nichts. + + Dann überarbeitest du. Jeder Eintrag im Protokoll ändert **eine** Sache. Änderst du zwei Dinge auf einmal und es klingt besser, weißt du nicht, welche geholfen hat, und kannst die andere nicht zurücknehmen. Nach der Überarbeitung speicherst du die neue Fassung getrennt von der alten: Projektstand, Exporte und Protokoll. + + > [!MERKE] + > Ein Hörtest, eine Liste. Eine Änderung, ein Eintrag. Jede Fassung bleibt erhalten. + + Das **Entscheidungsprotokoll** hält jede Entscheidung fest, die über den Plan hinausgeht oder ihn ändert. Fünf Spalten reichen: Entscheidung, Grund, Alternative, Test, Ergebnis. Als CSV-Datei neben den Originalen. Ein Lehrbeispiel für die Form, kein Vorschlag für deinen Abschnitt: + + ```csv + datum,fassung,entscheidung,grund,alternative,test,ergebnis + 2026-10-25,2,"Übergang B->A auf nächsten Takt","Hörtest 1: Schnitt mitten in Phrase","sofort mit Blende","Prüfloop + Log, 5 Anfragen","Wechsel 0,3 bis 1,9 s nach Anfrage; Schnitt weg" + ``` + + Die Spalte **Alternative** zeigt, dass es eine Wahl war. Die Spalte **Test** sagt, wie du es geprüft hast, so genau, dass du den Test wiederholen kannst. Das **Ergebnis** steht erst nach dem Test, mit dem, was du gemessen oder gehört hast. Zusammen mit der Quellenliste aus [Quellen und Lizenzen](/sound/quellen-lizenzen) macht das Protokoll jede Datei im Abschnitt nachvollziehbar, auch für dich in einem halben Jahr. + + > [!PRAXIS] Im Zusammenhang hören + > Koji Kondo legte seinem Director laut einem Bericht über seinen GDC-Vortrag 2007 keine einzelnen Stücke vor. Er wartete, bis vier oder fünf fertig waren, und präsentierte sie als Gruppe, damit die Balance zwischen den Stücken hörbar wird; auch die Übergänge zwischen Stücken nennt er eine Frage der Balance ([Vincent Diamante, Game Developer](https://www.gamedeveloper.com/game-platforms/gdc-koji-kondo-s-interactive-musical-landscapes)). Für die Werkstatt heißt das: Ein Klang wird im Abschnitt beurteilt, mit allem, was dort gleichzeitig klingt, nicht solo in REAPER. + + > [!INTERVIEW] + > „Erzähl uns von einem Stück in deinem Portfolio. Was hast du entschieden und warum?“ Gute Antwort, etwa als Sound Designer oder Audio Designer: Ich zeige den Abschnitt im Spiel, dann eine Entscheidung aus dem Protokoll, mit Grund, verworfener Alternative und dem Test, der sie bestätigt hat. Ich spiele Fassung 1 und die letzte Fassung nacheinander vor und sage, welche Änderung welchen Unterschied macht. Ich nenne die Herkunft jeder fremden Datei mit Lizenz und sage, was ich selbst aufgenommen habe. Und ich nenne, was ich bewusst nicht geändert habe. + + ## KI in der Werkstatt + + Jeder Klang, jedes Stück und jede Mischentscheidung in diesem Abschnitt stammt von dir. KI darf hier Technik beschleunigen: ein GDScript-Gerüst, das die Musik umschaltet und ins Log schreibt, ein Skript, das Dateien nach deinem Schema umbenennt, eine CSV-Vorlage für Protokoll und Quellenliste mit Platzhaltern. Generierte Klänge oder Musik gehören nicht in den Abschnitt, auch nicht als Platzhalter, denn Platzhalter bleiben gern liegen. Assistenten, die einen Mix oder ein Master vorschlagen, fallen ebenfalls darunter. + + Auch Fragen sind Aufträge. „Klingt die Mischung gut?“ oder „Was fehlt hier?“ liefert dir fremde Mischentscheidungen. Diese Fragen beantwortest du im Hörtest, mit deinem Plan als Maßstab, und später mit Menschen, die den Abschnitt zum ersten Mal hören. + + ## Häufige Fehler + + - **Umfang wächst beim Bauen.** Aus einer Aktion werden drei, und keine wird fertig. Plan begrenzen, Neues ins Protokoll für später. + - **Gleich mit eigenem Material eingebaut.** Ein Fehler kann dann im Klang oder im Einbau liegen. Erst Prüfmuster, dann Material. + - **Solo beurteilt.** Ein Klang, der allein trägt, verschwindet im Abschnitt. Beurteilen im Spiel, mit allem zugleich. + - **Zwei Änderungen in einem Eintrag.** Es klingt besser, aber du weißt nicht, warum. Eine Änderung je Eintrag. + - **Ergebnis vor dem Test notiert.** Das Protokoll bestätigt dann die Erwartung statt der Messung. + - **Rohaufnahmen im Godot-Projekt.** Godot importiert sie mit. Originale außerhalb oder mit `.gdignore`. + - **„Silently increment filenames“ beim Export an.** Die neue Datei heißt anders, das Spiel spielt die alte. + - **Git als Backup.** Lokal geht es mit der Platte verloren, gepusht fehlen die Originale. Kopie von allem auf einem anderen Datenträger, Wiederherstellung ausprobiert. + + ## Zusammenfassung + + - Der Umfang steht vorher fest: eine Aktion, ein Stück mit mindestens zwei Zuständen, eine Atmo, bewusste Stille. + - Der Abschnittsplan aus Cue-Liste, Diagramm, Busplan, Prioritäten und Lautheitsziel ist der Maßstab für jeden Hörtest. + - Originale und Exporte sind getrennt; jeder Export lässt sich einem Projektstand zuordnen; ein Backup liegt auf einem anderen Datenträger und ist einmal wiederhergestellt worden. + - Erst Prüfmuster, dann eigenes Material; Übergänge werden mit Log gegen die Rechnung geprüft. + - Gemischt und beurteilt wird im Spiel, laut und leise, mit verschiedenen Wiedergabegeräten. + - Jede Überarbeitung ändert eine Sache je Eintrag, jede Fassung bleibt erhalten. + - Das Entscheidungsprotokoll nennt Entscheidung, Grund, Alternative, Test und Ergebnis; die Quellenliste die Herkunft jeder fremden Datei. + - KI beschleunigt nur Technik. Klänge, Musik und Mischentscheidungen bleiben deine. +exercises: + - id: ablauf-ordnen + type: order + title: Was kommt wann? + prompt: | + Bring die Schritte der Werkstatt in die Reihenfolge, in der du sie machst. + lang: prose + lines: | + Abschnittsplan mit Umfang, Cue-Liste, Diagramm, Busplan und Prioritäten anlegen + Ordner für Originale und Exporte anlegen und eine Sicherung einrichten + Fassung 1 mit Prüfmustern einbauen und testen + Eigenes Material einsetzen, mischen und die Lautheit messen + Hörtest 1 nach mindestens einer Nacht + Überarbeitung 1, eine Änderung je Protokolleintrag + Hörtest 2 und Überarbeitung 2 + Quellenliste und Entscheidungsprotokoll abschließen und die Wiederherstellung prüfen + explanation: | + Der Plan kommt zuerst, weil er der Maßstab für alle Tests ist. Die Ordner kommen vor dem ersten Export, sonst landen Originale und Exporte vermischt im Projekt. Prüfmuster vor dem eigenen Material trennen Fehler im Einbau von Fehlern im Klang. Die Nacht vor dem Hörtest trennt Bauen und Urteilen. Am Ende steht die Wiederherstellung, weil erst sie zeigt, dass die Sicherung alles enthält, was du gebaut hast. + resources: + - title: REAPER User Guide 7.81 · Multiple Project Backup Versions (S. 234) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=234 + + - id: was-ist-ein-backup + type: choice + title: Was übersteht einen Plattenausfall? + prompt: | + Die Platte deines PCs fällt aus. Welche Sicherung bringt dir Godot-Projekt, REAPER-Projekte und Rohaufnahmen zurück? + options: + - text: Eine Kopie von Godot-Projekt und Originalen auf einem zweiten Datenträger, aus der du schon einmal wiederhergestellt hast. + correct: true + why: Sie liegt woanders, enthält alles, und du weißt, dass die Wiederherstellung funktioniert. + - text: Ein Commit im Git-Repository des Godot-Projekts. + correct: false + why: Ein lokaler Commit liegt auf derselben Platte. Auch gepusht enthält das Repository nur das Godot-Projekt, nicht die REAPER-Projekte und Rohaufnahmen außerhalb davon. + - text: Die `.RPP-bak`-Dateien von REAPER. + correct: false + why: Sie liegen neben dem Projekt auf derselben Platte und enthalten nur den vorigen Projektstand, keine Aufnahmen. + - text: Die exportierten Ogg- und WAV-Dateien im Godot-Projekt. + correct: false + why: Exporte sind Ergebnisse. Ohne REAPER-Projekte und Rohaufnahmen kannst du sie nicht mehr ändern, nur noch abspielen. + explanation: | + Ein Backup braucht drei Eigenschaften: eine Kopie, an einem anderen Ort, und eine Wiederherstellung, die du ausprobiert hast. Git und `.RPP-bak` sind Werkzeuge für Fassungen. Lokal liegen beide auf derselben Platte, und ein Push sichert nur das Godot-Projekt. Sie helfen, wenn du eine Änderung zurücknehmen willst, nicht wenn die Platte weg ist. Welches Werkzeug du für die Kopie nimmst, entscheidest du. + resources: + - title: REAPER User Guide 7.81 · Project Backup File (S. 29) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=29 + + - id: ki-auftrag + type: choice + title: Was darf die Maschine hier? + prompt: | + Welche Aufträge an eine KI passen zur Regel dieser Werkstatt? Mehrere Antworten richtig. + options: + - text: Schreib ein GDScript-Gerüst, das bei jeder Musik-Anfrage Frame und Clip ins Log schreibt. + correct: true + why: Das ist Technik. Das Gerüst entscheidet nichts über den Klang, es macht deinen Test schneller. Prüfen musst du es trotzdem, wie jeden Code. + - text: Leg eine CSV-Vorlage für das Entscheidungsprotokoll mit leeren Spalten an. + correct: true + why: Eine Vorlage mit Platzhaltern. Was in den Zeilen steht, entscheidest und misst du. + - text: Erzeug eine Atmo als Platzhalter, bis meine eigene Aufnahme fertig ist. + correct: false + why: Generierte Klänge gehören nicht in den Abschnitt, auch nicht als Platzhalter, denn Platzhalter bleiben gern liegen. Zum Testen des Einbaus nimmst du ein Prüfmuster. + - text: Hör dir den Mitschnitt an und sag mir, ob die Mischung stimmt. + correct: false + why: Das ist eine Mischentscheidung. Ob die Mischung trägt, prüfst du im Hörtest gegen deinen Plan und später mit Menschen, die den Abschnitt zum ersten Mal hören. + - text: Schlag mir eine Lautstärke für den Atmo-Bus vor. + correct: false + why: Auch ein einzelner Wert ist eine Mischentscheidung. Den Startpunkt setzt du als Platzhalter selbst und prüfst ihn im Hörtest. + explanation: | + Die Grenze verläuft zwischen Werkzeug und Gestaltung. Ein Log-Gerüst oder eine leere Vorlage beschleunigt Arbeit, deren Ergebnis du bestimmst. Ein generierter Klang, ein Urteil über die Mischung oder ein vorgeschlagener Wert nimmt dir die Entscheidung ab, die diese Werkstatt üben soll. Auch die Frage „Klingt das gut?“ ist so ein Auftrag. + + - id: lautheit-fassung + type: output + title: Fassung 1 gegen das Ziel + prompt: | + Du hast den Abschnitt in Fassung 1 gemessen: −19,4 LUFS-I und −0,8 dBTP True Peak. Dein Platzhalterziel ist −16 LUFS-I, deine Grenze für True Peak −1 dBTP. Das Programm rechnet den Gain bis zum Ziel und prüft den True Peak vor und nach dem Gain gegen die Grenze. Was gibt es aus? + lang: python + code: | + ziel_lufs = -16.0 # Platzhalter + gemessen_lufs = -19.4 + true_peak = -0.8 # dBTP, gemessen + grenze_tp = -1.0 # Platzhalter + + gain = ziel_lufs - gemessen_lufs + neu_tp = true_peak + gain + print(f"{gain:+.1f} dB") + print("vorher:", "über der Grenze" if true_peak > grenze_tp else "ok") + print(f"{neu_tp:+.1f} dBTP") + print("nachher:", "über der Grenze" if neu_tp > grenze_tp else "ok") + expected: | + +3.4 dB + vorher: über der Grenze + +2.6 dBTP + nachher: über der Grenze + hints: + - Ein Gain verschiebt Lautheit und True Peak um dieselben Dezibel. + explanation: | + Bis zum Ziel fehlen 3,4 dB. Schon Fassung 1 liegt mit −0,8 dBTP über der Grenze von −1 dBTP, noch ohne jeden Gain. Ein reiner Gain hebt den True Peak um weitere 3,4 dB, auf +2,6 dBTP. Fassung 1 hat also zu wenig Abstand zwischen Spitzen und Lautheit, um mit Gain allein ans Ziel zu kommen. Was du tust, entscheidest du: das Ziel als Platzhalter überprüfen, die Spitzen bearbeiten oder die Mischung ändern. Ins Protokoll gehören Messwerte, Entscheidung und der Test danach. Wie du misst, steht in [Lautheit, Export, Import](/sound/export-im-spiel). + resources: + - title: REAPER User Guide 7.81 · Dry Run Render Stats, LUFS-I und True Peak (S. 416) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=416 + + - id: ruhe-erwartet + type: output + title: Wann sollte die Ruhe kommen? + prompt: | + Vor dem Hörtest rechnest du aus, was du hören solltest. Ein Lehrbeispiel: Das Diagramm wechselt 3 s nach dem letzten Gegner zurück, auf den nächsten Takt, 4/4 bei 90 BPM (Platzhalter). Der Kampf-Clip läuft seit 0,0 s und wurde nicht neu gestartet; seine Takte zählen also ab 0,0 s. Der letzte Gegner verschwindet bei 20,0 s. Das Programm gibt den erwarteten Wechsel und den Abstand zum letzten Gegner aus. Was steht da? + lang: python + code: | + import math + + BPM = 90 + TAKT = 4 + nachlauf = 3.0 + letzter_gegner = 20.0 + + takt_s = TAKT * 60 / BPM + anfrage = letzter_gegner + nachlauf + wechsel = math.ceil(anfrage / takt_s) * takt_s + print(f"{wechsel:.2f}") + print(f"{wechsel - letzter_gegner:.2f}") + expected: | + 24.00 + 4.00 + hints: + - Ein Takt dauert 4 × 60 / 90 ≈ 2,67 s. Die Taktgrenzen liegen bei 21,33 und 24,00 s. + explanation: | + Der Nachlauf endet bei 23,0 s. Weil der Kampf-Clip bei 0,0 s begonnen hat, liegen seine Taktgrenzen bei 0, 2,67, 5,33 … 21,33 und 24,0 s; die nächste nach 23,0 s ist 24,0 s. Die Ruhe kommt also 4 Sekunden nach dem letzten Gegner, nicht 3. Diese Zahl schreibst du vor dem Hörtest auf. Wirkt die Ruhe im Test zu spät, weißt du, dass Nachlauf und Takt zusammen die Ursache sind, und kannst im Protokoll eins von beiden ändern, nicht beide. + resources: + - title: Godot Docs · AudioStreamInteractive, TRANSITION_FROM_TIME_NEXT_BAR + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html#class-audiostreaminteractive-constant-transition-from-time-next-bar + + - id: zwei-aenderungen + type: bug + title: Besser, aber warum? + prompt: | + Nach Überarbeitung 2 klingt der Abschnitt besser. Im Protokoll kannst du aber nicht ablesen, welche Änderung das bewirkt hat. Finde den Eintrag, der gegen die Regel der Werkstatt verstößt. + lang: prose + code: | + Fassung 1, Hörtest 1: Die Schritte gehen unter der Atmo unter. + Überarbeitung 1: Atmo-Bus 3 dB leiser. + Fassung 2, Hörtest 2: Schritte hörbar, die Atmo wirkt jetzt dünn. + Überarbeitung 2: Atmo-Bett neu gerendert und Ducking der Musik verlängert. + Fassung 3, Hörtest 3: Schritte hörbar, Atmo trägt den Raum. + lines: [4] + explanation: | + Überarbeitung 2 ändert zwei Dinge auf einmal: das Atmo-Bett und das Ducking der Musik. Beides kann den Eindruck in Hörtest 3 erklären, und wenn eins davon an anderer Stelle schadet, weißt du nicht, welches du zurücknehmen musst. Getrennt wären es zwei Einträge mit zwei Hörtests oder wenigstens zwei A/B-Vergleichen. Überarbeitung 1 ändert genau eine Sache, so wie es sein soll. + resources: + - title: Godot Docs · Audio buses + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html + + - id: rohaufnahmen-im-projekt + type: bug + title: Der Import dauert ewig + prompt: | + Nach jedem neuen Aufnahmetag braucht Godot beim Öffnen des Projekts lange, und der Ordner `.godot/imported` wächst um Hunderte Megabyte, obwohl im Spiel nur wenige Dateien benutzt werden. Finde die Zeile im Ordneraufbau, die das verursacht. + lang: text + code: | + spiel/ + project.godot + audio/sfx/ + audio/musik/ + audio/roh/ (Rohaufnahmen, alle Takes) + scenes/ + lines: [5] + explanation: | + Godot importiert alle Dateien, die es kennt, aus dem ganzen Projektordner, auch jede Rohaufnahme. Die Rohaufnahmen gehören zu den Originalen und damit außerhalb des Godot-Projekts. Sollen sie doch im Projekt liegen, verhindert eine leere Datei `.gdignore` im Ordner `audio/roh/` den Import. Es gibt also zwei richtige Korrekturen; beide beginnen an dieser Zeile. + resources: + - title: Godot Docs · Project organization, Ignoring specific folders + url: https://docs.godotengine.org/en/stable/tutorials/best_practices/project_organization.html#ignoring-specific-folders + + - id: alte-datei-spielt + type: bug + title: Die Änderung kommt nicht an + prompt: | + Du hast die Atmo in REAPER geändert und neu gerendert. Im Spiel klingt sie wie vorher. Im Ordner `audio/atmo/` liegen jetzt `atmo_bett_01.ogg` und `atmo_bett_01-001.ogg`. Finde die Einstellung im Render-Blatt, die das verursacht. + lang: text + code: | + Quelle: Selected media items + Dateiname: atmo_bett_01 + Verzeichnis: spiel/audio/atmo + Format: OGG Vorbis + Silently increment filenames: an + Save copy of project: an + lines: [5] + fix: + - line: 5 + answers: + - "Silently increment filenames: aus" + - "Silently increment filenames: Aus" + - "Silently increment filenames aus" + - "Silently increment filenames: off" + - "Silently increment filenames off" + - "Silently increment filenames: nein" + explanation: | + Laut Guide hängt „Silently increment filenames“ eine Nummer an, wenn der Name schon existiert. Die neue Fassung landet als `atmo_bett_01-001.ogg` neben der alten, und das Spiel spielt weiter `atmo_bett_01.ogg`. Für Exporte ins Spiel schaltest du die Option aus, damit die Datei ersetzt wird. Fassungen hebst du stattdessen über die Projektkopie auf, die „Save copy of project“ zu jedem Export ablegt, und über deine Sicherung. Die Option steht auch in [REAPER als Werkbank](/sound/reaper-werkbank) unter den Einstellungen, die über den Export entscheiden. + resources: + - title: REAPER User Guide 7.81 · Render to File, Silently increment filenames (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 + + - id: protokollzeile + type: explain + title: Was eine Protokollzeile leistet + prompt: | + Ein Lehrbeispiel aus einem Entscheidungsprotokoll, kein Vorschlag für deinen Abschnitt. Erkläre, wozu jede der fünf Spalten da ist und was fehlen würde, wenn eine leer bliebe. + lang: text + code: | + Entscheidung: Übergang B->A auf + nächsten Takt statt sofort + Grund: Hörtest 1, Schnitt mitten + in der Phrase + Alternative: sofort mit Blende + Test: Prüfloop + Log, 5 Anfragen + Ergebnis: Wechsel 0,3 bis 1,9 s + nach Anfrage; Schnitt weg + points: + - Der Grund verbindet die Entscheidung mit einer Beobachtung aus einem Hörtest oder mit dem Plan, nicht mit einem Gefühl. + - Die Alternative zeigt, dass es eine Wahl war, und ist der erste Kandidat, wenn die Entscheidung sich später als falsch herausstellt. + - Der Test ist so genau beschrieben, dass du ihn wiederholen kannst. + - Das Ergebnis enthält, was gemessen oder gehört wurde, auch den Preis, hier die Verzögerung. + explanation: | + Ohne Grund lässt sich die Entscheidung später nicht prüfen; du weißt nicht mehr, welches Problem sie lösen sollte. Ohne Alternative sieht sie aus wie die einzige Möglichkeit, und beim nächsten Problem fängst du von vorn an. Ohne genauen Test kannst du nach einer weiteren Änderung nicht noch einmal nachsehen, ob sie noch hält. Und ohne Ergebnis bleibt nur die Erwartung. Hier steht auch der Preis drin: Der Wechsel wartet bis zu 1,9 Sekunden. Ob dir das recht ist, entscheidest du beim nächsten Hörtest, und du weißt, wonach du hören musst. + + - id: abschnittsplan + type: practice + title: Der Plan vor dem Bauen + prompt: | + Wähle einen Abschnitt deines Spiels, zwei bis vier Bildschirme groß, und schreib den Abschnittsplan in der Form aus der Lektion. Übernimm, was du schon hast: Cue-Liste aus [Vom Ereignis zur Cue-Liste](/sound/klangplan), Diagramm aus [Musik für Zustände](/sound/musik-zustaende), Busplan und Prioritäten aus [Mischung und Stille](/sound/mischung-stille). Was in deinem Abschnitt klingt, entscheidest du; Werte sind Platzhalter. Speichere den Plan als eigene Datei bei den Originalen. + lang: prose + starter: | + Abschnitt (Name, Bildschirme): + Aktion (Name, Auslöser): + Cue-Liste (ID, Auslöser, One-Shot/Loop, Varianten, Priorität, Bus): + Musik (Zustände, Diagramm, Nachlauf): + Atmo (Bett, Spot-Klänge): + Stille (wo, warum): + Busse und Ducking: + Prioritäten: + Lautheit (Platzhalter): + Bewusst nicht in diesem Abschnitt: + checklist: + - Der Plan enthält genau eine Aktion, ein Stück mit mindestens zwei Zuständen und eine Atmo. + - Jeder Eintrag der Cue-Liste hat einen Auslöser, eine Priorität und einen Bus. + - Jeder Pfeil im Diagramm hat Auslöser, Zeitpunkt, Ziel und Art. + - Mindestens eine Stelle mit bewusster Stille ist notiert. + - Lautheitsziel und True-Peak-Grenze sind als Platzhalter mit Einheit notiert. + - Unter „Bewusst nicht“ steht mindestens ein Punkt. + - Der Plan liegt als Datei bei den Originalen, mit Datum. + explanation: | + Der Plan begrenzt den Umfang und macht jeden Hörtest prüfbar. Die Zeile „Bewusst nicht in diesem Abschnitt“ ist kein Beiwerk: Sie fängt die Ideen auf, die beim Bauen kommen, ohne dass der Abschnitt wächst. Lautheitsziel und Grenze als Platzhalter heißen: Du startest mit einer Zahl und prüfst sie im Hörtest, statt ihr zu gehorchen. + resources: + - title: Godot Docs · Audio buses + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html + + - id: ordner-und-sicherung + type: practice + title: Ordner, Projektkopien und eine geprüfte Sicherung + prompt: | + Leg die Ordner für Originale und Exporte an, schalte in REAPER „Keep multiple versions“ ein und im Render-Dialog „Save copy of project to outfile.xxx.RPP“, und richte eine Sicherung auf einem zweiten Datenträger ein. Stell danach einmal wieder her: Projekt aus der Sicherung öffnen, eine Datei neu rendern, im Spiel hören. Trag hier Ordneraufbau, Einstellungen und Ergebnis der Wiederherstellung ein. Namen und Pfade sind Platzhalter. + lang: text + starter: | + Ordneraufbau: + + REAPER (Preferences, Render-Dialog): + + Godot-Projekt (Git, LFS, .gdignore): + + Sicherung (wohin, womit, wie oft): + + Wiederherstellung (Datum, was geprüft, Ergebnis): + solution: | + Ordneraufbau: + audio-quellen/ (außerhalb des Godot-Projekts) + reaper/ roh/ protokolle/ plan/ + spiel/ (Godot-Projekt) + audio/sfx/ audio/musik/ audio/atmo/ + + REAPER: + Preferences, Project: Keep multiple versions an + Render-Dialog: Save copy of project an, + Silently increment filenames aus + + Godot-Projekt: + Git-Repository, Audio über Git LFS + keine Rohaufnahmen im Projekt + + Sicherung: + audio-quellen/ und spiel/ auf , + mit , + + Wiederherstellung: + : abschnitt-1.rpp aus der Sicherung geöffnet, + atmo_bett_01.ogg neu gerendert, im Spiel gehört: ok + checklist: + - Originale und Exporte liegen in getrennten Ordnern; im Godot-Projekt liegen keine Rohaufnahmen ohne `.gdignore`. + - „Keep multiple versions“ ist an, und nach zweimal Speichern gibt es mehrere Fassungen in der `.RPP-bak`. + - Zu einem Testexport liegt eine Projektkopie mit dem Zustand beim Rendern. + - Die Sicherung liegt auf einem anderen Datenträger als das Projekt und enthält Originale und Godot-Projekt. + - Du hast einmal aus der Sicherung wiederhergestellt und das Ergebnis mit Datum notiert. + explanation: | + Die Musterlösung zeigt die Form, nicht deine Werkzeuge: Welches Programm die Kopie macht und wie oft, entscheidest du. Entscheidend ist die Wiederherstellung. Eine Sicherung, aus der du nie zurückgeholt hast, kann leer sein, alte Stände enthalten oder einen Ordner auslassen, und du merkst es erst, wenn du sie brauchst. Die REAPER-Optionen beantworten eine andere Frage: aus welchem Projektstand ein Export stammt. + resources: + - title: REAPER User Guide 7.81 · Multiple Project Backup Versions (S. 234) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=234 + - title: Godot Docs · Version control systems, Git LFS + url: https://docs.godotengine.org/en/stable/tutorials/best_practices/version_control_systems.html#git-lfs + + - id: fassung-1 + type: practice + title: Fassung 1 im Spiel + prompt: | + Bau Fassung 1 in deine Testumgebung, erst mit Prüfmustern, dann mit deinem Material: die Aktion mit ihren Varianten auf dem Auslöser aus deinem Plan, das Stück mit mindestens zwei Zuständen und den Übergängen aus deinem Diagramm, die Atmo aus Bett und Spot-Klängen. Prüf jeden Übergang mit Log gegen deine Rechnung. Misch nach deinen Prioritäten, setz die geplante Stille und miss die Lautheit des Abschnitts. Trag hier ein, was du gebaut, gemessen und notiert hast. + lang: prose + starter: | + Testumgebung: + + Prüfmuster (Klick auf Auslöser, Prüfloop): Ergebnis + + Aktion und Varianten (Dateien, Auslöser): + + Stück (Dateien, Zustände, Übergänge): + + Übergänge im Log (Anfrage, Wechsel, Rechnung): + + Atmo (Bett, Spots): + + Busse und Prioritäten (was ist wann vorn?): + + Stille (wo): + + Lautheit Fassung 1 (LUFS-I, True Peak): + checklist: + - Im Spiel ist die Aktion mit mindestens zwei eigenen Varianten zu hören, auf dem Auslöser aus deinem Plan. + - Im Spiel sind mindestens zwei Zustände deines Stücks zu hören, und jeder Übergang aus dem Diagramm ist mindestens einmal ausgelöst worden. + - Für jeden Übergang stehen Anfrage- und Wechselzeit aus dem Log neben deiner Rechnung. + - Bett und mindestens ein Spot-Klang der Atmo sind im Spiel zu hören. + - Jeder Player hängt an einem Bus aus deinem Busplan. + - Die Lautheit von Fassung 1 ist mit Messwert und Einheit notiert, neben deinem Platzhalterziel. + - Fassung 1 ist gespeichert, mit Projektstand und Exporten. + explanation: | + Prüfmuster zuerst, weil du sonst bei jedem Fehler zwei Ursachen hast. Das Log macht aus „die Musik kommt irgendwie spät“ eine Zahl, die du mit deiner Rechnung vergleichen kannst. Die Lautheit von Fassung 1 ist kein Urteil über die Fassung, sondern der Ausgangspunkt für die Überarbeitungen. Ob die Mischung trägt, entscheidet der Hörtest, nicht der Messwert. + resources: + - title: Godot Docs · AudioStreamInteractive + url: https://docs.godotengine.org/en/stable/classes/class_audiostreaminteractive.html + + - id: hoertest-ueberarbeitung-1 + type: practice + title: Hörtest 1 und Überarbeitung 1 + prompt: | + Mindestens eine Nacht nach Fassung 1. Hör den Abschnitt im Spiel mit Kopfhörern und über einen zweiten Lautsprecher, laut und sehr leise. Notier jede Stelle, an der etwas nicht so ankommt wie geplant, ohne beim Hören zu ändern. Überarbeite dann, eine Änderung je Protokolleintrag, und speichere Fassung 2 getrennt von Fassung 1. + lang: prose + starter: | + Hörtest 1 (Datum, Geräte, Lautstärken): + + Beobachtungen (Stelle, was ankommt, was laut Plan ankommen soll): + + Überarbeitung 1 (je Zeile: Entscheidung, Grund, Alternative, Test, Ergebnis): + + Fassung 2 gespeichert (wo): + checklist: + - Zwischen Fassung 1 und Hörtest 1 lag mindestens eine Nacht. + - Du hast mit mindestens zwei Wiedergabegeräten und mindestens zwei Lautstärken gehört und beides notiert. + - Jede Beobachtung nennt die Stelle im Abschnitt und bezieht sich auf einen Punkt im Plan. + - Jede Änderung steht als eigener Eintrag mit Entscheidung, Grund, Alternative, Test und Ergebnis im Protokoll. + - Kein Eintrag ändert mehr als eine Sache. + - Fassung 1 ist unverändert gespeichert, Fassung 2 getrennt davon. + explanation: | + Hören und Ändern sind getrennt, damit die erste auffällige Stelle nicht alles andere überdeckt. Leise hörst du, was zuerst verschwindet, und das ist oft das, was die Prioritäten im Plan schützen sollten. Der zweite Lautsprecher zeigt, was nur auf deinen Kopfhörern funktioniert. Eine Änderung je Eintrag kostet mehr Hörtests, aber nur so weißt du nach Fassung 2, was gewirkt hat. + resources: + - title: Godot Docs · Audio buses, Decibel scale + url: https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html#decibel-scale + + - id: hoertest-ueberarbeitung-2-abschluss + type: practice + title: Hörtest 2, Überarbeitung 2 und Abschluss + prompt: | + Wiederhol Hörtest und Überarbeitung mit Fassung 2 und speichere Fassung 3. Schließ dann ab: Quellenliste für jede fremde Datei im Abschnitt, Entscheidungsprotokoll vollständig, Sicherung erneuert und einmal wiederhergestellt. Leg Fassung 1 und Fassung 3 nebeneinander, im Spiel nacheinander gespielt, und schreib eine kurze Reflexion: drei Änderungen mit Grund und Wirkung, eine Stelle, die du bewusst nicht geändert hast, und was du beim nächsten Abschnittsplan anders festlegst. + lang: prose + starter: | + Hörtest 2 (Datum, Geräte, Lautstärken, Beobachtungen): + + Überarbeitung 2 (Einträge): + + Fassung 3 gespeichert (wo): + + Quellenliste (Datei, Herkunft, Lizenz mit Version, Nachweis): + + Sicherung und Wiederherstellung (Datum, Ergebnis): + + Reflexion – Änderung 1, 2, 3 (Stelle, vorher, nachher, Grund): + + Bewusst nicht geändert: + + Für den nächsten Abschnittsplan: + checklist: + - Fassung 1, 2 und 3 sind getrennt gespeichert und lassen sich im Spiel abspielen. + - Jede Änderung aus Überarbeitung 2 steht als eigener Eintrag mit Test und Ergebnis im Protokoll. + - Jede Datei im Abschnitt, die nicht von dir stammt, steht mit Herkunft, Lizenz mit Version und Nachweis in der Quellenliste; jede eigene ist als eigen markiert. + - Die Sicherung ist nach Fassung 3 erneuert und einmal wiederhergestellt worden, mit Datum. + - Die Reflexion nennt drei Änderungen mit Stelle, vorher, nachher und Grund, und jeder Grund verweist auf einen Hörtest oder den Plan. + - Du nennst eine Stelle, die du bewusst nicht geändert hast, und einen Punkt für den nächsten Plan. + explanation: | + Der zweite Durchgang zeigt, ob die Änderungen aus dem ersten halten, wenn du mit Abstand hörst. Der Vergleich von Fassung 1 und 3 im Spiel macht sichtbar, wie viel sich bewegt hat, und die bewusst unveränderte Stelle schützt vor dem Reflex, alles zu glätten. Quellenliste und Protokoll sind das, was aus dem Abschnitt ein Portfolio-Stück macht: Jede Datei hat eine Herkunft, jede Entscheidung einen Grund. Der Punkt für den nächsten Plan fließt in deinen nächsten Abschnitt ein. + resources: + - title: REAPER User Guide 7.81 · Render to File, Save copy of project (S. 415) + url: https://www.reaper.fm/userguide/ReaperUserGuide781c.pdf#page=415 diff --git a/content/sound/projects.yaml b/content/sound/projects.yaml new file mode 100644 index 0000000..2b283e0 --- /dev/null +++ b/content/sound/projects.yaml @@ -0,0 +1,253 @@ +projects: + - id: hoeranalyse-abschnitt + title: Höranalyse eines veröffentlichten Spielabschnitts + level: 1 + hours: 6 + summary: Du hörst einen kurzen Abschnitt eines veröffentlichten Spiels mit Kopfhörern Klang für Klang auseinander, rekonstruierst daraus seine Cue-Liste und seine Musikzustände und schreibst auf, was der Mix dir sagt und was er weglässt. Geht größtenteils unterwegs. + brief: | + Wähle zwei bis drei Minuten aus einem veröffentlichten Spiel, am besten aus einer Draufsicht mit Kampf und Erkundung. Grundlage ist ein Video, das du selbst aufnimmst oder das der Hersteller veröffentlicht hat. Ziel ist eine Analyse, die ein anderer nachprüfen kann: Jede Zeile hat eine Zeitmarke im Video. + + Hör mit Kopfhörern, einmal laut und einmal leise. Am Handy gehen Protokoll und Urteil; Zeitmarken auf die Zehntelsekunde setzt du leichter am PC. + + Grundlage sind [Kritisch hören](/sound/hoeren-analysieren), [Vom Ereignis zur Cue-Liste](/sound/klangplan), [Musik für Zustände](/sound/musik-zustaende) und [Mischung und Stille](/sound/mischung-stille). + skills: [kritisch-hoeren, cue-liste, prioritaet, musikzustaende, analyse] + steps: + - id: abschnitt-waehlen + title: Abschnitt und Quelle festlegen + detail: Notiere Spiel, Plattform, Abschnitt, Link und Länge des Videos. Prüfe, ob im Video kommentiert oder Musik nachträglich unterlegt wurde; dann taugt es nicht. + - id: hoerprotokoll + title: Hörprotokoll mit Zeitmarken + detail: Jede Zeile hat Zeit, Klang, Quelle im Bild oder außerhalb, Funktion (Rückmeldung, Warnung, Ort, Stimmung) und eine Priorität von 1 bis 3. Stille notierst du auch. + - id: cue-liste-rekonstruieren + title: Cue-Liste rekonstruieren + detail: Fasse gleiche Klänge zu Ereignissen zusammen und schätze, wie viele Varianten jedes Ereignis hat. Schreib dazu, woran du Varianten erkennst und wo du unsicher bist. + - id: musik-zustaende + title: Musikzustände und Übergänge + detail: Zeichne die Zustände der Musik als Diagramm und notiere bei jedem Wechsel, was ihn ausgelöst hat und ob er auf einem Schlag, einem Takt oder sofort passiert. + - id: mix-lesen + title: Den Mix lesen + detail: Markiere drei Stellen, an denen ein Klang einen anderen verdrängt oder etwas leiser wird. Beschreibe, welche Information dadurch hörbar bleibt. + - id: leise-hoeren + title: Leise und am Handylautsprecher hören + detail: Hör den Abschnitt noch einmal sehr leise und einmal über den Handylautsprecher. Was verschwindet, was bleibt? + - id: fragen-notieren + title: Eigene Fragen notieren + detail: Schreib drei Fragen auf, die diese Analyse für dein eigenes Spiel aufwirft. Antworten brauchst du nicht. + stretch: + - Denselben Abschnitt in einem zweiten Spiel desselben Genres analysieren und die Prioritäten vergleichen. + - Eine zweite Person das Hörprotokoll unabhängig schreiben lassen und die Prioritäten vergleichen. + portfolio: | + Eine Höranalyse zeigt, dass du Ton in Funktionen und Prioritäten beschreiben kannst, nicht nur in Geschmack. Zeig Protokoll, Cue-Liste und Zustandsdiagramm nebeneinander. Im Gespräch fragen Audio-Leute gern, welchen Klang du zuerst streichen würdest, wenn das Spiel zu voll klingt. + + - id: eigene-geraeuschbibliothek + title: Eine kleine eigene Geräuschbibliothek + level: 1 + hours: 10 + summary: Du nimmst mit dem Handy eine kleine Sammlung eigener Geräusche auf, protokollierst jede Aufnahme, säuberst, schneidest und benennst sie in REAPER und legst eine Quellenliste an, mit der du jede Datei später wiederfindest. + brief: | + Eine eigene Bibliothek ist dein Rohstoff. Sie muss nicht groß sein, aber jede Datei soll nachvollziehbar sein: Wann, wo und womit ist sie entstanden, was wurde bearbeitet, und unter welchem Namen liegt sie. Was du aufnimmst, entscheidest du: Flächen, Gegenstände, Räume, Kleidung, Wasser, Wind. + + Grundlage sind [Feldaufnahme mit dem Handy](/sound/feldaufnahme), [Quellen und Lizenzen](/sound/quellen-lizenzen) und [REAPER als Werkbank](/sound/reaper-werkbank). + skills: [feldaufnahme, aufnahmeprotokoll, reaper, saeubern, benennung, quellenliste] + steps: + - id: app-pruefen + title: Aufnahme-App prüfen + detail: Notiere App, Format, Abtastrate und ob Pegelautomatik und Rauschunterdrückung aus sind. Mach eine Probeaufnahme von Stille und eine von einem Klatscher und hör beide am PC an. + - id: aufnahmeplan + title: Aufnahmeplan + detail: Liste zehn bis zwanzig Klänge, die du aufnehmen willst, mit Ort und Zeit, zu der es dort ruhig ist. + - id: aufnehmen + title: Aufnehmen mit Ansage und Room Tone + detail: Jede Aufnahme beginnt mit einer kurzen Ansage, jeder Ort bekommt 30 Sekunden Room Tone. Pro Klang mehrere Takes aus unterschiedlichem Abstand. + - id: protokoll + title: Aufnahmeprotokoll + detail: Datum, Ort, Gerät, Abstand, Take-Nummer und Notizen zu Störungen für jede Aufnahme, am besten direkt nach dem Take. + - id: saeubern + title: Säubern und schneiden + detail: In REAPER Hochpass, Rauschreduktion mit dem Room Tone, Fades und Schnitt mit Bordmitteln. Notiere, was du bei welchem Klang gemacht hast. + - id: regionen-rendern + title: Regionen und Render + detail: Eine Region je Klang, Render über die Region Render Matrix mit einem Namensschema, das du festlegst. Abtastrate und Bittiefe stehen im Protokoll. + - id: quellenliste + title: Quellenliste + detail: Eine Datei mit einer Zeile je Klang – Datei, Herkunft, Aufnahmedatum, Ort, Bearbeitung, Lizenz „eigene Aufnahme“. Rohaufnahmen und REAPER-Projekt liegen getrennt von den Exporten. + - id: sichern + title: Sichern + detail: Rohaufnahmen, Projekt und Exporte liegen in einer Sicherung außerhalb des Rechners. Git allein ist kein Backup. + stretch: + - Dieselbe Fläche mit drei Schuhwerken aufnehmen und die Unterschiede beschreiben. + - Eine eigene Impulsantwort eines Raums aufnehmen und in ReaVerb laden. + portfolio: | + Eine kleine, sauber dokumentierte Bibliothek zeigt Arbeitsweise: Planung, Protokoll, Säubern, Benennung. Zeig ein Vorher-nachher-Beispiel und die Quellenliste. Wer sieht, dass du jede Datei zurückverfolgen kannst, traut dir auch Lizenzfragen zu. + + - id: aktion-varianten + title: Eine Aktion, mehrere Varianten + level: 2 + hours: 10 + summary: Du baust für eine selbst gewählte Aktion einen eigenen Klang aus Schichten, erzeugst mehrere Varianten, legst die Transiente auf das richtige Bild und vergleichst im Spiel zwei Fassungen in protokollierten Hörtests. + brief: | + Eine Aktion, die oft vorkommt, verrät schnell, ob ihr Klang trägt: ein Schritt, ein Ausweichen, ein Angriff, ein Einsammeln. Welche Aktion es ist und wie sie klingt, entscheidest du. Der Kurs gibt dir das Werkzeug und die Messung. + + Grundlage sind [Vom Ereignis zur Cue-Liste](/sound/klangplan), [Hüllkurve, Frequenz, Dynamik und Raum](/sound/klangformung), [Layering und Varianten](/sound/layering-varianten) und für den Einbau [Audio zuordnen](/godot/audio). Die Bildnummern der Aktion kommen aus deiner Animation ([Animation im Spiel](/animation/animation-im-spiel)) oder aus der Testszene des Combat-Bereichs ([Testszene: Angriff](/combat/testszene-angriff)). + skills: [layering, varianten, huellkurve, synchronisation, godot-audio, hoertest] + steps: + - id: aktion-waehlen + title: Aktion und Bildnummern + detail: Wähle die Aktion und notiere ihre Dauer in Frames und das Bild, in dem der Klang „passiert“. Schreib in einem Satz, was der Klang dem Spieler sagen soll. + - id: cue-zeile + title: Cue-Zeile schreiben + detail: Name, Auslöser, One-Shot oder Loop, Länge, Priorität, Bus, Zahl der Varianten mit Begründung aus der Häufigkeit. + - id: schichten-bauen + title: Schichten bauen + detail: Mindestens zwei Schichten aus eigenem Material in REAPER, zeitlich ausgerichtet, Phase geprüft. Notiere, welche Schicht welche Aufgabe hat. + - id: varianten + title: Varianten erzeugen + detail: Mindestens vier Varianten aus unterschiedlichen Takes oder Kombinationen, mit gleicher Lautheit. Render über Regionen. + - id: einbauen + title: Im Spiel einbauen + detail: Die Varianten in einen `AudioStreamRandomizer`, Auslösen im richtigen Bild. Prüfe mit einem Log oder einer Aufnahme, dass die Transiente im geplanten Frame liegt. + - id: ab-vergleich + title: Zwei Fassungen vergleichen + detail: Fassung A und B unterscheiden sich in genau einer Sache. Hör beide im Spiel mehrmals hintereinander, notiere vorher deine Erwartung und danach, was du gehört hast. + - id: entscheiden + title: Entscheiden und festhalten + detail: Welche Fassung bleibt, warum, und was du als Nächstes testen würdest. Beide Fassungen bleiben als Dateien erhalten. + stretch: + - Die Varianten zur Laufzeit aus Schichten kombinieren statt vorzurendern und beide Wege vergleichen. + - Eine zweite Person die Aktion fünf Minuten spielen lassen und fragen, ob ihr eine Wiederholung auffällt. + portfolio: | + Ein Klang mit Varianten, die auf ein Bild genau sitzen, zeigt Handwerk und Umsetzung zugleich. Zeig im Video die Aktion zehnmal hintereinander und daneben Cue-Zeile und Protokoll. Im Gespräch fragen Sounddesigner, wie du verhinderst, dass derselbe Klang zweimal hintereinander kommt. + + - id: stueck-zwei-zustaende + title: Ein eigenes Stück für zwei Zustände + level: 2 + hours: 12 + summary: Du bereitest ein eigenes Musikstück so auf, dass es zwei Spielzuständen folgt, setzt es mit AudioStreamInteractive in Godot um und misst, wann und wie jeder Übergang tatsächlich erklingt. + brief: | + Ein Stück, das als Ganzes gut ist, muss im Spiel an Stellen umschalten, die du nicht kennst. Dieses Projekt prüft, ob deins das kann. Welches Stück, welche Zustände und welche Übergänge, entscheidest du. Neu komponieren musst du nicht; ein vorhandenes eigenes Stück ist der vorgesehene Weg. + + Grundlage sind [Musik für Zustände](/sound/musik-zustaende), [Loops, Hallfahnen, Stems](/sound/loops-stems) und [Musik in Godot](/sound/godot-musik). + skills: [interaktive-musik, loops, stems, uebergaenge, audiostreaminteractive, messen] + steps: + - id: zustaende-festlegen + title: Zustände und Auslöser + detail: Zwei Zustände, die Ereignisse, die zwischen ihnen wechseln, und eine Regel gegen schnelles Hin- und Herschalten. Als Zustandsdiagramm. + - id: stueck-pruefen + title: Stück prüfen + detail: Tempo, Taktart, Länge der Abschnitte in Takten. Welche Teile taugen als Loop, welche als Übergang? + - id: loops-rendern + title: Loops und Stems rendern + detail: Loops auf Taktgrenzen mit Hallfahne am Anfang, Länge in Samples nachgerechnet. Für eine vertikale Schicht gleich lange Stems. + - id: uebergaenge-rendern + title: Übergänge und Stinger + detail: Mindestens ein Übergangsstück oder ein Stinger, gerendert und benannt. + - id: godot-einrichten + title: In Godot einrichten + detail: Ogg-Import mit Tempo und Takt, `AudioStreamInteractive` mit Clips und Übergangstabelle. Umschalten per Spielereignis und per Taste. + - id: messen + title: Übergänge messen + detail: Für jeden Übergang drei Anfragen zu unterschiedlichen Zeitpunkten. Notiere Anfragezeit, erwarteten und tatsächlichen Wechsel, gemessen im Log oder in einer Aufnahme. + - id: hoeren + title: Im Spiel hören + detail: Spiel den Abschnitt zehn Minuten. Notiere jeden Übergang, der dich stört, und was ihn auslöst. + - id: ueberarbeiten + title: Überarbeiten + detail: Eine Änderung je Runde, mindestens zwei Runden. Alte Fassungen bleiben erhalten. + stretch: + - Einen dritten Zustand als vertikale Schicht mit `AudioStreamSynchronized` ergänzen. + - Den Prüfloop aus Metronom-Klicks gegen dein Stück tauschen und die Messung wiederholen. + portfolio: | + Ein Video mit sichtbarem Zustandswechsel, in dem die Musik auf dem Takt folgt, zeigt mehr als jedes Stück allein. Daneben gehören Zustandsdiagramm und Übergangstabelle. Im Gespräch fragen Komponisten und Audio Directors, was passiert, wenn der Spieler im falschen Moment umschaltet. + + - id: abschluss-audioabschnitt + title: "Abschlussprojekt: Audio-Test für einen Spielprototyp" + capstone: true + level: 3 + hours: 40 + summary: Ein kleines Studio schickt dir einen Audio-Test. Du vertonst einen kurzen spielbaren Abschnitt mit eigenen Klängen, einer Atmo und einem eigenen Stück in mindestens zwei Zuständen, mischst nach Priorität, stimmst die Lautheit ab und lieferst Cue-Liste, Quellenliste, Entscheidungsprotokoll und ein kurzes Video. Jeder Klang und jede Mischentscheidung liegt bei dir. + brief: | + ## Auftrag + + Mira Petersen leitet ein Indie-Studio mit fünf Leuten in Hamburg. Das Team baut ein Actionspiel in der Draufsicht in Godot 4.7. Für die Stelle „Sound Designer (m/w/d)“ schickt sie dir einen Audio-Test. Ihre Lage: + + - Der Prototyp hat nur Platzhalter-Klicks. Tester sagen, das Spiel fühle sich „leer“ an. + - Niemand im Team weiß, welche Klänge das Spiel überhaupt braucht oder wie laut sie sein sollen. + - Musik gibt es, aber sie läuft als ein Stück durch und ignoriert, was passiert. + + Gesucht ist kein fertiger Soundtrack, sondern ein Beleg, dass du einen Abschnitt planst, vertonst, einbaust, mischst und so dokumentierst, dass das Team weiterarbeiten kann. Was der Abschnitt zeigt, wie er klingt und welche Musik ihn trägt, gibt dir niemand vor. Wenn du willst, ist er dein erster Audioabschnitt für Project Delicate Flower; der Kurs schlägt dafür keine Klänge vor. + + ## Was du lieferst + + 1. **Ein spielbarer Abschnitt** in Godot 4.7, etwa die Testszene des Combat-Bereichs oder eine eigene Szene: mindestens eine Aktion mit eigenen Varianten, eine Atmo, ein eigenes Stück in mindestens zwei Zuständen mit Übergängen. + 2. **`CUE-LISTE.md`:** alle Klänge mit Auslöser, Varianten, Priorität, Bus und Status. + 3. **`QUELLEN.md`:** eine Zeile je Datei mit Herkunft, Lizenz und Bearbeitung. + 4. **`ENTSCHEIDUNGEN.md`:** mindestens acht Einträge mit Entscheidung, Grund, verworfener Alternative, Test und Ergebnis. + 5. **`HOERTEST.md`:** Hörprotokolle mit mindestens drei Wiedergabegeräten und zwei Lautstärken. + 6. **Originale und Exporte getrennt:** REAPER-Projekte und Rohaufnahmen in einem Ordner, gerenderte Dateien in einem anderen. + 7. **Ein Video** von höchstens 90 Sekunden mit sichtbarem Zustandswechsel. + + ## Technischer Rahmen + + Godot 4.7, REAPER 7 mit Bordmitteln, eigene Plugins nur, wenn sie bei dir stabil laufen. Den Code für das Einbauen darf eine KI schreiben, auch nach deiner Vorgabe. Klänge, Musik, Mischung und das Urteil über die Wirkung sind deine. Kein generiertes Audio; fremdes Material nur mit dokumentierter Lizenz. Das Lautheitsziel legst du selbst fest und begründest es. + + Grundlage sind alle Module dieses Bereichs, besonders [Vom Ereignis zur Cue-Liste](/sound/klangplan), [Layering und Varianten](/sound/layering-varianten), [Musik in Godot](/sound/godot-musik), [Mischung und Stille](/sound/mischung-stille), [Lautheit, Export und Hörtest](/sound/export-im-spiel) und die [Werkstatt](/sound/werkstatt-audioabschnitt). + skills: [sounddesign, interaktive-musik, mischung, lautheit, godot-audio, dokumentation, lizenzen] + steps: + - id: abschnitt-festlegen + title: Abschnitt und Ziel + detail: Welcher Abschnitt, wie lang, was passiert darin? Schreib in einem Satz, was ein Spieler danach über den Abschnitt gehört haben soll. + - id: cue-liste + title: Cue-Liste + detail: Alle Ereignisse mit Auslöser, Bildnummer oder Signal, Varianten, Priorität und Bus, bevor du einen Klang baust. + - id: aufnehmen-bauen + title: Klänge aufnehmen und bauen + detail: Eigene Aufnahmen, Schichten und Varianten nach der Cue-Liste. Jede Datei bekommt sofort eine Zeile in `QUELLEN.md`. + - id: atmo + title: Atmo + detail: Ein nahtloses Bett und Spot-Klänge, die das Spiel zufällig verteilt. + - id: musik-aufbereiten + title: Musik aufbereiten + detail: Eigenes Stück als Loops, Stems und Übergänge, Zustände und Auslöser als Diagramm. + - id: einbauen + title: Einbauen + detail: Busse, Varianten, `AudioStreamInteractive` und Auslöser im Spiel. Jede Bildnummer aus der Cue-Liste ist geprüft. + - id: mischen + title: Mischen nach Priorität + detail: Prioritätentabelle, Ducking wo nötig, bewusste Stille an mindestens einer Stelle. Begründung in `ENTSCHEIDUNGEN.md`. + - id: lautheit + title: Lautheit abstimmen + detail: Lautheitsziel festlegen, alle Exporte messen und angleichen, True Peak prüfen. Messwerte stehen in der Cue-Liste. + - id: hoertest + title: Hörtest + detail: Kopfhörer, Laptop-Lautsprecher, Handy oder ein drittes Gerät, je laut und leise. Notiere, welche Information verloren geht. + - id: fremdtest + title: Fremdtest + detail: Eine Person spielt den Abschnitt ohne Erklärung. Frag danach, was sie gehört hat und was ihr gefehlt hat. + - id: ueberarbeiten + title: Zwei Überarbeitungsrunden + detail: Jede Runde ändert gezielt, was Hörtest oder Fremdtest gezeigt haben, und hat einen Eintrag in `ENTSCHEIDUNGEN.md`. + - id: abgabe + title: Abgabe zusammenstellen + detail: Video, Dokumente, README mit Godot- und REAPER-Version und Startanleitung. Lies alles einmal als Mira Petersen. + acceptance: + - id: spielbar + text: Der Abschnitt startet mit Godot 4.7 ohne Fehler oder Warnungen in der Ausgabe; alle Klänge der Cue-Liste sind im Spiel zu hören. + - id: cue-liste-vollstaendig + text: "`CUE-LISTE.md` nennt für jedes Ereignis Auslöser, Varianten, Priorität, Bus und gemessene Lautheit; jede Bildnummer ist im Spiel geprüft." + - id: varianten + text: Mindestens eine Aktion hat vier oder mehr eigene Varianten, und eine direkte Wiederholung derselben Datei ist ausgeschlossen oder begründet. + - id: uebergaenge-gemessen + text: Jeder Musikübergang ist mit Anfragezeit und tatsächlichem Wechsel gemessen; der Wechsel liegt dort, wo die Übergangstabelle ihn vorsieht. + - id: quellen + text: "`QUELLEN.md` hat eine Zeile je Datei im Projekt; fremdes Material nennt Lizenz mit Version, Quelle und Abrufdatum, generiertes Audio kommt nicht vor." + - id: entscheidungen + text: "`ENTSCHEIDUNGEN.md` enthält mindestens acht Einträge mit Grund, verworfener Alternative und Testergebnis." + - id: hoertest-belegt + text: "`HOERTEST.md` belegt Tests mit mindestens drei Wiedergabegeräten bei zwei Lautstärken und einen Fremdtest." + - id: originale-getrennt + text: Rohaufnahmen und REAPER-Projekte liegen getrennt von den Exporten und sind außerhalb des Rechners gesichert. + stretch: + - Lautstärkeregler für Musik, SFX und Atmo im Spielmenü, die auf die Busse wirken. + - Den Abschnitt mit dem Prüfloop statt der Musik testen und zeigen, dass jeder Übergang auf dem geplanten Schlag liegt. + portfolio: | + Das ist dein Reel-Stück. Zeig im Video zuerst den Abschnitt mit Platzhalter-Klicks, dann mit deinem Ton, und lass den Zustandswechsel sehen. Daneben gehören Cue-Liste, Übergangstabelle und zwei Einträge aus dem Entscheidungsprotokoll. Im Gespräch wirst du gefragt, was du mit doppelt so viel Zeit ändern würdest und was du zuerst streichst, wenn der Speicher knapp wird.