ab43578049
Add AudioFilterPipeline for chained filter execution on AudioAnalysis. Wire filter pipelines into AudioColorStripStream, AudioValueStream, and WebSocket test endpoint. Add hot-update support via ProcessorManager.refresh_audio_filter_pipelines(). Thread AudioProcessingTemplateStore through dependency injection hierarchy.
6.2 KiB
6.2 KiB
Phase 4: Runtime Integration
Status: ✅ Done Parent plan: PLAN.md Domain: backend
Objective
Wire the audio filter pipeline into the runtime audio streaming system so that ProcessedAudioSources actually apply their filter chains to live audio data.
Tasks
- Task 1: Create filter pipeline executor in
core/audio/filters/pipeline.pyAudioFilterPipelineclass:__init__(filter_instances: List[FilterInstance], registry: AudioFilterRegistry)- Instantiates all filters from FilterInstance specs
process(analysis: AudioAnalysis) -> AudioAnalysis— runs analysis through all filters in orderreset()— resets all stateful filtersclose()— cleanup resources
- Handles stateful filter lifecycle (create on init, reset on demand, close on cleanup)
- Task 2: Update
AudioColorStripStreamincore/processing/audio_stream.py- On construction: if source is ProcessedAudioSource, resolve the full chain:
- Walk to CaptureAudioSource for device info
- Collect all AudioProcessingTemplates along the chain
- Resolve all filter instances (with template expansion)
- Create AudioFilterPipeline
- In render loop: after getting AudioAnalysis from ManagedAudioStream, run it through the filter pipeline before visualization
- Remove old inline channel selection and band filtering code (now handled by filters)
- On stop: close the filter pipeline
- On construction: if source is ProcessedAudioSource, resolve the full chain:
- Task 3: Update
AudioValueStreamincore/processing/value_stream.py- Same pattern: resolve ProcessedAudioSource chain, create filter pipeline, apply in get_value()
- Remove old inline channel/band handling
- Task 4: Hot-update support for filter templates
- When an AudioProcessingTemplate is updated, running streams that use it should re-resolve their filter pipeline
- Listen for template update events (or implement a refresh mechanism)
- Re-create AudioFilterPipeline with updated filter instances
- Reset stateful filter state on pipeline refresh
- Task 5: Update WebSocket test endpoint in
api/routes/audio_sources.py- For ProcessedAudioSource: resolve chain, create pipeline, apply filters to test stream data
- Return filtered analysis in real-time over WebSocket
- Task 6: Update any code that calls
AudioSourceStore.resolve_audio_source()to handle the new return shape
Files to Modify/Create
core/audio/filters/pipeline.py— create — AudioFilterPipelinecore/processing/audio_stream.py— modify — integrate filter pipelinecore/processing/value_stream.py— modify — integrate filter pipelineapi/routes/audio_sources.py— modify — update WebSocket test- Any other consumers of
resolve_audio_source()— modify
Acceptance Criteria
- ProcessedAudioSource chains are resolved and filter pipelines created at stream start
- AudioAnalysis passes through the filter chain before visualization/value extraction
- Stateful filters maintain correct state across frames
- Hot-update of templates refreshes running filter pipelines
- WebSocket test endpoint works with processed sources
- Old inline channel/band code removed from stream classes
Notes
- ⚠️ Temporary breakage: Removing inline channel/band code from AudioColorStripStream breaks existing MonoAudioSource/BandExtractAudioSource flows — but those types were already removed in Phase 3.
- Filter pipeline must be thread-safe: AudioColorStripStream and AudioValueStream run in background threads.
- For hot-update: consider using an event callback from the template store rather than polling.
- The filter pipeline should produce a new AudioAnalysis each time (immutability), not mutate the shared snapshot from ManagedAudioStream.
Review Checklist
- All tasks completed
- Code follows project conventions
- No unintended side effects
- Build passes
- Tests pass (new + existing)
Handoff to Next Phase
What was built
AudioFilterPipelineclass incore/audio/filters/pipeline.py— thread-safe pipeline that instantiates, processes, resets, and closes audio filtersbuild_pipeline_from_template_ids()helper — resolves template IDs to FilterInstance lists and creates a pipelineAudioColorStripStreamnow builds and applies filter pipeline:_rebuild_filter_pipeline()called on construction and source update;_apply_filters()replaces_pick_channel()in render loop; pipeline closed on stopAudioValueStreamnow builds and applies filter pipeline:_rebuild_filter_pipeline()called on construction; filters applied in_extract_raw()before scalar extraction; pipeline closed on stop- Hot-update:
ProcessorManager.refresh_audio_filter_pipelines(template_id)dispatches to both CSS and value stream managers; called from audio processing template update/delete routes - WebSocket test endpoint creates a filter pipeline from the resolved template chain and applies it to analysis before sending
- Dependency injection:
audio_processing_template_storethreaded throughProcessorDependencies->ProcessorManager->ColorStripStreamManager/ValueStreamManager->AudioColorStripStream/AudioValueStream
What Phase 5 needs to know
- The backend is fully wired: creating a ProcessedAudioSource that references templates with channel_extract, band_extract, gain, etc. filters will apply those filters to live audio data
- The WebSocket test endpoint shows filtered analysis in real-time
- Frontend needs to provide UI for creating/editing AudioProcessingTemplates and ProcessedAudioSources
- Filter pipeline is per-stream-instance (not shared) — stateful filters maintain independent state
Temporary breakages resolved
_pick_channel()removed from AudioColorStripStream — replaced by_apply_filters()which uses the filter pipeline- Channel/band handling in AudioValueStream now goes through filter pipeline
- All "Phase 4 will wire..." comments resolved
Known deviations from plan
AudioFilterPipeline.__init__takesList[FilterInstance]only (notAudioFilterRegistry— uses the class-level registry directly viaAudioFilterRegistry.create_instance())- Hot-update uses explicit method calls from the route handler rather than an event-listener pattern — simpler and avoids the need for a subscription system