using System; using FractalVisualizer.Audio; using FractalVisualizer.Models; using FractalVisualizer.Rendering; using FractalVisualizer.UI; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace FractalVisualizer; public class Game1 : Game { private readonly GraphicsDeviceManager _graphics; private SpriteBatch? _spriteBatch; private Texture2D? _pixel; private HudBitmapFont? _hudFont; private readonly AudioCapture _audioCapture; private readonly AudioAnalyzer _audioAnalyzer; private FractalRenderer? _fractalRenderer; private KeyboardState _previousKeyboardState; private AudioInputMode _currentInputMode; private AudioFeatures _latestFeatures; private bool _isPaused; private bool _showHud = true; private int _fractalPresetIndex; private int _fps; private int _frameCounter; private TimeSpan _fpsElapsed; private const int TargetFps = 120; private RenderQuality _renderQuality = RenderQuality.Medium; private readonly (int Width, int Height)[] _windowedResolutions = [ (1280, 720), (1600, 900), (1920, 1080) ]; private int _resolutionIndex = 0; public Game1() { _graphics = new GraphicsDeviceManager(this); _graphics.SynchronizeWithVerticalRetrace = false; _graphics.PreferredBackBufferWidth = 1280; _graphics.PreferredBackBufferHeight = 720; Content.RootDirectory = "Content"; IsMouseVisible = true; Window.AllowUserResizing = true; IsFixedTimeStep = true; TargetElapsedTime = TimeSpan.FromSeconds(1.0 / 120.0); _audioCapture = new AudioCapture(); _audioAnalyzer = new AudioAnalyzer(_audioCapture, fftSize: 2048, spectrumBins: 64); _currentInputMode = AudioInputMode.Microphone; _latestFeatures = AudioFeatures.CreateEmpty(64); } protected override void Initialize() { _audioCapture.Start(); _audioAnalyzer.Start(); Window.Title = "Fractal Visualizer"; base.Initialize(); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); _fractalRenderer = new FractalRenderer(GraphicsDevice); _fractalRenderer.SetQuality(_renderQuality); _pixel = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color); _pixel.SetData([Color.White]); _hudFont = new HudBitmapFont(); } protected override void Update(GameTime gameTime) { KeyboardState keyboard = Keyboard.GetState(); if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) { Exit(); return; } if (IsPressed(keyboard, Keys.Escape)) { if (_graphics.IsFullScreen) { _graphics.ToggleFullScreen(); } else { Exit(); return; } } if (IsPressed(keyboard, Keys.F11)) { ToggleFullScreen(); } if (IsPressed(keyboard, Keys.F)) { ToggleFullScreen(); } if (IsPressed(keyboard, Keys.R)) { CycleWindowResolution(); } if (IsPressed(keyboard, Keys.D1)) { SetRenderQuality(RenderQuality.Low); } if (IsPressed(keyboard, Keys.D2)) { SetRenderQuality(RenderQuality.Medium); } if (IsPressed(keyboard, Keys.D3)) { SetRenderQuality(RenderQuality.High); } if (IsPressed(keyboard, Keys.Space)) { _isPaused = !_isPaused; } if (IsPressed(keyboard, Keys.Tab)) { _showHud = !_showHud; } if (IsPressed(keyboard, Keys.Left)) { _fractalPresetIndex = Math.Max(0, _fractalPresetIndex - 1); } if (IsPressed(keyboard, Keys.Right)) { _fractalPresetIndex = Math.Min(19, _fractalPresetIndex + 1); } if (IsPressed(keyboard, Keys.M)) { _currentInputMode = _currentInputMode == AudioInputMode.Microphone ? AudioInputMode.SystemOutput : AudioInputMode.Microphone; _audioCapture.SwitchMode(_currentInputMode); } _latestFeatures = _audioAnalyzer.GetLatestFeatures(); _fpsElapsed += gameTime.ElapsedGameTime; _frameCounter++; if (_fpsElapsed >= TimeSpan.FromSeconds(1)) { _fps = _frameCounter; _frameCounter = 0; _fpsElapsed -= TimeSpan.FromSeconds(1); } Window.Title = $"Fractal Visualizer | {GetPresetName(_fractalPresetIndex)} | {_renderQuality} | Mode: {_currentInputMode} | FPS:{_fps}"; _previousKeyboardState = keyboard; base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); if (_fractalRenderer is not null) { _fractalRenderer.UpdatePerformanceHint(_fps, TargetFps); float renderTime = _isPaused ? _latestFeatures.TimeSeconds : (float)gameTime.TotalGameTime.TotalSeconds; _fractalRenderer.Draw(_latestFeatures, renderTime, _fractalPresetIndex); } if (_showHud && _spriteBatch is not null && _hudFont is not null && _pixel is not null) { _spriteBatch.Begin(samplerState: SamplerState.PointClamp, blendState: BlendState.AlphaBlend); string modeText = _currentInputMode == AudioInputMode.Microphone ? "MIC" : "SYS"; _hudFont.DrawText(_spriteBatch, _pixel, "MODE " + modeText + " M SWITCH", 12, 10, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "PRESET " + GetPresetName(_fractalPresetIndex) + " LEFT RIGHT", 12, 30, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "LEVEL " + _latestFeatures.Level.ToString("0.000"), 12, 50, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "BASS " + _latestFeatures.Bass.ToString("0.000"), 12, 70, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "MID " + _latestFeatures.Mid.ToString("0.000"), 12, 90, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "HIGH " + _latestFeatures.High.ToString("0.000"), 12, 110, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "BEAT " + _latestFeatures.Beat.ToString("0.000"), 12, 130, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "PAUSED " + (_isPaused ? "YES" : "NO"), 12, 150, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "FPS " + _fps, 12, 170, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "QUALITY " + _renderQuality + " 1 2 3", 12, 190, 2, Color.White); _hudFont.DrawText(_spriteBatch, _pixel, "F OR F11 FULLSCREEN R RESIZE", 12, 210, 2, Color.White); _spriteBatch.End(); } base.Draw(gameTime); } protected override void Dispose(bool disposing) { if (disposing) { _audioAnalyzer.Dispose(); _audioCapture.Dispose(); _fractalRenderer?.Dispose(); _pixel?.Dispose(); _spriteBatch?.Dispose(); } base.Dispose(disposing); } private bool IsPressed(KeyboardState keyboardState, Keys key) { return keyboardState.IsKeyDown(key) && _previousKeyboardState.IsKeyUp(key); } private static string GetPresetName(int presetIndex) { return presetIndex switch { 0 => "JULIA", 1 => "MANDELBULB", 2 => "KALEIDO", 3 => "FLAME", 4 => "PLASMA", 5 => "NEBULA", 6 => "FLAMEGRILL", 7 => "NOTES", 8 => "FIREWORKS", 9 => "EQCITY", 10 => "METABALL", 11 => "GALAXY", 12 => "AURORA", 13 => "VORTEX", 14 => "LASERGRID", 15 => "RAINRIPPLE", 16 => "INKSMOKE", 17 => "MOSAIC", 18 => "RIBBON", 19 => "GRAINBARS", _ => "JULIA" }; } private void ToggleFullScreen() { _graphics.ToggleFullScreen(); } private void SetRenderQuality(RenderQuality quality) { _renderQuality = quality; _fractalRenderer?.SetQuality(quality); } private void CycleWindowResolution() { if (_graphics.IsFullScreen) { return; } _resolutionIndex = (_resolutionIndex + 1) % _windowedResolutions.Length; (int width, int height) = _windowedResolutions[_resolutionIndex]; _graphics.PreferredBackBufferWidth = width; _graphics.PreferredBackBufferHeight = height; _graphics.ApplyChanges(); } }