#!/usr/bin/env python3 """ Pygame Demo: Effects with LFO Modulation This demo shows how to use LFO (Low Frequency Oscillator) to modulate effect intensities over time, creating smooth animated changes. Effects modulated: - noise: Random noise intensity - fade: Fade effect intensity - tint: Color tint intensity - glitch: Glitch effect intensity The LFO uses a sine wave to oscillate intensity between 0.0 and 1.0. """ import math import sys import time from dataclasses import dataclass from typing import Any from engine import config from engine.display import DisplayRegistry from engine.effects import get_registry from engine.pipeline import Pipeline, PipelineConfig, PipelineContext, list_presets from engine.pipeline.params import PipelineParams from engine.pipeline.preset_loader import load_presets from engine.sensors.oscillator import OscillatorSensor from engine.sources import FEEDS @dataclass class LFOEffectConfig: """Configuration for LFO-modulated effect.""" name: str frequency: float # LFO frequency in Hz phase_offset: float # Phase offset (0.0 to 1.0) min_intensity: float = 0.0 max_intensity: float = 1.0 class LFOEffectDemo: """Demo controller that modulates effect intensities using LFO.""" def __init__(self, pipeline: Pipeline): self.pipeline = pipeline self.effects = [ LFOEffectConfig("noise", frequency=0.5, phase_offset=0.0), LFOEffectConfig("fade", frequency=0.3, phase_offset=0.33), LFOEffectConfig("tint", frequency=0.4, phase_offset=0.66), LFOEffectConfig("glitch", frequency=0.6, phase_offset=0.9), ] self.start_time = time.time() self.frame_count = 0 def update(self): """Update effect intensities based on LFO.""" elapsed = time.time() - self.start_time self.frame_count += 1 for effect_cfg in self.effects: # Calculate LFO value using sine wave angle = ( (elapsed * effect_cfg.frequency + effect_cfg.phase_offset) * 2 * 3.14159 ) lfo_value = 0.5 + 0.5 * math.sin(angle) # Scale to intensity range intensity = effect_cfg.min_intensity + lfo_value * ( effect_cfg.max_intensity - effect_cfg.min_intensity ) # Update effect intensity in pipeline self.pipeline.set_effect_intensity(effect_cfg.name, intensity) def run(self, duration: float = 30.0): """Run the demo for specified duration.""" print(f"\n{'=' * 60}") print("LFO EFFECT MODULATION DEMO") print(f"{'=' * 60}") print("\nEffects being modulated:") for effect in self.effects: print(f" - {effect.name}: {effect.frequency}Hz") print(f"\nPress Ctrl+C to stop") print(f"{'=' * 60}\n") start = time.time() try: while time.time() - start < duration: self.update() time.sleep(0.016) # ~60 FPS except KeyboardInterrupt: print("\n\nDemo stopped by user") finally: print(f"\nTotal frames rendered: {self.frame_count}") def main(): """Main entry point for the LFO demo.""" # Configuration effect_names = ["noise", "fade", "tint", "glitch"] # Get pipeline config from preset preset_name = "demo-pygame" presets = load_presets() preset = presets["presets"].get(preset_name) if not preset: print(f"Error: Preset '{preset_name}' not found") print(f"Available presets: {list(presets['presets'].keys())}") sys.exit(1) # Create pipeline context ctx = PipelineContext() ctx.terminal_width = preset.get("viewport_width", 80) ctx.terminal_height = preset.get("viewport_height", 24) # Create params params = PipelineParams( source=preset.get("source", "headlines"), display="pygame", # Force pygame display camera_mode=preset.get("camera", "feed"), effect_order=effect_names, # Enable our effects viewport_width=preset.get("viewport_width", 80), viewport_height=preset.get("viewport_height", 24), ) ctx.params = params # Create pipeline config pipeline_config = PipelineConfig( source=preset.get("source", "headlines"), display="pygame", camera=preset.get("camera", "feed"), effects=effect_names, ) # Create pipeline pipeline = Pipeline(config=pipeline_config, context=ctx) # Build pipeline pipeline.build() # Create demo controller demo = LFOEffectDemo(pipeline) # Run demo demo.run(duration=30.0) if __name__ == "__main__": main()