#pragma once #include #include #include "VisualiserParameters.h" struct Texture { GLuint id; int width; int height; }; class VisualiserWindow; class VisualiserRenderer : public juce::Component, public osci::AudioBackgroundThread, public juce::OpenGLRenderer, public juce::AsyncUpdater { public: VisualiserRenderer( VisualiserParameters ¶meters, osci::AudioBackgroundThreadManager &threadManager, int resolution = 1024, double frameRate = 60.0f, juce::String threadName = "" ); ~VisualiserRenderer() override; void resized() override; int prepareTask(double sampleRate, int samplesPerBlock) override; void runTask(const std::vector& points) override; void stopTask() override; void handleAsyncUpdate() override; void newOpenGLContextCreated() override; void renderOpenGL() override; void openGLContextClosing() override; void setResolution(int width); void setFrameRate(double frameRate); int getRenderWidth() const { return renderTexture.width; } int getRenderHeight() const { return renderTexture.height; } Texture getRenderTexture() const { return renderTexture; } void getFrame(std::vector& frame); void drawFrame(); juce::Rectangle getViewportArea() const { return viewportArea; } void setViewportArea(juce::Rectangle area) { viewportArea = area; viewportChanged(viewportArea); } // Set a crop rectangle for the renderTexture when drawing to screen // If not set, the entire texture will be displayed in a square void setCropRectangle(std::optional> cropRect) { cropRectangle = cropRect; viewportChanged(viewportArea); } std::optional> getCropRectangle() const { return cropRectangle; } protected: juce::OpenGLContext openGLContext; VisualiserParameters ¶meters; osci::Semaphore renderingSemaphore{0}; std::function preRenderCallback = nullptr; std::function postRenderCallback = nullptr; juce::AudioBuffer audioOutputBuffer; private: juce::Rectangle viewportArea; std::optional> cropRectangle; float renderScale = 1.0f; GLuint quadIndexBuffer = 0; GLuint vertexIndexBuffer = 0; GLuint vertexBuffer = 0; int nEdges = 0; juce::CriticalSection samplesLock; long sampleCount = 0; std::vector xSamples{2}; std::vector ySamples{2}; std::vector zSamples{2}; std::vector smoothedXSamples; std::vector smoothedYSamples; std::vector smoothedZSamples; std::atomic sampleBufferCount = 0; int prevSampleBufferCount = 0; long lastTriggerPosition = 0; std::vector scratchVertices; std::vector fullScreenQuad; GLuint frameBuffer = 0; double currentFrameRate = 60.0; Texture lineTexture; Texture blur1Texture; Texture blur2Texture; Texture blur3Texture; Texture blur4Texture; Texture glowTexture; Texture renderTexture; Texture screenTexture; juce::OpenGLTexture screenOpenGLTexture; std::optional targetTexture = std::nullopt; juce::Image screenTextureImage = juce::ImageFileFormat::loadFrom(BinaryData::noise_jpg, BinaryData::noise_jpgSize); juce::Image emptyScreenImage = juce::ImageFileFormat::loadFrom(BinaryData::empty_jpg, BinaryData::empty_jpgSize); #if OSCI_PREMIUM juce::Image oscilloscopeImage = juce::ImageFileFormat::loadFrom(BinaryData::real_png, BinaryData::real_pngSize); juce::Image vectorDisplayImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_png, BinaryData::vector_display_pngSize); juce::Image emptyReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::no_reflection_jpg, BinaryData::no_reflection_jpgSize); juce::Image oscilloscopeReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::real_reflection_png, BinaryData::real_reflection_pngSize); juce::Image vectorDisplayReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_reflection_png, BinaryData::vector_display_reflection_pngSize); osci::Point REAL_SCREEN_OFFSET = {0.02, -0.15}; osci::Point REAL_SCREEN_SCALE = {0.6}; osci::Point VECTOR_DISPLAY_OFFSET = {0.075, -0.045}; osci::Point VECTOR_DISPLAY_SCALE = {0.6}; float VECTOR_DISPLAY_FISH_EYE = 0.5; juce::OpenGLTexture reflectionOpenGLTexture; Texture reflectionTexture; std::unique_ptr glowShader; std::unique_ptr afterglowShader; #endif std::unique_ptr simpleShader; std::unique_ptr texturedShader; std::unique_ptr blurShader; std::unique_ptr wideBlurShader; std::unique_ptr lineShader; std::unique_ptr outputShader; juce::OpenGLShaderProgram* currentShader; float fadeAmount; ScreenOverlay screenOverlay = ScreenOverlay::INVALID; int resolution; double frameRate; const double RESAMPLE_RATIO = 6.0; double sampleRate = -1; double oldSampleRate = -1; chowdsp::ResamplingTypes::LanczosResampler<2048, 8> xResampler; chowdsp::ResamplingTypes::LanczosResampler<2048, 8> yResampler; chowdsp::ResamplingTypes::LanczosResampler<2048, 8> zResampler; void setOffsetAndScale(juce::OpenGLShaderProgram* shader); Texture makeTexture(int width, int height, GLuint textureID = 0); void setupArrays(int num_points); void setupTextures(int resolution); void drawLineTexture(const std::vector& xPoints, const std::vector& yPoints, const std::vector& zPoints); void saveTextureToPNG(Texture texture, const juce::File& file); void activateTargetTexture(std::optional texture); void setShader(juce::OpenGLShaderProgram* program); void drawTexture(std::vector> textures); void setAdditiveBlending(); void setNormalBlending(); void drawLine(const std::vector& xPoints, const std::vector& yPoints, const std::vector& zPoints); void fade(); void drawCRT(); void checkGLErrors(juce::String file, int line); void viewportChanged(juce::Rectangle area); void renderScope(const std::vector& xPoints, const std::vector& yPoints, const std::vector& zPoints); double getSweepIncrement(); Texture createScreenTexture(); Texture createReflectionTexture(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserRenderer) JUCE_DECLARE_WEAK_REFERENCEABLE(VisualiserRenderer) };