#pragma once #include #include #include "../LookAndFeel.h" #include "../concurrency/AudioBackgroundThread.h" #include "../components/SvgButton.h" #include "VisualiserSettings.h" #include "RecordingSettings.h" #include "../components/StopwatchComponent.h" #include "../img/qoixx.hpp" #include "../components/DownloaderComponent.h" #include "../concurrency/WriteProcess.h" #include "../audio/AudioRecorder.h" #define FILE_RENDER_DUMMY 0 #define FILE_RENDER_PNG 1 #define FILE_RENDER_QOI 2 enum class FullScreenMode { TOGGLE, FULL_SCREEN, MAIN_COMPONENT, }; struct Texture { GLuint id; int width; int height; }; class VisualiserWindow; class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater { public: VisualiserComponent(juce::File& lastOpenedDirectory, juce::File ffmpegFile, std::function& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, RecordingParameters& recordingParameters, VisualiserComponent* parent = nullptr, bool visualiserOnly = false); ~VisualiserComponent() override; std::function openSettings; std::function closeSettings; void enableFullScreen(); void setFullScreenCallback(std::function callback); void mouseDoubleClick(const juce::MouseEvent& event) override; void resized() override; void paint(juce::Graphics& g) override; int prepareTask(double sampleRate, int samplesPerBlock) override; void runTask(const std::vector& points) override; void stopTask() override; void setPaused(bool paused); void mouseDown(const juce::MouseEvent& event) override; bool keyPressed(const juce::KeyPress& key) override; void handleAsyncUpdate() override; void newOpenGLContextCreated() override; void renderOpenGL() override; void openGLContextClosing() override; void setFullScreen(bool fullScreen); void setRecording(bool recording); void childUpdated(); VisualiserComponent* parent = nullptr; VisualiserComponent* child = nullptr; std::unique_ptr popout = nullptr; std::atomic active = true; private: float intensity; const double FRAME_RATE = 60.0; bool visualiserOnly; std::function& haltRecording; AudioBackgroundThreadManager& threadManager; SvgButton fullScreenButton{ "fullScreen", BinaryData::fullscreen_svg, juce::Colours::white, juce::Colours::white }; SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white }; SvgButton settingsButton{ "settings", BinaryData::cog_svg, juce::Colours::white, juce::Colours::white }; #if SOSCI_FEATURES SvgButton sharedTextureButton{ "sharedTexture", BinaryData::spout_svg, juce::Colours::white, juce::Colours::red }; SharedTextureSender* sharedTextureSender = nullptr; #endif std::function fullScreenCallback; VisualiserSettings& settings; RecordingParameters& recordingParameters; bool recordingAudio = false; bool recordingVideo = false; StopwatchComponent stopwatch; SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)}; long numFrames = 0; std::vector framePixels; WriteProcess ffmpegProcess; juce::File ffmpegFile; juce::File& lastOpenedDirectory; std::unique_ptr chooser; std::unique_ptr tempVideoFile; std::unique_ptr tempAudioFile; AudioRecorder audioRecorder; juce::String ffmpegURL = juce::String("https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/") + #if JUCE_WINDOWS #if JUCE_64BIT "ffmpeg-win32-x64" #elif JUCE_32BIT "ffmpeg-win32-ia32" #endif #elif JUCE_MAC #if JUCE_ARM "ffmpeg-darwin-arm64" #elif JUCE_INTEL "ffmpeg-darwin-x64" #endif #elif JUCE_LINUX #if JUCE_ARM #if JUCE_64BIT "ffmpeg-linux-arm64" #elif JUCE_32BIT "ffmpeg-linux-arm" #endif #elif JUCE_INTEL #if JUCE_64BIT "ffmpeg-linux-x64" #elif JUCE_32BIT "ffmpeg-linux-ia32" #endif #endif #endif + ".gz"; DownloaderComponent ffmpegDownloader{ffmpegURL, ffmpegFile}; Semaphore renderingSemaphore{0}; void popoutWindow(); // OPENGL juce::OpenGLContext openGLContext; float time = 0; juce::Rectangle buttonRow; juce::Rectangle viewportArea; float renderScale = 1.0f; GLuint quadIndexBuffer = 0; GLuint vertexIndexBuffer = 0; GLuint vertexBuffer = 0; int nEdges = 0; juce::CriticalSection samplesLock; std::vector xSamples{2}; std::vector ySamples{2}; std::vector zSamples{2}; std::vector smoothedXSamples; std::vector smoothedYSamples; std::vector smoothedZSamples; int sampleBufferCount = 0; int prevSampleBufferCount = 0; std::vector scratchVertices; std::vector fullScreenQuad; GLuint frameBuffer = 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 SOSCI_FEATURES juce::Image oscilloscopeImage = juce::ImageFileFormat::loadFrom(BinaryData::real_jpg, BinaryData::real_jpgSize); juce::Image vectorDisplayImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_jpg, BinaryData::vector_display_jpgSize); juce::Image emptyReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::no_reflection_jpg, BinaryData::no_reflection_jpgSize); juce::Image oscilloscopeReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::real_reflection_jpg, BinaryData::real_reflection_jpgSize); juce::Image vectorDisplayReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_reflection_jpg, BinaryData::vector_display_reflection_jpgSize); OsciPoint REAL_SCREEN_OFFSET = { 0.02, -0.15 }; OsciPoint REAL_SCREEN_SCALE = { 0.6 }; OsciPoint VECTOR_DISPLAY_OFFSET = { 0.075, -0.045 }; OsciPoint VECTOR_DISPLAY_SCALE = { 0.6 }; float VECTOR_DISPLAY_FISH_EYE = 0.5; juce::OpenGLTexture reflectionOpenGLTexture; Texture reflectionTexture; std::unique_ptr glowShader; #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; ScreenType screenType = settings.getScreenType(); 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); #if SOSCI_FEATURES void initialiseSharedTexture(); void closeSharedTexture(); #endif Texture makeTexture(int width, int height); void setupArrays(int num_points); void setupTextures(); void drawLineTexture(const std::vector& xPoints, const std::vector& yPoints, const std::vector& zPoints); void saveTextureToPNG(Texture texture, const juce::File& file); void saveTextureToQOI(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(const juce::String& location); void viewportChanged(juce::Rectangle area); void renderScope(const std::vector& xPoints, const std::vector& yPoints, const std::vector& zPoints); int renderAudioFile(juce::File& sourceAudio, int method = 1, int width = 1024, int height = 1024); Texture createScreenTexture(); Texture createReflectionTexture(); juce::File audioFile; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent) }; class VisualiserWindow : public juce::DocumentWindow { public: VisualiserWindow(juce::String name, VisualiserComponent* parent) : parent(parent), wasPaused(!parent->active), juce::DocumentWindow(name, juce::Colours::black, juce::DocumentWindow::TitleBarButtons::allButtons) {} void closeButtonPressed() override { parent->setPaused(wasPaused); parent->child = nullptr; parent->childUpdated(); parent->resized(); parent->popout.reset(); } private: VisualiserComponent* parent; bool wasPaused; };