summaryrefslogtreecommitdiff
path: root/TODO.md
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-04 13:08:13 +0100
committerskal <pascal.massimino@gmail.com>2026-02-04 13:08:13 +0100
commitc4888bea71326f7a69e8214af0d9c2a62a60b887 (patch)
treedd1e71be51893414c590db8a7be578ddfd6232f0 /TODO.md
parentf64f842bb0dabd89308e2378e56358bc8abdd653 (diff)
feat(audio): Implement audio backend abstraction (Task #51.1)
Created interface-based audio backend system to enable testing without hardware. This is the foundation for robust tracker timing verification. Changes: - Created AudioBackend interface with init/start/shutdown methods - Added test-only hooks: on_voice_triggered() and on_frames_rendered() - Moved miniaudio implementation to MiniaudioBackend class - Refactored audio.cc to use backend abstraction with auto-fallback - Added time tracking to synth.cc (elapsed time from rendered frames) - Created test_audio_backend.cc to verify backend injection works - Fixed audio test linking to include util/procedural dependencies All test infrastructure guarded by #if !defined(STRIP_ALL) for zero size impact on final build. Production path unchanged, 100% backward compatible. All 13 tests pass. handoff(Claude): Task #51.1 complete, audio backend abstraction ready Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'TODO.md')
-rw-r--r--TODO.md221
1 files changed, 220 insertions, 1 deletions
diff --git a/TODO.md b/TODO.md
index fd76e69..a981f8e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,6 +3,15 @@
This file tracks prioritized tasks with detailed attack plans.
## Recently Completed (February 4, 2026)
+- [x] **Task #51.1: Audio Backend Abstraction**:
+ - [x] **Interface Created**: Defined `AudioBackend` interface in `src/audio/audio_backend.h` with hooks for voice triggering and frame rendering.
+ - [x] **Production Backend**: Moved miniaudio implementation to `MiniaudioBackend` class, maintaining backward compatibility.
+ - [x] **Audio Refactoring**: Updated `audio.cc` to use backend abstraction with automatic fallback to `MiniaudioBackend`.
+ - [x] **Event Hooks**: Added time tracking to `synth.cc` with `on_voice_triggered()` callbacks (guarded by `!STRIP_ALL`).
+ - [x] **Verification Test**: Created `test_audio_backend.cc` to verify backend injection and event recording work correctly.
+ - [x] **Build Integration**: Updated CMakeLists.txt to include new backend files and link audio tests properly with util/procedural dependencies.
+ - [x] **Zero Size Impact**: All test infrastructure under `#if !defined(STRIP_ALL)`, production path unchanged.
+
- [x] **Task #50: WGSL Modularization**:
- [x] **Recursive Composition**: Updated `ShaderComposer` to support recursive `#include "snippet_name"` directives with cycle detection.
- [x] **Granular SDF Library**: Extracted `math/sdf_shapes.wgsl`, `math/sdf_utils.wgsl`, `render/shadows.wgsl`, `render/scene_query.wgsl`, and `render/lighting_utils.wgsl`.
@@ -61,7 +70,12 @@ This file tracks prioritized tasks with detailed attack plans.
## Priority 4: Developer Tooling & CI
**Goal**: Improve developer workflows, code quality, and release processes.
-*(No active tasks)*
+
+- [ ] **Task #51: Tracker Timing Verification**
+ - [x] **Task #51.1: Audio Backend Abstraction**: Create an interface to separate audio output from synth logic, enabling testable backends.
+ - [ ] **Task #51.2: Mock Audio Backend**: Implement a test backend that records voice trigger events with precise timestamps.
+ - [ ] **Task #51.3: Tracker Test Suite**: Create `test_tracker.cc` to verify pattern triggering, timing accuracy, and synchronization.
+ - [ ] **Task #51.4: Integration with Build**: Wire up tests to CMake and ensure they run in CI.
## Phase 2: Size Optimization (Final Goal)
@@ -73,4 +87,209 @@ This file tracks prioritized tasks with detailed attack plans.
- [ ] **Task #35: CRT Replacement**: investigation and implementation of CRT-free entry point.
+---
+
+## Task #51: Tracker Timing Verification - Detailed Attack Plan
+
+**Problem Statement**: The tracker and synthesizer have audio sync issues. There's no robust way to verify that tracker patterns trigger at the correct timestamps without running the full audio hardware stack.
+
+**Goal**: Implement a testable audio backend abstraction with event recording capabilities to verify tracker timing accuracy.
+
+### Task #51.1: Audio Backend Abstraction
+**Objective**: Decouple audio output from synthesis logic to enable testing without hardware.
+
+**Implementation Steps**:
+- [ ] **Create `src/audio/audio_backend.h`**:
+ - Define `AudioBackend` interface with pure virtual methods:
+ - `init()`: Initialize backend resources
+ - `start()`: Start audio playback/recording
+ - `shutdown()`: Clean up resources
+ - `on_voice_triggered(timestamp, spec_id, volume, pan)`: Hook for voice events
+ - Add `#if !defined(STRIP_ALL)` guards around test-only methods
+
+- [ ] **Create `src/audio/miniaudio_backend.h` and `.cc`**:
+ - Move current miniaudio implementation from `audio.cc` to `MiniaudioBackend` class
+ - Implement `AudioBackend` interface
+ - Keep production behavior identical (no regressions)
+ - This backend does NOT record events (production path)
+
+- [ ] **Refactor `src/audio/audio.cc`**:
+ - Add global `AudioBackend* g_audio_backend` pointer
+ - Add `void audio_set_backend(AudioBackend* backend)` function (under `!STRIP_ALL`)
+ - Default to `MiniaudioBackend` if no backend is set
+ - Replace direct miniaudio calls with backend interface calls
+
+- [ ] **Update `src/audio/synth.cc`**:
+ - Add external hook: `extern AudioBackend* g_audio_backend_for_events`
+ - In `synth_trigger_voice()`, call `backend->on_voice_triggered()` if backend exists
+ - Ensure hook is `#if !defined(STRIP_ALL)` guarded
+
+**Size Impact**: Zero (test code stripped in final build).
+
+**Validation**: Existing tests (`test_synth.cc`) must pass unchanged.
+
+---
+
+### Task #51.2: Mock Audio Backend
+**Objective**: Create a test-only backend that records all audio events with timestamps.
+
+**Implementation Steps**:
+- [ ] **Create `src/audio/mock_audio_backend.h` and `.cc`** (under `#if !defined(STRIP_ALL)`):
+ - Define `struct VoiceTriggerEvent`:
+ ```cpp
+ struct VoiceTriggerEvent {
+ float timestamp_sec;
+ int spectrogram_id;
+ float volume;
+ float pan;
+ };
+ ```
+ - Implement `MockAudioBackend` class:
+ - Maintain `std::vector<VoiceTriggerEvent> recorded_events`
+ - Override `on_voice_triggered()` to record events with current time
+ - Add `const std::vector<VoiceTriggerEvent>& get_events() const`
+ - Add `void clear_events()`
+ - Add `void advance_time(float delta_sec)` to simulate time progression
+ - Implement `init()`, `start()`, `shutdown()` as no-ops
+
+- [ ] **Time Tracking**:
+ - Add `float current_time_sec` member to `MockAudioBackend`
+ - Increment `current_time_sec` in `advance_time()`
+ - Use `current_time_sec` as timestamp when recording events
+
+- [ ] **Synth Integration**:
+ - When `synth_render()` is called, calculate frames rendered
+ - Notify backend of time elapsed: `frames / sample_rate`
+ - Update mock's internal clock accordingly
+
+**Testing**: Create minimal unit test in `test_tracker.cc` to verify event recording works.
+
+---
+
+### Task #51.3: Tracker Test Suite
+**Objective**: Comprehensive tests for tracker pattern triggering and timing accuracy.
+
+**Implementation Steps**:
+- [ ] **Create `src/tests/test_tracker.cc`**:
+
+ - **Test 1: Single Pattern Trigger**
+ - Define a minimal `TrackerScore` with 1 pattern at `t=1.0s`
+ - Set up mock backend
+ - Call `tracker_update(0.5)` → verify no events
+ - Call `tracker_update(1.0)` → verify 1 event recorded
+ - Validate event timestamp matches expected trigger time
+
+ - **Test 2: Multiple Pattern Triggers**
+ - Score with 3 patterns at `t=0.5s, 1.0s, 2.0s`
+ - Progressively call `tracker_update()` with increasing times
+ - Verify each pattern triggers exactly once at correct time
+
+ - **Test 3: Event Timing Accuracy**
+ - Pattern with multiple events at different beat offsets
+ - Verify each event's timestamp matches: `pattern_start_time + (beat * beat_duration)`
+ - Use tolerance: `±1 frame` (1/32000 sec ≈ 31.25µs)
+
+ - **Test 4: BPM Scaling**
+ - Same pattern tested at different BPMs (60, 120, 180)
+ - Verify beat-to-time conversion is accurate: `beat_sec = 60.0 / bpm`
+
+ - **Test 5: Pattern Overlap**
+ - Two patterns with overlapping time ranges
+ - Verify both trigger correctly without interference
+
+ - **Test 6: Asset vs Procedural Samples**
+ - Pattern using both asset-based spectrograms and procedural notes
+ - Verify both types render and trigger correctly
+
+ - **Test 7: Seek/Fast-Forward Simulation**
+ - Simulate `audio_render_silent()` behavior
+ - Start at `t=0`, fast-forward to `t=10.0s`
+ - Verify all patterns in range [0, 10] triggered correctly
+
+- [ ] **Helper Functions**:
+ ```cpp
+ void assert_event_at_time(const std::vector<VoiceTriggerEvent>& events,
+ float expected_time, float tolerance = 0.001f);
+
+ void assert_event_count(const std::vector<VoiceTriggerEvent>& events,
+ int expected_count);
+
+ TrackerScore create_test_score(const std::vector<float>& trigger_times,
+ float bpm = 120.0f);
+ ```
+
+**Coverage Target**: 95%+ for `src/audio/tracker.cc`.
+
+---
+
+### Task #51.4: Integration with Build
+**Objective**: Wire up tests to CMake and ensure they run automatically.
+
+**Implementation Steps**:
+- [ ] **Update `src/CMakeLists.txt`**:
+ - Add `mock_audio_backend.cc` to test-only sources (under `DEMO_BUILD_TESTS`)
+ - Link `test_tracker` executable with audio subsystem library
+
+- [ ] **Add test to CTest**:
+ ```cmake
+ if(DEMO_BUILD_TESTS)
+ add_executable(test_tracker tests/test_tracker.cc audio/mock_audio_backend.cc)
+ target_link_libraries(test_tracker PRIVATE audio_lib util_lib)
+ add_test(NAME TrackerTest COMMAND test_tracker)
+ endif()
+ ```
+
+- [ ] **Verify in CI**:
+ - Run `cmake --build build && cd build && ctest`
+ - Ensure `test_tracker` runs and passes
+ - Check coverage report includes tracker.cc
+
+**Validation**: `ctest` output shows `TrackerTest: PASSED`.
+
+---
+
+## Implementation Layout Summary
+
+### New Files
+```
+src/audio/audio_backend.h # Interface definition
+src/audio/miniaudio_backend.h # Production backend (header)
+src/audio/miniaudio_backend.cc # Production backend (impl)
+src/audio/mock_audio_backend.h # Test backend (header, !STRIP_ALL)
+src/audio/mock_audio_backend.cc # Test backend (impl, !STRIP_ALL)
+src/tests/test_tracker.cc # Comprehensive tracker tests
+```
+
+### Modified Files
+```
+src/audio/audio.cc # Backend abstraction layer
+src/audio/synth.cc # Add event hooks
+src/CMakeLists.txt # Add new files and tests
+```
+
+### File Structure
+```
+src/audio/
+├── audio_backend.h [NEW] Interface (50 lines)
+├── miniaudio_backend.h [NEW] Header (30 lines)
+├── miniaudio_backend.cc [NEW] Production impl (~100 lines, moved from audio.cc)
+├── mock_audio_backend.h [NEW] Test header (60 lines)
+├── mock_audio_backend.cc [NEW] Test impl (~120 lines)
+├── audio.h [MODIFIED] Add backend setter
+├── audio.cc [MODIFIED] Use backend abstraction (~30 lines changed)
+├── synth.cc [MODIFIED] Add event hook (~10 lines)
+└── tracker.cc [NO CHANGE]
+
+src/tests/
+└── test_tracker.cc [NEW] Test suite (~400 lines)
+```
+
+### Code Organization Principles
+1. **Zero Size Impact**: All test infrastructure under `#if !defined(STRIP_ALL)`
+2. **Backward Compatible**: Production path unchanged, existing tests pass
+3. **Clean Separation**: Interface-based design, easy to add more backends later
+4. **Testable**: Mock backend has minimal dependencies (no hardware/threads)
+
+---
+
## Future Goals