Refactor software oscilloscope and remove legacy visualiser

pull/261/head
James H Ball 2024-11-09 19:19:36 +00:00 zatwierdzone przez James H Ball
rodzic ee7e040fa6
commit 7f12ee9490
27 zmienionych plików z 459 dodań i 644 usunięć

Wyświetl plik

@ -4,7 +4,7 @@
#include "PluginProcessor.h"
#include "parser/FileParser.h"
#include "parser/FrameProducer.h"
#include "components/VisualiserComponent.h"
#include "visualiser/VisualiserComponent.h"
#include "audio/PitchDetector.h"
#include "UGen/ugen_JuceEnvelopeComponent.h"
#include "components/SvgButton.h"

Wyświetl plik

@ -9,7 +9,7 @@
#include "LookAndFeel.h"
#include "components/ErrorCodeEditorComponent.h"
#include "components/LuaConsole.h"
#include "components/VisualiserSettings.h"
#include "visualiser/VisualiserSettings.h"
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener {
public:
@ -54,7 +54,7 @@ public:
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor, audioProcessor.threadManager, visualiserSettings, nullptr, audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue()};
VisualiserComponent visualiser{audioProcessor.threadManager, visualiserSettings, nullptr};
SettingsComponent settings{audioProcessor, *this};

Wyświetl plik

@ -182,7 +182,6 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
booleanParameters.push_back(visualiserParameters.graticuleEnabled);
booleanParameters.push_back(visualiserParameters.smudgesEnabled);
booleanParameters.push_back(visualiserParameters.upsamplingEnabled);
booleanParameters.push_back(visualiserParameters.legacyVisualiserEnabled);
booleanParameters.push_back(visualiserParameters.visualiserFullScreen);
for (auto parameter : booleanParameters) {
@ -824,11 +823,6 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
fileXml->addTextElement(base64);
}
xml->setAttribute("currentFile", currentFile);
auto visualiserXml = xml->createNewChildElement("visualiser");
visualiserXml->setAttribute("roughness", visualiserParameters.roughness);
visualiserXml->setAttribute("intensity", visualiserParameters.intensity);
copyXmlToBinary(*xml, destData);
}
@ -942,12 +936,6 @@ void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInB
}
}
changeCurrentFile(xml->getIntAttribute("currentFile", -1));
auto visualiserXml = xml->getChildByName("visualiser");
if (visualiserXml != nullptr) {
visualiserParameters.roughness = visualiserXml->getIntAttribute("roughness");
visualiserParameters.intensity = visualiserXml->getDoubleAttribute("intensity");
}
broadcaster.sendChangeMessage();
prevMidiEnabled = !midiEnabled->getBoolValue();

Wyświetl plik

@ -14,7 +14,7 @@
#include "shape/Shape.h"
#include "concurrency/AudioBackgroundThread.h"
#include "concurrency/AudioBackgroundThreadManager.h"
#include "components/VisualiserSettings.h"
#include "visualiser/VisualiserSettings.h"
#include "audio/Effect.h"
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"

Wyświetl plik

@ -2,9 +2,9 @@
#include <JuceHeader.h>
#include "SosciPluginProcessor.h"
#include "components/VisualiserComponent.h"
#include "visualiser/VisualiserComponent.h"
#include "LookAndFeel.h"
#include "components/VisualiserSettings.h"
#include "visualiser/VisualiserSettings.h"
#include "components/SosciMainMenuBarModel.h"
#include "components/SvgButton.h"
@ -31,7 +31,7 @@ public:
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.parameters, 3);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor, audioProcessor.threadManager, visualiserSettings, nullptr, false, true};
VisualiserComponent visualiser{audioProcessor.threadManager, visualiserSettings, nullptr, true};
std::unique_ptr<juce::FileChooser> chooser;
SosciMainMenuBarModel menuBarModel{*this, audioProcessor};

Wyświetl plik

@ -39,7 +39,6 @@ SosciAudioProcessor::SosciAudioProcessor()
booleanParameters.push_back(parameters.graticuleEnabled);
booleanParameters.push_back(parameters.smudgesEnabled);
booleanParameters.push_back(parameters.upsamplingEnabled);
booleanParameters.push_back(parameters.legacyVisualiserEnabled);
booleanParameters.push_back(parameters.visualiserFullScreen);
for (auto parameter : booleanParameters) {

Wyświetl plik

@ -12,7 +12,7 @@
#include "concurrency/AudioBackgroundThread.h"
#include "concurrency/AudioBackgroundThreadManager.h"
#include "audio/SampleRateManager.h"
#include "components/VisualiserSettings.h"
#include "visualiser/VisualiserSettings.h"
#include "audio/Effect.h"
//==============================================================================

Wyświetl plik

@ -2,26 +2,15 @@
#include "../PluginEditor.h"
#include "../PluginProcessor.h"
MainMenuBarModel::MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {
audioProcessor.visualiserParameters.legacyVisualiserEnabled->addListener(this);
}
MainMenuBarModel::MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {}
MainMenuBarModel::~MainMenuBarModel() {
audioProcessor.visualiserParameters.legacyVisualiserEnabled->removeListener(this);
}
void MainMenuBarModel::parameterValueChanged(int parameterIndex, float legacyVisualiserEnabled) {
editor.visualiser.setVisualiserType(legacyVisualiserEnabled >= 0.5f);
menuItemsChanged();
}
void MainMenuBarModel::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
MainMenuBarModel::~MainMenuBarModel() {}
juce::StringArray MainMenuBarModel::getMenuBarNames() {
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
return juce::StringArray("File", "View", "About", "Audio");
return juce::StringArray("File", "About", "Audio");
} else {
return juce::StringArray("File", "View", "About");
return juce::StringArray("File", "About");
}
}
@ -36,10 +25,8 @@ juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const j
menu.addItem(4, "Create New Project");
}
} else if (topLevelMenuIndex == 1) {
menu.addItem(1, "Use Legacy Visualiser", true, audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue());
} else if (topLevelMenuIndex == 2) {
menu.addItem(1, "About osci-render");
} else if (topLevelMenuIndex == 3) {
} else if (topLevelMenuIndex == 2) {
menu.addItem(1, "Settings");
}
@ -67,10 +54,6 @@ void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
}
break;
case 1: {
audioProcessor.visualiserParameters.legacyVisualiserEnabled->setBoolValueNotifyingHost(!audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue());
menuItemsChanged();
} break;
case 2: {
juce::DialogWindow::LaunchOptions options;
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize,
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName + "\n"
@ -96,7 +79,7 @@ void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
juce::DialogWindow* dw = options.launchAsync();
} break;
case 3:
case 2:
editor.openAudioSettings();
break;
default:

Wyświetl plik

@ -5,7 +5,7 @@
class OscirenderAudioProcessorEditor;
class OscirenderAudioProcessor;
class MainMenuBarModel : public juce::MenuBarModel, public juce::AudioProcessorParameter::Listener {
class MainMenuBarModel : public juce::MenuBarModel {
public:
MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor);
~MainMenuBarModel();
@ -14,8 +14,6 @@ public:
juce::PopupMenu getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) override;
void menuItemSelected(int menuItemID, int topLevelMenuIndex) override;
void menuBarActivated(bool isActive);
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
private:
OscirenderAudioProcessor& audioProcessor;

Wyświetl plik

@ -1,285 +0,0 @@
#include "../LookAndFeel.h"
#include "VisualiserComponent.h"
VisualiserComponent::VisualiserComponent(SampleRateManager& sampleRateManager, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser, bool visualiserOnly) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), sampleRateManager(sampleRateManager), threadManager(threadManager), oldVisualiser(useOldVisualiser), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent", threadManager), parent(parent) {
addChildComponent(openGLVisualiser);
setVisualiserType(oldVisualiser);
startTimerHz(60);
setShouldBeRunning(true);
addAndMakeVisible(record);
record.setPulseAnimation(true);
record.onClick = [this] {
toggleRecording();
stopwatch.stop();
stopwatch.reset();
if (record.getToggleState()) {
stopwatch.start();
}
resized();
};
addAndMakeVisible(stopwatch);
setMouseCursor(juce::MouseCursor::PointingHandCursor);
setWantsKeyboardFocus(true);
roughness.textBox.setValue(settings.parameters.roughness);
roughness.textBox.onValueChange = [this]() {
this->settings.parameters.roughness = (int) roughness.textBox.getValue();
};
intensity.textBox.setValue(settings.parameters.intensity);
intensity.textBox.onValueChange = [this]() {
this->settings.parameters.intensity = intensity.textBox.getValue();
};
if (parent == nullptr && !visualiserOnly) {
addAndMakeVisible(fullScreenButton);
}
if (child == nullptr && parent == nullptr && !visualiserOnly) {
addAndMakeVisible(popOutButton);
}
addAndMakeVisible(settingsButton);
fullScreenButton.onClick = [this]() {
enableFullScreen();
};
settingsButton.onClick = [this]() {
if (oldVisualiser) {
juce::PopupMenu menu;
menu.addCustomItem(1, roughness, 160, 40, false);
menu.addCustomItem(1, intensity, 160, 40, false);
menu.showMenuAsync(juce::PopupMenu::Options(), [this](int result) {});
} else if (openSettings != nullptr) {
openSettings();
}
};
popOutButton.onClick = [this]() {
popoutWindow();
};
setFullScreen(false);
}
VisualiserComponent::~VisualiserComponent() {
masterReference.clear();
}
void VisualiserComponent::setFullScreenCallback(std::function<void(FullScreenMode)> callback) {
fullScreenCallback = callback;
}
void VisualiserComponent::enableFullScreen() {
if (fullScreenCallback) {
fullScreenCallback(FullScreenMode::TOGGLE);
}
grabKeyboardFocus();
}
void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) {
enableFullScreen();
}
void VisualiserComponent::setBuffer(const std::vector<OsciPoint>& newBuffer) {
juce::CriticalSection::ScopedLockType scope(lock);
if (!oldVisualiser) {
openGLVisualiser.updateBuffer(newBuffer);
return;
}
buffer.clear();
int stride = oldVisualiser ? roughness.textBox.getValue() : 1;
for (int i = 0; i < newBuffer.size(); i += stride) {
buffer.push_back(newBuffer[i]);
}
}
void VisualiserComponent::setColours(juce::Colour bk, juce::Colour fg) {
backgroundColour = bk;
waveformColour = fg;
}
void VisualiserComponent::paint(juce::Graphics& g) {
g.fillAll(Colours::veryDark);
if (oldVisualiser) {
g.setColour(backgroundColour);
g.fillRoundedRectangle(getLocalBounds().toFloat(), OscirenderLookAndFeel::RECT_RADIUS);
auto r = getLocalBounds().toFloat();
r.removeFromBottom(25);
auto minDim = juce::jmin(r.getWidth(), r.getHeight());
{
juce::CriticalSection::ScopedLockType scope(lock);
if (buffer.size() > 0) {
g.setColour(waveformColour);
paintXY(g, r.withSizeKeepingCentre(minDim, minDim));
}
}
if (!active) {
// add translucent layer
g.setColour(juce::Colours::black.withAlpha(0.5f));
g.fillRoundedRectangle(r, OscirenderLookAndFeel::RECT_RADIUS);
// add text
g.setColour(juce::Colours::white);
g.setFont(14.0f);
auto text = child != nullptr ? "Open in separate window" : "Paused";
g.drawFittedText(text, r.toNearestInt(), juce::Justification::centred, 1);
}
}
}
void VisualiserComponent::timerCallback() {
if (oldVisualiser) {
repaint();
}
}
void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
setBuffer(points);
}
int VisualiserComponent::prepareTask(double sampleRate, int bufferSize) {
return sampleRate * BUFFER_LENGTH_SECS;
}
void VisualiserComponent::setPaused(bool paused) {
openGLVisualiser.setPaused(paused);
active = !paused;
if (active) {
startTimerHz(60);
} else {
stopTimer();
}
setShouldBeRunning(active);
repaint();
}
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
if (event.mods.isLeftButtonDown() && child == nullptr) {
setPaused(active);
}
}
bool VisualiserComponent::keyPressed(const juce::KeyPress& key) {
if (key.isKeyCode(juce::KeyPress::escapeKey)) {
if (fullScreenCallback) {
fullScreenCallback(FullScreenMode::MAIN_COMPONENT);
}
return true;
}
return false;
}
void VisualiserComponent::setFullScreen(bool fullScreen) {}
void VisualiserComponent::setVisualiserType(bool oldVisualiser) {
this->oldVisualiser = oldVisualiser;
if (child != nullptr) {
child->setVisualiserType(oldVisualiser);
}
if (oldVisualiser) {
if (closeSettings != nullptr) {
closeSettings();
}
}
openGLVisualiser.setVisible(!oldVisualiser);
resized();
repaint();
}
void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area) {
auto transform = juce::AffineTransform::fromTargetPoints(-1.0f, -1.0f, area.getX(), area.getBottom(), 1.0f, 1.0f, area.getRight(), area.getY(), 1.0f, -1.0f, area.getRight(), area.getBottom());
std::vector<juce::Line<float>> lines;
for (int i = 2; i < buffer.size(); i++) {
lines.emplace_back(buffer[i - 1].x, buffer[i - 1].y, buffer[i].x, buffer[i].y);
}
double strength = 15;
double widthDivisor = 160;
double lengthIntensityScale = 700;
juce::Colour waveColor = waveformColour;
for (auto& line : lines) {
double thickness = area.getWidth() / widthDivisor;
float normalisedLength = line.getLength() * (sampleRate / DEFAULT_SAMPLE_RATE) / roughness.textBox.getValue();
line.applyTransform(transform);
double beamIntensity = intensity.textBox.getValue();
double lengthScale = (lengthIntensityScale * 0.5 + lengthIntensityScale * (1 - beamIntensity)) * (normalisedLength + 0.001);
double lengthScaleLog = std::log(strength * (1 / lengthScale) + 1) / std::log(strength + 1);
g.setColour(waveColor.withAlpha((float) std::max(0.0, std::min(lengthScaleLog * beamIntensity, 1.0))).withSaturation(lengthScale / 4));
if (normalisedLength < 0.00001) {
g.fillEllipse(line.getStartX(), line.getStartY(), thickness, thickness);
} else {
g.drawLine(line, thickness);
}
}
}
void VisualiserComponent::toggleRecording() {
}
void VisualiserComponent::haltRecording() {
record.setToggleState(false, juce::NotificationType::dontSendNotification);
}
void VisualiserComponent::resized() {
auto area = getLocalBounds();
juce::Rectangle<int> topRow;
if (visualiserOnly) {
topRow = area.removeFromTop(25);
} else {
topRow = area.removeFromBottom(25);
}
if (parent == nullptr && !visualiserOnly) {
fullScreenButton.setBounds(topRow.removeFromRight(30));
}
if (child == nullptr && parent == nullptr && !visualiserOnly) {
popOutButton.setBounds(topRow.removeFromRight(30));
}
settingsButton.setBounds(topRow.removeFromRight(30));
record.setBounds(topRow.removeFromRight(25));
if (record.getToggleState()) {
stopwatch.setVisible(true);
stopwatch.setBounds(topRow.removeFromRight(100));
} else {
stopwatch.setVisible(false);
}
if (!oldVisualiser) {
openGLVisualiser.setBounds(area);
}
}
void VisualiserComponent::popoutWindow() {
haltRecording();
auto visualiser = new VisualiserComponent(sampleRateManager, threadManager, settings, this, oldVisualiser);
visualiser->settings.setLookAndFeel(&getLookAndFeel());
visualiser->openSettings = openSettings;
visualiser->closeSettings = closeSettings;
visualiser->recordingHalted = recordingHalted;
child = visualiser;
childUpdated();
visualiser->setSize(300, 325);
popout = std::make_unique<VisualiserWindow>("Software Oscilloscope", this);
popout->setContentOwned(visualiser, true);
popout->setUsingNativeTitleBar(true);
popout->setResizable(true, false);
popout->setVisible(true);
popout->centreWithSize(300, 325);
setPaused(true);
resized();
}
void VisualiserComponent::childUpdated() {
popOutButton.setVisible(child == nullptr);
}

Wyświetl plik

@ -1,118 +0,0 @@
#pragma once
#include <algorithm>
#include <JuceHeader.h>
#include "../LookAndFeel.h"
#include "../concurrency/AudioBackgroundThread.h"
#include "../audio/SampleRateManager.h"
#include "../components/LabelledTextBox.h"
#include "../components/SvgButton.h"
#include "VisualiserSettings.h"
#include "VisualiserOpenGLComponent.h"
#include "../components/StopwatchComponent.h"
enum class FullScreenMode {
TOGGLE,
FULL_SCREEN,
MAIN_COMPONENT,
};
class VisualiserWindow;
class VisualiserComponent : public juce::Component, public juce::Timer, public AudioBackgroundThread, public juce::MouseListener, public juce::SettableTooltipClient {
public:
VisualiserComponent(SampleRateManager& sampleRateManager, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false, bool visualiserOnly = false);
~VisualiserComponent() override;
std::function<void()> openSettings;
std::function<void()> closeSettings;
void enableFullScreen();
void setFullScreenCallback(std::function<void(FullScreenMode)> callback);
void mouseDoubleClick(const juce::MouseEvent& event) override;
void setBuffer(const std::vector<OsciPoint>& buffer);
void setColours(juce::Colour backgroundColour, juce::Colour waveformColour);
void paintXY(juce::Graphics&, juce::Rectangle<float> bounds);
void paint(juce::Graphics&) override;
void resized() override;
void timerCallback() override;
int prepareTask(double sampleRate, int samplesPerBlock) override;
void runTask(const std::vector<OsciPoint>& points) override;
void setPaused(bool paused);
void mouseDown(const juce::MouseEvent& event) override;
bool keyPressed(const juce::KeyPress& key) override;
void setFullScreen(bool fullScreen);
void setVisualiserType(bool oldVisualiser);
void toggleRecording();
void haltRecording();
void childUpdated();
VisualiserComponent* parent = nullptr;
VisualiserComponent* child = nullptr;
std::unique_ptr<VisualiserWindow> popout = nullptr;
std::atomic<bool> active = true;
std::function<void()> recordingHalted;
private:
// 60fps
const double BUFFER_LENGTH_SECS = 1/60.0;
const double DEFAULT_SAMPLE_RATE = 192000.0;
std::atomic<int> timerId;
std::atomic<int> lastMouseX;
std::atomic<int> lastMouseY;
std::atomic<bool> oldVisualiser;
bool visualiserOnly;
juce::CriticalSection lock;
std::vector<OsciPoint> buffer;
std::vector<juce::Line<float>> prevLines;
juce::Colour backgroundColour, waveformColour;
SampleRateManager& sampleRateManager;
AudioBackgroundThreadManager& threadManager;
int sampleRate = DEFAULT_SAMPLE_RATE;
LabelledTextBox roughness{"Roughness", 1, 8, 1};
LabelledTextBox intensity{"Intensity", 0, 1, 0.01};
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 };
int precision = 4;
std::function<void(FullScreenMode)> fullScreenCallback;
VisualiserSettings& settings;
VisualiserOpenGLComponent openGLVisualiser {settings, sampleRateManager};
std::unique_ptr<juce::FileChooser> chooser;
juce::File tempVideoFile;
StopwatchComponent stopwatch;
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
void popoutWindow();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent)
JUCE_DECLARE_WEAK_REFERENCEABLE(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;
};

Wyświetl plik

@ -1,3 +1,5 @@
std::string blurFragmentShader = R"(
uniform sampler2D uTexture0;
uniform vec2 uOffset;
varying vec2 vTexCoord;
@ -23,3 +25,5 @@ void main() {
sum += texture2D(uTexture0, vTexCoord + uOffset*8.0) * 0.000078;
gl_FragColor = sum;
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string blurVertexShader = R"(
attribute vec2 aPos;
varying vec2 vTexCoord;
@ -5,3 +7,5 @@ void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
vTexCoord = (0.5*aPos+0.5);
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string lineFragmentShader = R"(
#define EPS 1E-6
#define TAU 6.283185307179586
#define TAUR 2.5066282746310002
@ -43,3 +45,5 @@ void main() {
gl_FragColor = 2.0 * texture2D(uScreen, vTexCoord) * brightness;
gl_FragColor.a = 1.0;
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string lineVertexShader = R"(
#define EPS 1E-6
uniform float uInvert;
@ -66,3 +68,5 @@ void main () {
//seed = mod(sin(seed*seed), 7.0);
//if (mod(seed/2.0, 1.0)<0.5) gl_Position = vec4(10.0);
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string outputFragmentShader = R"(
uniform sampler2D uTexture0; //line
uniform sampler2D uTexture1; //tight glow
uniform sampler2D uTexture2; //big glow
@ -35,3 +37,5 @@ void main() {
gl_FragColor.rgb += (1.0 / 255.0) * gradientNoise(gl_FragCoord.xy) - (0.5 / 255.0);
gl_FragColor.a = 1.0;
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string outputVertexShader = R"(
attribute vec2 aPos;
varying vec2 vTexCoord;
varying vec2 vTexCoordCanvas;
@ -8,3 +10,5 @@ void main() {
vTexCoord = (0.5 * aPos + 0.5);
vTexCoordCanvas = vTexCoord * uResizeForCanvas;
}
)";

Wyświetl plik

@ -1,5 +1,9 @@
std::string simpleFragmentShader = R"(
uniform vec4 colour;
void main() {
gl_FragColor = colour;
}
)";

Wyświetl plik

@ -1,5 +1,9 @@
std::string simpleVertexShader = R"(
attribute vec2 vertexPosition;
void main() {
gl_Position = vec4(vertexPosition, 0.0, 1.0);
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string texturedFragmentShader = R"(
uniform sampler2D uTexture0;
varying vec2 vTexCoord;
@ -5,3 +7,5 @@ void main() {
gl_FragColor = texture2D(uTexture0, vTexCoord);
gl_FragColor.a = 1.0;
}
)";

Wyświetl plik

@ -1,3 +1,5 @@
std::string texturedVertexShader = R"(
attribute vec2 aPos;
varying vec2 vTexCoord;
uniform float uResizeForCanvas;
@ -6,3 +8,5 @@ void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
vTexCoord = (0.5 * aPos + 0.5) * uResizeForCanvas;
}
)";

Wyświetl plik

@ -1,20 +1,194 @@
#define CPP_GLSL_INCLUDE
#include "../LookAndFeel.h"
#include "VisualiserOpenGLComponent.h"
#include "VisualiserComponent.h"
#include "BlurFragmentShader.glsl"
#include "BlurVertexShader.glsl"
#include "LineFragmentShader.glsl"
#include "LineVertexShader.glsl"
#include "OutputFragmentShader.glsl"
#include "OutputVertexShader.glsl"
#include "SimpleFragmentShader.glsl"
#include "SimpleVertexShader.glsl"
#include "TexturedFragmentShader.glsl"
#include "TexturedVertexShader.glsl"
VisualiserComponent::VisualiserComponent(AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent", threadManager), parent(parent) {
setShouldBeRunning(true);
addAndMakeVisible(record);
record.setPulseAnimation(true);
record.onClick = [this] {
toggleRecording();
stopwatch.stop();
stopwatch.reset();
if (record.getToggleState()) {
stopwatch.start();
}
resized();
};
addAndMakeVisible(stopwatch);
setMouseCursor(juce::MouseCursor::PointingHandCursor);
setWantsKeyboardFocus(true);
if (parent == nullptr && !visualiserOnly) {
addAndMakeVisible(fullScreenButton);
}
if (child == nullptr && parent == nullptr && !visualiserOnly) {
addAndMakeVisible(popOutButton);
}
addAndMakeVisible(settingsButton);
fullScreenButton.onClick = [this]() {
enableFullScreen();
};
settingsButton.onClick = [this]() {
openSettings();
};
popOutButton.onClick = [this]() {
popoutWindow();
};
VisualiserOpenGLComponent::VisualiserOpenGLComponent(VisualiserSettings& settings, SampleRateManager& sampleRateManager) : settings(settings), sampleRateManager(sampleRateManager) {
setFullScreen(false);
openGLContext.setRenderer(this);
openGLContext.attachTo(*this);
setInterceptsMouseClicks(false, false);
}
VisualiserOpenGLComponent::~VisualiserOpenGLComponent() {
VisualiserComponent::~VisualiserComponent() {
openGLContext.detach();
}
void VisualiserOpenGLComponent::newOpenGLContextCreated() {
void VisualiserComponent::setFullScreenCallback(std::function<void(FullScreenMode)> callback) {
fullScreenCallback = callback;
}
void VisualiserComponent::enableFullScreen() {
if (fullScreenCallback) {
fullScreenCallback(FullScreenMode::TOGGLE);
}
grabKeyboardFocus();
}
void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) {
enableFullScreen();
}
void VisualiserComponent::setBuffer(const std::vector<OsciPoint>& buffer) {
juce::CriticalSection::ScopedLockType lock(samplesLock);
if (xSamples.size() != buffer.size()) {
needsReattach = true;
}
xSamples.clear();
ySamples.clear();
zSamples.clear();
for (auto& point : buffer) {
xSamples.push_back(point.x);
ySamples.push_back(point.y);
zSamples.push_back(point.z);
}
triggerAsyncUpdate();
}
void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
setBuffer(points);
}
int VisualiserComponent::prepareTask(double sampleRate, int bufferSize) {
this->sampleRate = sampleRate;
xResampler.prepare(sampleRate, RESAMPLE_RATIO);
yResampler.prepare(sampleRate, RESAMPLE_RATIO);
zResampler.prepare(sampleRate, RESAMPLE_RATIO);
return sampleRate / FRAME_RATE;
}
void VisualiserComponent::setPaused(bool paused) {
active = !paused;
setShouldBeRunning(active);
repaint();
}
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
if (event.mods.isLeftButtonDown() && child == nullptr) {
setPaused(active);
}
}
bool VisualiserComponent::keyPressed(const juce::KeyPress& key) {
if (key.isKeyCode(juce::KeyPress::escapeKey)) {
if (fullScreenCallback) {
fullScreenCallback(FullScreenMode::MAIN_COMPONENT);
}
return true;
}
return false;
}
void VisualiserComponent::setFullScreen(bool fullScreen) {}
void VisualiserComponent::toggleRecording() {
}
void VisualiserComponent::haltRecording() {
record.setToggleState(false, juce::NotificationType::dontSendNotification);
}
void VisualiserComponent::resized() {
auto area = getLocalBounds();
if (visualiserOnly) {
buttonRow = area.removeFromTop(25);
} else {
buttonRow = area.removeFromBottom(25);
}
if (parent == nullptr && !visualiserOnly) {
fullScreenButton.setBounds(buttonRow.removeFromRight(30));
}
if (child == nullptr && parent == nullptr && !visualiserOnly) {
popOutButton.setBounds(buttonRow.removeFromRight(30));
}
settingsButton.setBounds(buttonRow.removeFromRight(30));
record.setBounds(buttonRow.removeFromRight(25));
if (record.getToggleState()) {
stopwatch.setVisible(true);
stopwatch.setBounds(buttonRow.removeFromRight(100));
} else {
stopwatch.setVisible(false);
}
viewportArea = area;
viewportChanged(viewportArea);
}
void VisualiserComponent::popoutWindow() {
haltRecording();
auto visualiser = new VisualiserComponent(threadManager, settings, this);
visualiser->settings.setLookAndFeel(&getLookAndFeel());
visualiser->openSettings = openSettings;
visualiser->closeSettings = closeSettings;
visualiser->recordingHalted = recordingHalted;
child = visualiser;
childUpdated();
visualiser->setSize(300, 325);
popout = std::make_unique<VisualiserWindow>("Software Oscilloscope", this);
popout->setContentOwned(visualiser, true);
popout->setUsingNativeTitleBar(true);
popout->setResizable(true, false);
popout->setVisible(true);
popout->centreWithSize(300, 325);
setPaused(true);
resized();
}
void VisualiserComponent::childUpdated() {
popOutButton.setVisible(child == nullptr);
}
void VisualiserComponent::newOpenGLContextCreated() {
using namespace juce::gl;
juce::CriticalSection::ScopedLockType lock(samplesLock);
@ -22,35 +196,35 @@ void VisualiserOpenGLComponent::newOpenGLContextCreated() {
juce::OpenGLHelpers::clear(juce::Colours::black);
glColorMask(true, true, true, true);
viewportChanged();
viewportChanged(viewportArea);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
fullScreenQuad = { -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f };
simpleShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
simpleShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(BinaryData::SimpleVertexShader_glsl));
simpleShader->addFragmentShader(BinaryData::SimpleFragmentShader_glsl);
simpleShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(simpleVertexShader));
simpleShader->addFragmentShader(simpleFragmentShader);
simpleShader->link();
lineShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
lineShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(BinaryData::LineVertexShader_glsl));
lineShader->addFragmentShader(BinaryData::LineFragmentShader_glsl);
lineShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(lineVertexShader));
lineShader->addFragmentShader(lineFragmentShader);
lineShader->link();
outputShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
outputShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(BinaryData::OutputVertexShader_glsl));
outputShader->addFragmentShader(BinaryData::OutputFragmentShader_glsl);
outputShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(outputVertexShader));
outputShader->addFragmentShader(outputFragmentShader);
outputShader->link();
texturedShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
texturedShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(BinaryData::TexturedVertexShader_glsl));
texturedShader->addFragmentShader(BinaryData::TexturedFragmentShader_glsl);
texturedShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(texturedVertexShader));
texturedShader->addFragmentShader(texturedFragmentShader);
texturedShader->link();
blurShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
blurShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(BinaryData::BlurVertexShader_glsl));
blurShader->addFragmentShader(BinaryData::BlurFragmentShader_glsl);
blurShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(blurVertexShader));
blurShader->addFragmentShader(blurFragmentShader);
blurShader->link();
glGenBuffers(1, &vertexBuffer);
@ -59,7 +233,7 @@ void VisualiserOpenGLComponent::newOpenGLContextCreated() {
setupArrays(smoothedXSamples.size());
}
void VisualiserOpenGLComponent::openGLContextClosing() {
void VisualiserComponent::openGLContextClosing() {
using namespace juce::gl;
glDeleteBuffers(1, &quadIndexBuffer);
@ -80,43 +254,21 @@ void VisualiserOpenGLComponent::openGLContextClosing() {
outputShader.reset();
}
void VisualiserOpenGLComponent::updateBuffer(const std::vector<OsciPoint>& buffer) {
juce::CriticalSection::ScopedLockType lock(samplesLock);
if (xSamples.size() != buffer.size()) {
needsReattach = true;
void VisualiserComponent::handleAsyncUpdate() {
{
juce::CriticalSection::ScopedLockType lock(samplesLock);
int newResampledSize = xSamples.size() * RESAMPLE_RATIO;
smoothedXSamples.resize(newResampledSize);
smoothedYSamples.resize(newResampledSize);
smoothedZSamples.resize(newResampledSize);
smoothedZSamples.resize(newResampledSize);
xResampler.process(xSamples.data(), smoothedXSamples.data(), xSamples.size());
yResampler.process(ySamples.data(), smoothedYSamples.data(), ySamples.size());
zResampler.process(zSamples.data(), smoothedZSamples.data(), zSamples.size());
}
xSamples.clear();
ySamples.clear();
zSamples.clear();
for (auto& point : buffer) {
xSamples.push_back(point.x);
ySamples.push_back(point.y);
zSamples.push_back(point.z);
}
triggerAsyncUpdate();
}
void VisualiserOpenGLComponent::handleAsyncUpdate() {
juce::CriticalSection::ScopedLockType lock(samplesLock);
int newResampledSize = xSamples.size() * RESAMPLE_RATIO;
smoothedXSamples.resize(newResampledSize);
smoothedYSamples.resize(newResampledSize);
smoothedZSamples.resize(newResampledSize);
if (sampleRate != sampleRateManager.getSampleRate()) {
sampleRate = sampleRateManager.getSampleRate();
xResampler.prepare(sampleRate, RESAMPLE_RATIO);
yResampler.prepare(sampleRate, RESAMPLE_RATIO);
zResampler.prepare(sampleRate, RESAMPLE_RATIO);
}
xResampler.process(xSamples.data(), smoothedXSamples.data(), xSamples.size());
yResampler.process(ySamples.data(), smoothedYSamples.data(), ySamples.size());
zResampler.process(zSamples.data(), smoothedZSamples.data(), zSamples.size());
if (needsReattach) {
openGLContext.detach();
@ -126,44 +278,47 @@ void VisualiserOpenGLComponent::handleAsyncUpdate() {
repaint();
}
void VisualiserOpenGLComponent::renderOpenGL() {
void VisualiserComponent::renderOpenGL() {
if (openGLContext.isActive()) {
juce::CriticalSection::ScopedLockType lock(samplesLock);
if (graticuleEnabled != settings.getGraticuleEnabled() || smudgesEnabled != settings.getSmudgesEnabled()) {
graticuleEnabled = settings.getGraticuleEnabled();
smudgesEnabled = settings.getSmudgesEnabled();
screenTexture = createScreenTexture();
juce::OpenGLHelpers::clear(juce::Colours::black);
if (active) {
juce::CriticalSection::ScopedLockType lock(samplesLock);
if (graticuleEnabled != settings.getGraticuleEnabled() || smudgesEnabled != settings.getSmudgesEnabled()) {
graticuleEnabled = settings.getGraticuleEnabled();
smudgesEnabled = settings.getSmudgesEnabled();
screenTexture = createScreenTexture();
}
renderScale = (float) openGLContext.getRenderingScale();
drawLineTexture(smoothedXSamples, smoothedYSamples, smoothedZSamples);
checkGLErrors("drawLineTexture");
drawCRT();
checkGLErrors("drawCRT");
}
renderScale = (float) openGLContext.getRenderingScale();
drawLineTexture(smoothedXSamples, smoothedYSamples, smoothedZSamples);
checkGLErrors("drawLineTexture");
drawCRT();
checkGLErrors("drawCRT");
}
}
void VisualiserOpenGLComponent::resized() {
viewportChanged();
}
void VisualiserOpenGLComponent::viewportChanged() {
void VisualiserComponent::viewportChanged(juce::Rectangle<int> area) {
using namespace juce::gl;
if (openGLContext.isAttached()) {
float realWidth = getWidth() * renderScale;
float realHeight = getHeight() * renderScale;
float realWidth = area.getWidth() * renderScale;
float realHeight = area.getHeight() * renderScale;
float xOffset = getWidth() * renderScale - realWidth;
float yOffset = getHeight() * renderScale - realHeight;
float minDim = juce::jmin(realWidth, realHeight);
float x = (realWidth - minDim) / 2;
float y = (realHeight - minDim) / 2;
float x = (realWidth - minDim) / 2 + area.getX() * renderScale + xOffset;
float y = (realHeight - minDim) / 2 + area.getY() * renderScale + yOffset;
glViewport(juce::roundToInt(x), juce::roundToInt(y), juce::roundToInt(minDim), juce::roundToInt(minDim));
}
}
void VisualiserOpenGLComponent::setupArrays(int nPoints) {
void VisualiserComponent::setupArrays(int nPoints) {
using namespace juce::gl;
if (nPoints == 0) {
@ -207,7 +362,7 @@ void VisualiserOpenGLComponent::setupArrays(int nPoints) {
scratchVertices.resize(12 * nPoints);
}
void VisualiserOpenGLComponent::setupTextures() {
void VisualiserComponent::setupTextures() {
using namespace juce::gl;
// Create the framebuffer
@ -226,7 +381,7 @@ void VisualiserOpenGLComponent::setupTextures() {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind
}
Texture VisualiserOpenGLComponent::makeTexture(int width, int height) {
Texture VisualiserComponent::makeTexture(int width, int height) {
using namespace juce::gl;
GLuint textureID;
@ -245,7 +400,7 @@ Texture VisualiserOpenGLComponent::makeTexture(int width, int height) {
return { textureID, width, height };
}
void VisualiserOpenGLComponent::drawLineTexture(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) {
void VisualiserComponent::drawLineTexture(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) {
using namespace juce::gl;
fadeAmount = juce::jmin(1.0, std::pow(0.5, settings.getPersistence()) * 0.4);
@ -255,7 +410,7 @@ void VisualiserOpenGLComponent::drawLineTexture(const std::vector<float>& xPoint
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
}
void VisualiserOpenGLComponent::saveTextureToFile(GLuint textureID, int width, int height, const juce::File& file) {
void VisualiserComponent::saveTextureToFile(GLuint textureID, int width, int height, const juce::File& file) {
using namespace juce::gl;
// Bind the texture to read its data
@ -301,7 +456,7 @@ void VisualiserOpenGLComponent::saveTextureToFile(GLuint textureID, int width, i
}
void VisualiserOpenGLComponent::activateTargetTexture(std::optional<Texture> texture) {
void VisualiserComponent::activateTargetTexture(std::optional<Texture> texture) {
using namespace juce::gl;
if (texture.has_value()) {
@ -310,17 +465,17 @@ void VisualiserOpenGLComponent::activateTargetTexture(std::optional<Texture> tex
glViewport(0, 0, texture.value().width, texture.value().height);
} else {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
viewportChanged();
viewportChanged(viewportArea);
}
targetTexture = texture;
}
void VisualiserOpenGLComponent::setShader(juce::OpenGLShaderProgram* program) {
void VisualiserComponent::setShader(juce::OpenGLShaderProgram* program) {
currentShader = program;
program->use();
}
void VisualiserOpenGLComponent::drawTexture(std::optional<Texture> texture0, std::optional<Texture> texture1, std::optional<Texture> texture2, std::optional<Texture> texture3) {
void VisualiserComponent::drawTexture(std::optional<Texture> texture0, std::optional<Texture> texture1, std::optional<Texture> texture2, std::optional<Texture> texture3) {
using namespace juce::gl;
glEnableVertexAttribArray(glGetAttribLocation(currentShader->getProgramID(), "aPos"));
@ -360,19 +515,19 @@ void VisualiserOpenGLComponent::drawTexture(std::optional<Texture> texture0, std
}
}
void VisualiserOpenGLComponent::setAdditiveBlending() {
void VisualiserComponent::setAdditiveBlending() {
using namespace juce::gl;
glBlendFunc(GL_ONE, GL_ONE);
}
void VisualiserOpenGLComponent::setNormalBlending() {
void VisualiserComponent::setNormalBlending() {
using namespace juce::gl;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void VisualiserOpenGLComponent::drawLine(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) {
void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) {
using namespace juce::gl;
setAdditiveBlending();
@ -408,7 +563,7 @@ void VisualiserOpenGLComponent::drawLine(const std::vector<float>& xPoints, cons
lineShader->setUniform("uGain", 450.0f / 512.0f);
lineShader->setUniform("uInvert", 1.0f);
float intensity = settings.getIntensity() * (41000.0f / sampleRateManager.getSampleRate());
float intensity = settings.getIntensity() * (41000.0f / sampleRate);
if (settings.getUpsamplingEnabled()) {
lineShader->setUniform("uIntensity", intensity);
} else {
@ -429,7 +584,7 @@ void VisualiserOpenGLComponent::drawLine(const std::vector<float>& xPoints, cons
glDisableVertexAttribArray(glGetAttribLocation(lineShader->getProgramID(), "aIdx"));
}
void VisualiserOpenGLComponent::fade() {
void VisualiserComponent::fade() {
using namespace juce::gl;
setNormalBlending();
@ -446,7 +601,7 @@ void VisualiserOpenGLComponent::fade() {
glDisableVertexAttribArray(glGetAttribLocation(simpleShader->getProgramID(), "vertexPosition"));
}
void VisualiserOpenGLComponent::drawCRT() {
void VisualiserComponent::drawCRT() {
setNormalBlending();
activateTargetTexture(blur1Texture);
@ -493,7 +648,7 @@ void VisualiserOpenGLComponent::drawCRT() {
drawTexture(lineTexture, blur1Texture, blur3Texture, screenTexture);
}
Texture VisualiserOpenGLComponent::createScreenTexture() {
Texture VisualiserComponent::createScreenTexture() {
using namespace juce::gl;
if (settings.getSmudgesEnabled()) {
@ -562,7 +717,7 @@ Texture VisualiserOpenGLComponent::createScreenTexture() {
return texture;
}
void VisualiserOpenGLComponent::checkGLErrors(const juce::String& location) {
void VisualiserComponent::checkGLErrors(const juce::String& location) {
using namespace juce::gl;
GLenum error;
@ -582,18 +737,14 @@ void VisualiserOpenGLComponent::checkGLErrors(const juce::String& location) {
}
}
void VisualiserOpenGLComponent::setPaused(bool paused) {
this->paused = paused;
repaint();
}
void VisualiserOpenGLComponent::paint(juce::Graphics& g) {
if (paused) {
g.setColour(juce::Colours::black.withAlpha(0.5f));
g.fillRect(getLocalBounds());
void VisualiserComponent::paint(juce::Graphics& g) {
g.setColour(juce::Colours::black);
g.fillRect(buttonRow);
if (!active) {
g.setColour(juce::Colours::white);
g.setFont(30.0f);
g.drawText("Paused", getLocalBounds(), juce::Justification::centred);
juce::String text = child == nullptr ? "Paused" : "Open in another window";
g.drawText(text, viewportArea, juce::Justification::centred);
}
}

Wyświetl plik

@ -1,9 +1,18 @@
#pragma once
#include <algorithm>
#include <JuceHeader.h>
#include "../LookAndFeel.h"
#include "../concurrency/AudioBackgroundThread.h"
#include "../components/SvgButton.h"
#include "VisualiserSettings.h"
#include "../audio/SampleRateManager.h"
#include "../shape/OsciPoint.h"
#include "../components/StopwatchComponent.h"
enum class FullScreenMode {
TOGGLE,
FULL_SCREEN,
MAIN_COMPONENT,
};
struct Texture {
GLuint id;
@ -11,23 +20,73 @@ struct Texture {
int height;
};
class VisualiserOpenGLComponent : public juce::Component, public juce::OpenGLRenderer, public juce::AsyncUpdater {
class VisualiserWindow;
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
public:
VisualiserOpenGLComponent(VisualiserSettings& settings, SampleRateManager& sampleRateManager);
~VisualiserOpenGLComponent() override;
VisualiserComponent(AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false);
~VisualiserComponent() override;
std::function<void()> openSettings;
std::function<void()> closeSettings;
void enableFullScreen();
void setFullScreenCallback(std::function<void(FullScreenMode)> callback);
void mouseDoubleClick(const juce::MouseEvent& event) override;
void setBuffer(const std::vector<OsciPoint>& buffer);
void resized() override;
void paint(juce::Graphics& g) override;
int prepareTask(double sampleRate, int samplesPerBlock) override;
void runTask(const std::vector<OsciPoint>& points) 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 resized() override;
void paint(juce::Graphics& g) override;
void updateBuffer(const std::vector<OsciPoint>& buffer);
void setPaused(bool paused);
void handleAsyncUpdate() override;
void setFullScreen(bool fullScreen);
void toggleRecording();
void haltRecording();
void childUpdated();
VisualiserComponent* parent = nullptr;
VisualiserComponent* child = nullptr;
std::unique_ptr<VisualiserWindow> popout = nullptr;
std::atomic<bool> active = true;
std::function<void()> recordingHalted;
private:
const double FRAME_RATE = 60.0;
bool visualiserOnly;
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 };
std::function<void(FullScreenMode)> fullScreenCallback;
VisualiserSettings& settings;
std::unique_ptr<juce::FileChooser> chooser;
juce::File tempVideoFile;
StopwatchComponent stopwatch;
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
void popoutWindow();
// OPENGL
juce::OpenGLContext openGLContext;
juce::Rectangle<int> buttonRow;
juce::Rectangle<int> viewportArea;
float renderScale = 1.0f;
GLuint quadIndexBuffer = 0;
@ -68,14 +127,10 @@ private:
std::unique_ptr<juce::OpenGLShaderProgram> outputShader;
juce::OpenGLShaderProgram* currentShader;
VisualiserSettings& settings;
SampleRateManager& sampleRateManager;
float fadeAmount;
bool smudgesEnabled = settings.getSmudgesEnabled();
bool graticuleEnabled = settings.getGraticuleEnabled();
bool paused = false;
const double RESAMPLE_RATIO = 6.0;
double sampleRate = -1;
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> xResampler;
@ -96,9 +151,25 @@ private:
void fade();
void drawCRT();
void checkGLErrors(const juce::String& location);
void viewportChanged();
void viewportChanged(juce::Rectangle<int> area);
Texture createScreenTexture();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserOpenGLComponent)
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;
};

Wyświetl plik

@ -10,13 +10,9 @@
class VisualiserParameters {
public:
std::atomic<int> roughness = 4;
std::atomic<double> intensity = 1.0;
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, true, "Show the graticule or grid lines over the oscilloscope display.");
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, true, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, false, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
BooleanParameter* legacyVisualiserEnabled = new BooleanParameter("Use Legacy Visualiser", "legacyVisualiserEnabled", VERSION_HINT, false, "Replaces the realistic oscilloscope visualiser with the legacy visualiser. This may improve performance.");
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(

Wyświetl plik

@ -54,28 +54,6 @@
<FILE id="N9Q6Zg" name="greek.txt" compile="0" resource="1" file="Resources/text/greek.txt"/>
<FILE id="OABuJy" name="helloworld.txt" compile="0" resource="1" file="Resources/text/helloworld.txt"/>
</GROUP>
<GROUP id="{4CF78D2F-9583-3931-AC9C-E26821FA8A38}" name="visualiser">
<FILE id="uEd2jz" name="BlurFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/BlurFragmentShader.glsl"/>
<FILE id="DG8mXB" name="BlurVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/BlurVertexShader.glsl"/>
<FILE id="tLvJZI" name="LineFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/LineFragmentShader.glsl"/>
<FILE id="FWis2l" name="LineVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/LineVertexShader.glsl"/>
<FILE id="T8tAlt" name="OutputFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/OutputFragmentShader.glsl"/>
<FILE id="RYUdS3" name="OutputVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/OutputVertexShader.glsl"/>
<FILE id="Jj50qw" name="SimpleFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/SimpleFragmentShader.glsl"/>
<FILE id="jwWJN2" name="SimpleVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/SimpleVertexShader.glsl"/>
<FILE id="IkEMkv" name="TexturedFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/TexturedFragmentShader.glsl"/>
<FILE id="JNBqis" name="TexturedVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/TexturedVertexShader.glsl"/>
</GROUP>
</GROUP>
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
@ -187,18 +165,6 @@
<FILE id="QQzSwh" name="SliderTextBox.h" compile="0" resource="0" file="Source/components/SliderTextBox.h"/>
<FILE id="QrDKRZ" name="SvgButton.h" compile="0" resource="0" file="Source/components/SvgButton.h"/>
<FILE id="qzfstC" name="SwitchButton.h" compile="0" resource="0" file="Source/components/SwitchButton.h"/>
<FILE id="w2BHaX" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/components/VisualiserComponent.cpp"/>
<FILE id="QFef4T" name="VisualiserComponent.h" compile="0" resource="0"
file="Source/components/VisualiserComponent.h"/>
<FILE id="kz22Iu" name="VisualiserOpenGLComponent.cpp" compile="1"
resource="0" file="Source/components/VisualiserOpenGLComponent.cpp"/>
<FILE id="EyhCOm" name="VisualiserOpenGLComponent.h" compile="0" resource="0"
file="Source/components/VisualiserOpenGLComponent.h"/>
<FILE id="VDt1r3" name="VisualiserSettings.cpp" compile="1" resource="0"
file="Source/components/VisualiserSettings.cpp"/>
<FILE id="IqelJa" name="VisualiserSettings.h" compile="0" resource="0"
file="Source/components/VisualiserSettings.h"/>
<FILE id="icFMpl" name="VListBox.cpp" compile="1" resource="0" file="Source/components/VListBox.cpp"/>
<FILE id="mvp8je" name="VListBox.h" compile="0" resource="0" file="Source/components/VListBox.h"/>
<FILE id="s8EVcE" name="VolumeComponent.cpp" compile="1" resource="0"
@ -614,6 +580,36 @@
<FILE id="mC1tUv" name="ugen_JuceUtility.h" compile="0" resource="0"
file="Source/UGen/ugen_JuceUtility.h"/>
</GROUP>
<GROUP id="{16A8DC64-BA02-898D-4DBA-AA3DDF6F9297}" name="visualiser">
<FILE id="kfMvdQ" name="BlurFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/BlurFragmentShader.glsl" xcodeResource="0"/>
<FILE id="c59gvD" name="BlurVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/BlurVertexShader.glsl" xcodeResource="0"/>
<FILE id="WZFPXF" name="LineFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/LineFragmentShader.glsl" xcodeResource="0"/>
<FILE id="iS2Ipw" name="LineVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/LineVertexShader.glsl" xcodeResource="0"/>
<FILE id="hrzO1G" name="OutputFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/OutputFragmentShader.glsl" xcodeResource="0"/>
<FILE id="A0CrLF" name="OutputVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/OutputVertexShader.glsl" xcodeResource="0"/>
<FILE id="Gwupwc" name="SimpleFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/SimpleFragmentShader.glsl"/>
<FILE id="RX1oPv" name="SimpleVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/SimpleVertexShader.glsl"/>
<FILE id="zR2PyI" name="TexturedFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/TexturedFragmentShader.glsl"/>
<FILE id="TSORlO" name="TexturedVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/TexturedVertexShader.glsl"/>
<FILE id="Ju0mVn" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/visualiser/VisualiserComponent.cpp"/>
<FILE id="frfxSR" name="VisualiserComponent.h" compile="0" resource="0"
file="Source/visualiser/VisualiserComponent.h"/>
<FILE id="iZM7s0" name="VisualiserSettings.cpp" compile="1" resource="0"
file="Source/visualiser/VisualiserSettings.cpp"/>
<FILE id="CaPdPD" name="VisualiserSettings.h" compile="0" resource="0"
file="Source/visualiser/VisualiserSettings.h"/>
</GROUP>
<GROUP id="{DC345620-B6F6-F3B9-D359-C265590B0F00}" name="wav">
<FILE id="vYzJlF" name="WavParser.cpp" compile="1" resource="0" file="Source/wav/WavParser.cpp"/>
<FILE id="ZRT5Xk" name="WavParser.h" compile="0" resource="0" file="Source/wav/WavParser.h"/>

Wyświetl plik

@ -40,28 +40,6 @@
<FILE id="rFYmV8" name="timer.svg" compile="0" resource="1" file="Resources/svg/timer.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
</GROUP>
<GROUP id="{30FB8C4D-D6E9-397E-AFCF-F0E3DB8D3452}" name="visualiser">
<FILE id="ubvkbM" name="BlurFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/BlurFragmentShader.glsl"/>
<FILE id="yx3ze8" name="BlurVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/BlurVertexShader.glsl"/>
<FILE id="TFSFmo" name="LineFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/LineFragmentShader.glsl"/>
<FILE id="HFl7ZR" name="LineVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/LineVertexShader.glsl"/>
<FILE id="cbvxCE" name="OutputFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/OutputFragmentShader.glsl"/>
<FILE id="pFp9Fz" name="OutputVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/OutputVertexShader.glsl"/>
<FILE id="iaabuJ" name="SimpleFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/SimpleFragmentShader.glsl"/>
<FILE id="IeFRq9" name="SimpleVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/SimpleVertexShader.glsl"/>
<FILE id="TAi8uQ" name="TexturedFragmentShader.glsl" compile="0" resource="1"
file="Resources/visualiser/TexturedFragmentShader.glsl"/>
<FILE id="H28hvi" name="TexturedVertexShader.glsl" compile="0" resource="1"
file="Resources/visualiser/TexturedVertexShader.glsl"/>
</GROUP>
</GROUP>
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
@ -95,18 +73,6 @@
file="Source/components/StopwatchComponent.h"/>
<FILE id="f2kkHV" name="SvgButton.h" compile="0" resource="0" file="Source/components/SvgButton.h"/>
<FILE id="qzfstC" name="SwitchButton.h" compile="0" resource="0" file="Source/components/SwitchButton.h"/>
<FILE id="CAr661" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/components/VisualiserComponent.cpp"/>
<FILE id="wEqs55" name="VisualiserComponent.h" compile="0" resource="0"
file="Source/components/VisualiserComponent.h"/>
<FILE id="Hdm7tx" name="VisualiserOpenGLComponent.cpp" compile="1"
resource="0" file="Source/components/VisualiserOpenGLComponent.cpp"/>
<FILE id="KdwehX" name="VisualiserOpenGLComponent.h" compile="0" resource="0"
file="Source/components/VisualiserOpenGLComponent.h"/>
<FILE id="JxmEAH" name="VisualiserSettings.cpp" compile="1" resource="0"
file="Source/components/VisualiserSettings.cpp"/>
<FILE id="cY2WUE" name="VisualiserSettings.h" compile="0" resource="0"
file="Source/components/VisualiserSettings.h"/>
</GROUP>
<GROUP id="{9F5970A9-8094-E7F3-7AC1-812AE5589B9F}" name="concurrency">
<FILE id="wAUzPb" name="AudioBackgroundThread.cpp" compile="1" resource="0"
@ -127,6 +93,36 @@
<FILE id="G5fbub" name="Shape.cpp" compile="1" resource="0" file="Source/shape/Shape.cpp"/>
<FILE id="NmptSY" name="Shape.h" compile="0" resource="0" file="Source/shape/Shape.h"/>
</GROUP>
<GROUP id="{04ACA01B-2ADE-8356-BF1E-32942E0F3CFA}" name="visualiser">
<FILE id="Y4j91J" name="BlurFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/BlurFragmentShader.glsl"/>
<FILE id="Fimn0E" name="BlurVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/BlurVertexShader.glsl"/>
<FILE id="R6Yr8V" name="LineFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/LineFragmentShader.glsl"/>
<FILE id="aK7kZN" name="LineVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/LineVertexShader.glsl"/>
<FILE id="BnnCmE" name="OutputFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/OutputFragmentShader.glsl"/>
<FILE id="df9jjf" name="OutputVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/OutputVertexShader.glsl"/>
<FILE id="ePJ1g0" name="SimpleFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/SimpleFragmentShader.glsl"/>
<FILE id="GFGIEu" name="SimpleVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/SimpleVertexShader.glsl"/>
<FILE id="bie77n" name="TexturedFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/TexturedFragmentShader.glsl"/>
<FILE id="EW75fo" name="TexturedVertexShader.glsl" compile="0" resource="0"
file="Source/visualiser/TexturedVertexShader.glsl"/>
<FILE id="pxVOD9" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/visualiser/VisualiserComponent.cpp"/>
<FILE id="MH6n0v" name="VisualiserComponent.h" compile="0" resource="0"
file="Source/visualiser/VisualiserComponent.h"/>
<FILE id="wdZb9U" name="VisualiserSettings.cpp" compile="1" resource="0"
file="Source/visualiser/VisualiserSettings.cpp"/>
<FILE id="v3pCdC" name="VisualiserSettings.h" compile="0" resource="0"
file="Source/visualiser/VisualiserSettings.h"/>
</GROUP>
<FILE id="d2zFqF" name="LookAndFeel.cpp" compile="1" resource="0" file="Source/LookAndFeel.cpp"/>
<FILE id="TJDqWs" name="LookAndFeel.h" compile="0" resource="0" file="Source/LookAndFeel.h"/>
<FILE id="ajg1QS" name="SosciPluginEditor.cpp" compile="1" resource="0"