Skip to main content

dev-audio-lesson

Add an audio lesson — sound playback, mixing, spatial audio, DSP effects, with SDL GPU scenes and forge UI

Zur Installation springen

Quellinformationen

Repository
Nebulavenus/forge-gpu
Letzte Quellaktivität
27. März 2026 um 20:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
38
Forks
7

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
dev-audio-lesson
description
Add an audio lesson — sound playback, mixing, spatial audio, DSP effects, with SDL GPU scenes and forge UI
argument-hint
[number] [topic-name] [description]
Every audio lesson produces two things: **library code** and a **demo program**. The library — `common/audio/forge_audio.h` — is the primary deliverable. The lessons teach concepts; the library is what remains when the learning is done. It must be correct, efficient, tested, and safe. The demo program visualizes and controls the audio in action, rendered with SDL GPU and controlled through forge UI panels. **When to use this skill:** - You need to teach audio playback, mixing, spatial sound, or DSP effects - A learner wants to understand PCM audio, streaming, attenuation, or filters - The concept benefits from a visual 3D scene showing audio source positions - New functionality needs to be added to `common/audio/forge_audio.h` **Smart behavior:** - Before creating a lesson, check if an existing audio lesson already covers it - **Library first, demo second.** Design, implement, document, and test the library code before writing a single line of the demo program. The demo exercises the library — it does not replace it. - Audio lessons are interactive — every concept must be audible in the running program and controllable via forge UI - Focus on *why* the technique works, not just the code — connect DSP math to what the listener hears - Use simple geometric shapes for sound source visualization — the audio is the focus - All scene geometry comes from `common/shapes/forge_shapes.h` — never write inline geometry generation functions - Cross-reference math lessons (vectors, dot products) and GPU lessons where relevant ## Arguments The user (or you) can provide: - **Number**: two-digit lesson number (e.g. 01, 02) - **Topic name**: kebab-case (e.g. audio-basics, sound-effects) - **Description**: what this teaches (e.g. "PCM fundamentals, WAV loading, SDL audio streams") If any are missing, infer from context or ask. ## Steps ### 1. Analyze what's needed - **Check existing audio lessons**: Is there already a lesson for this topic? - **Check `common/audio/`**: Does relevant library code already exist? - **Check `common/ui/`**: Does the UI library provide the widgets needed for audio controls? If a required widget is missing, note it. - **Identify the scope**: What specific audio concepts does this lesson cover? - **Find cross-references**: Which math/GPU/UI lessons relate? - **Check PLAN.md**: Where does this lesson fit in the audio track? ### 2. Create the lesson directory `lessons/audio/NN-topic-name/` With subdirectories: ```text lessons/audio/NN-topic-name/ main.c CMakeLists.txt README.md audio.conf (gitignored — local path to audio files) assets/ screenshot.png (lesson screenshot — checked in) ``` Baseline rendering shaders (shadow, scene, grid, sky, UI) are provided by `forge_scene.h` — no per-lesson copies needed. If the lesson introduces a topic-specific shader (e.g. a waveform visualization pass), add a `shaders/` subdirectory for those files only. ### 3. Design and implement the audio library code Every audio lesson adds to `common/audio/forge_audio.h`. This is not optional — it is the primary deliverable. The demo program exists to exercise and visualize the library; the library is what ships. For the first audio lesson, create the file. For subsequent lessons, extend it. Design the API before writing the demo. The demo calls the library — never the reverse. #### Library standards **Correctness:** - Every function must implement a named, well-understood technique. Cite the source in the doc comment — a textbook, paper, or DSP reference. - Audio processing must not introduce clicks, pops, or DC offset. Transitions (volume changes, crossfades) must be smooth. - Sample rate conversion and channel mapping must be handled correctly via SDL audio stream properties. - Buffer sizes and sample counts must be computed correctly — off-by-one errors in audio produce audible artifacts. **Thread safety:** - SDL audio streams deliver data on a separate audio thread. Any shared state between the main thread and audio processing must use SDL atomics or SDL mutexes. - Volume, panning, and effect parameters should be set atomically so partial updates don't produce glitches. - Resource lifetimes must be clear — never free audio data while a stream might still reference it. **Performance:** - Audio processing runs in real time — dropped samples cause audible glitches. Keep per-sample operations simple (multiply, add, table lookup). - Precompute coefficients (filter taps, attenuation curves, panning tables) at init time or when parameters change, not per-sample. - Use `float` for all audio processing. Convert to/from integer formats only at the SDL boundary. **Numerical safety:** - Clamp output samples to `[-1.0f, 1.0f]` before delivery to prevent clipping distortion beyond what the user intends. - Guard against division by zero in attenuation calculations (zero distance). - Filter coefficients must be validated to prevent instability (poles inside the unit circle for IIR filters). **Header-only implementation:** - `static inline` for all functions — no separate `.c` compilation unit - Guard with `#ifndef FORGE_AUDIO_H` / `#define` / `#endif` - Include only SDL3, `"math/forge_math.h"`, and standard C headers - No heap allocation in per-sample paths — allocate buffers at init time - Deterministic: identical input samples and parameters produce identical output **Documentation (every function, no exceptions):** ```c /* Load a WAV file into an audio buffer. * * Reads the WAV file at the given path using SDL_LoadWAV and converts * it to the specified format. The returned buffer owns its sample data * and must be freed with forge_audio_buffer_free(). * * Parameters: * path — file path to the WAV file (must not be NULL) * spec — desired output format (sample rate, channels, format) * * Returns: * ForgeAudioBuffer with sample data, or a zero-initialized struct * on failure (check .data != NULL). * * Usage: * ForgeAudioBuffer buf = forge_audio_load_wav("/path/to/audio/click.wav", spec); * if (!buf.data) { SDL_Log("Failed to load WAV"); return; } * * See: Audio Lesson 01 — Audio Basics * Ref: SDL3 documentation, SDL_LoadWAV */ ``` Every doc comment must include: - Summary (one sentence — what it does) - Algorithm or technique being implemented - Parameters with types, valid ranges, and nullability - Return value (if any) with format/units - Usage example - Cross-reference to the lesson that introduces it - Reference to the source material **Naming:** - Functions: `forge_audio_verb_noun()` — e.g. `forge_audio_init()`, `forge_audio_load_wav()`, `forge_audio_play_oneshot()` - Types: `ForgeAudioNoun` — e.g. `ForgeAudioBuffer`, `ForgeAudioSource`, `ForgeAudioMixer`, `ForgeAudioListener` - Constants: `FORGE_AUDIO_UPPER` — e.g. `FORGE_AUDIO_MAX_SOURCES` **Core types to establish in Lesson 01:** ```c #ifndef FORGE_AUDIO_H #define FORGE_AUDIO_H #include <SDL3/SDL.h> #include "math/forge_math.h" /* --- Audio buffer (decoded sample data) --------------------------------- */ typedef struct ForgeAudioBuffer { float *data; /* interleaved float samples */ int sample_count; /* total samples (frames * channels) */ int channels; /* 1 = mono, 2 = stereo */ int sample_rate; /* Hz — typically 44100 or 48000 */ } ForgeAudioBuffer; /* --- Audio source (a playing instance) ---------------------------------- */ typedef struct ForgeAudioSource { const ForgeAudioBuffer *buffer; /* sample data (not owned) */ int cursor; /* current read position in samples */ float volume; /* linear gain [0..1] */ float pan; /* stereo pan [-1..1], 0 = center */ bool looping; /* true = restart at end of buffer */ bool playing; /* true = actively producing samples */ } ForgeAudioSource; /* ... functions grow lesson by lesson ... */ #endif /* FORGE_AUDIO_H */ ``` #### Testing the library (MANDATORY) The audio library is tested independently of the demo program. Tests validate correctness, edge cases, and determinism. Every function added to `forge_audio.h` must have corresponding tests in `tests/audio/test_audio.c`. **Test categories (every function must have all applicable categories):** 1. **Basic correctness** — Known inputs produce expected outputs. For audio processing functions, verify output samples against hand-computed values. 2. **Edge cases** — Empty buffers, zero-length audio, single-sample buffers, zero volume, maximum volume, out-of-range panning values, mismatched sample rates. 3. **No artifacts** — Volume transitions produce no clicks (verify samples are continuous). Looping produces seamless playback (verify loop-point samples match). Mixing does not overflow `[-1, 1]` without explicit clipping. 4. **Determinism** — Two identical sources with identical parameters produce bit-identical output. 5. **Thread safety** — Parameter changes during playback do not crash. Verify with single-threaded simulation (no need to test actual SDL threads in unit tests). **Test infrastructure:** - Test file: `tests/audio/test_audio.c` - Register in root `CMakeLists.txt` as `test_audio` - Use the same `ASSERT_NEAR` / test macros as `tests/math/test_math.c` - Run: `cmake --build build --target test_audio && ctest --test-dir build -R audio` - Tests must pass before the demo program is written ### 4. Create the demo program (`main.c`) A focused C program that demonstrates audio concepts with both audible output and visual feedback using SDL GPU and forge UI. **MANDATORY: Use `forge_scene.h` for the rendering baseline.** The scene library provides Blinn-Phong lighting, shadow mapping, grid floor, sky gradient, FPS camera, and UI in a single `forge_scene_init()` call. This eliminates hundreds of lines of rendering boilerplate and lets the lesson focus on audio. See the `forge-scene-renderer` skill for the full API. **Every audio lesson MUST include these features:** 1. **SDL GPU rendered scene via `forge_scene.h`** — A 3D scene providing visual context for the audio. For spatial audio lessons, this means visible sound source positions in the scene. For fundamentals lessons, a simple scene with visual feedback (waveform visualization, VU meters as colored geometry) is sufficient. The rendering baseline (Blinn-Phong, grid, shadow map, depth buffer, sRGB swapchain, sky) is provided by `forge_scene.h`. 2. **First-person camera** — Provided by `forge_scene.h`. The camera position (accessible via `forge_scene_cam_pos()`) doubles as the audio listener position for spatial audio lessons. 3. **Forge UI panel** — Provided by `forge_scene.h` via `forge_scene_begin_ui()` / `forge_scene_end_ui()`. Every lesson must have at least: - Master volume slider - Play/pause button (or status indicator) - Lesson-specific controls (per-source volume, panning, effect parameters) 4. **Capture support** — `forge_capture.h` integration (when `FORGE_CAPTURE` defined) via `scripts/capture_lesson.py`. **Audio requirements:** - **SDL audio stream** — All audio output goes through `SDL_AudioStream`. Create the stream at init, feed samples in the iterate callback or via SDL's audio device callback. - **User-supplied audio files** — Audio files are **not** checked into the repository. Each lesson has an `audio.conf` file (gitignored) that points to a local directory containing the user's audio files. See step 7 for the full config workflow. - **Real-time parameter control** — UI sliders and keys should change audio parameters (volume, pan, effect intensity) in real time with audible results. **Simulation controls:** | Key | Action | |---|---| | R | Reset / replay audio from start | | P | Pause / resume audio playback | | 1–9 | Trigger sound effects (where applicable) | | +/- | Adjust master volume | **Scene geometry via `forge_shapes.h` (MANDATORY):** All scene geometry **must** come from `common/shapes/forge_shapes.h`. Use spheres or icospheres for sound source positions, cones for directional sources. Never write inline geometry generation functions. **Template structure:** ```c /* * Audio Lesson NN — Topic Name * * Demonstrates: [what this shows] * * Controls: * WASD / Arrow keys — move camera (listener position) * Mouse — look around * R — reset / replay audio * P — pause / resume * 1–9 — trigger sound effects * +/- — adjust master volume * Escape — release mouse / quit * * SPDX-License-Identifier: Zlib */ #define SDL_MAIN_USE_CALLBACKS 1 #include <SDL3/SDL.h> #include <SDL3/SDL_main.h> #include <stddef.h> #include "math/forge_math.h" #include "audio/forge_audio.h" #include "shapes/forge_shapes.h" #define FORGE_SCENE_IMPLEMENTATION #include "scene/forge_scene.h" /* ── Constants ────────────────────────────────────────────────────── */ #define AUDIO_SAMPLE_RATE 48000 /* ── Types ────────────────────────────────────────────────────────── */ typedef struct app_state { ForgeScene scene; /* rendering, camera, shadow map, grid, sky, UI */ /* GPU resources — app-owned geometry */ SDL_GPUBuffer *sphere_vb, *sphere_ib; int sphere_index_count; /* Audio state — lesson-specific */ SDL_AudioStream *audio_stream; /* SDL audio output stream */ /* ForgeAudioBuffer buffers[N]; */ /* ForgeAudioSource sources[N]; */ float master_volume; /* linear gain [0..1] */ bool audio_paused; /* true = audio playback frozen */ } app_state; ``` **Init pattern:** ```c SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { app_state *state = SDL_calloc(1, sizeof(*state)); if (!state) return SDL_APP_FAILURE; *appstate = state; ForgeSceneConfig cfg = forge_scene_default_config("Audio NN — Topic"); cfg.cam_start_pos = vec3_create(0.0f, 4.0f, 12.0f); cfg.font_path = "assets/fonts/liberation_mono/LiberationMono-Regular.ttf"; if (!forge_scene_init(&state->scene, &cfg, argc, argv)) return SDL_APP_FAILURE; state->master_volume = 1.0f; /* Read audio directory from audio.conf */ char audio_dir[512]; if (!read_audio_conf("audio.conf", audio_dir, sizeof(audio_dir))) { SDL_Log("audio.conf not found or not configured."); SDL_Log("See the lesson README for setup instructions."); return SDL_APP_FAILURE; } /* Load audio files from user's directory, set up SDL audio stream, * upload shapes ... */ return SDL_APP_CONTINUE; } ``` **Iterate pattern (camera position as listener):** ```c SDL_AppResult SDL_AppIterate(void *appstate) { app_state *state = appstate; ForgeScene *s = &state->scene; if (!forge_scene_begin_frame(s)) return SDL_APP_CONTINUE; /* Use camera position as audio listener */ vec3 listener_pos = forge_scene_cam_pos(s); /* Shadow pass */ forge_scene_begin_shadow_pass(s);
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen