2024-03-18 23:06:14 +00:00
|
|
|
#include "../LookAndFeel.h"
|
2024-08-13 13:55:30 +00:00
|
|
|
#include "VisualiserComponent.h"
|
2023-07-08 17:59:05 +00:00
|
|
|
|
2024-10-27 14:30:39 +00:00
|
|
|
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) {
|
2024-10-23 10:23:58 +00:00
|
|
|
addChildComponent(openGLVisualiser);
|
|
|
|
setVisualiserType(oldVisualiser);
|
2023-07-09 20:30:33 +00:00
|
|
|
startTimerHz(60);
|
2024-10-27 14:30:39 +00:00
|
|
|
setShouldBeRunning(true);
|
2024-06-28 20:27:48 +00:00
|
|
|
|
2024-10-23 10:23:58 +00:00
|
|
|
addAndMakeVisible(record);
|
|
|
|
record.setPulseAnimation(true);
|
|
|
|
record.onClick = [this] {
|
|
|
|
toggleRecording();
|
|
|
|
stopwatch.stop();
|
|
|
|
stopwatch.reset();
|
|
|
|
if (record.getToggleState()) {
|
|
|
|
stopwatch.start();
|
|
|
|
}
|
|
|
|
resized();
|
|
|
|
};
|
|
|
|
|
|
|
|
addAndMakeVisible(stopwatch);
|
|
|
|
|
2023-12-29 13:01:09 +00:00
|
|
|
setMouseCursor(juce::MouseCursor::PointingHandCursor);
|
|
|
|
setWantsKeyboardFocus(true);
|
2024-06-02 18:25:10 +00:00
|
|
|
|
2024-09-12 22:09:54 +00:00
|
|
|
roughness.textBox.setValue(settings.parameters.roughness);
|
2024-08-14 19:27:56 +00:00
|
|
|
roughness.textBox.onValueChange = [this]() {
|
2024-09-12 22:09:54 +00:00
|
|
|
this->settings.parameters.roughness = (int) roughness.textBox.getValue();
|
2024-08-14 19:27:56 +00:00
|
|
|
};
|
2024-09-12 22:09:54 +00:00
|
|
|
intensity.textBox.setValue(settings.parameters.intensity);
|
2024-08-14 19:27:56 +00:00
|
|
|
intensity.textBox.onValueChange = [this]() {
|
2024-09-12 22:09:54 +00:00
|
|
|
this->settings.parameters.intensity = intensity.textBox.getValue();
|
2024-08-14 19:27:56 +00:00
|
|
|
};
|
|
|
|
|
2024-10-23 10:23:58 +00:00
|
|
|
if (parent == nullptr && !visualiserOnly) {
|
|
|
|
addAndMakeVisible(fullScreenButton);
|
2024-08-14 19:27:56 +00:00
|
|
|
}
|
2024-10-23 10:23:58 +00:00
|
|
|
if (child == nullptr && parent == nullptr && !visualiserOnly) {
|
|
|
|
addAndMakeVisible(popOutButton);
|
2024-08-14 19:27:56 +00:00
|
|
|
}
|
2024-10-23 10:23:58 +00:00
|
|
|
addAndMakeVisible(settingsButton);
|
2024-08-14 19:27:56 +00:00
|
|
|
|
|
|
|
fullScreenButton.onClick = [this]() {
|
|
|
|
enableFullScreen();
|
|
|
|
};
|
|
|
|
|
|
|
|
settingsButton.onClick = [this]() {
|
2024-10-23 10:23:58 +00:00
|
|
|
if (oldVisualiser) {
|
|
|
|
juce::PopupMenu menu;
|
2024-06-02 18:25:10 +00:00
|
|
|
|
2024-10-23 10:23:58 +00:00
|
|
|
menu.addCustomItem(1, roughness, 160, 40, false);
|
|
|
|
menu.addCustomItem(1, intensity, 160, 40, false);
|
2024-06-02 18:25:10 +00:00
|
|
|
|
2024-10-23 10:23:58 +00:00
|
|
|
menu.showMenuAsync(juce::PopupMenu::Options(), [this](int result) {});
|
|
|
|
} else if (openSettings != nullptr) {
|
|
|
|
openSettings();
|
|
|
|
}
|
2024-08-14 19:27:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
popOutButton.onClick = [this]() {
|
|
|
|
popoutWindow();
|
|
|
|
};
|
2024-08-10 17:36:43 +00:00
|
|
|
|
|
|
|
setFullScreen(false);
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 12:17:04 +00:00
|
|
|
VisualiserComponent::~VisualiserComponent() {
|
2024-08-14 19:27:56 +00:00
|
|
|
masterReference.clear();
|
2023-07-10 12:17:04 +00:00
|
|
|
}
|
2023-07-08 17:59:05 +00:00
|
|
|
|
2023-12-29 13:01:09 +00:00
|
|
|
void VisualiserComponent::setFullScreenCallback(std::function<void(FullScreenMode)> callback) {
|
|
|
|
fullScreenCallback = callback;
|
|
|
|
}
|
|
|
|
|
2024-06-02 18:25:10 +00:00
|
|
|
void VisualiserComponent::enableFullScreen() {
|
2023-12-29 13:01:09 +00:00
|
|
|
if (fullScreenCallback) {
|
|
|
|
fullScreenCallback(FullScreenMode::TOGGLE);
|
|
|
|
}
|
|
|
|
grabKeyboardFocus();
|
|
|
|
}
|
|
|
|
|
2024-06-02 18:25:10 +00:00
|
|
|
void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) {
|
|
|
|
enableFullScreen();
|
|
|
|
}
|
|
|
|
|
2024-10-27 14:30:39 +00:00
|
|
|
void VisualiserComponent::setBuffer(const std::vector<OsciPoint>& newBuffer) {
|
2023-07-22 17:42:30 +00:00
|
|
|
juce::CriticalSection::ScopedLockType scope(lock);
|
2024-10-22 13:41:09 +00:00
|
|
|
if (!oldVisualiser) {
|
|
|
|
openGLVisualiser.updateBuffer(newBuffer);
|
|
|
|
return;
|
|
|
|
}
|
2023-07-08 17:59:05 +00:00
|
|
|
buffer.clear();
|
2024-08-14 19:27:56 +00:00
|
|
|
int stride = oldVisualiser ? roughness.textBox.getValue() : 1;
|
2024-09-22 17:49:58 +00:00
|
|
|
for (int i = 0; i < newBuffer.size(); i += stride) {
|
2023-07-08 17:59:05 +00:00
|
|
|
buffer.push_back(newBuffer[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisualiserComponent::setColours(juce::Colour bk, juce::Colour fg) {
|
|
|
|
backgroundColour = bk;
|
|
|
|
waveformColour = fg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisualiserComponent::paint(juce::Graphics& g) {
|
2024-10-23 10:23:58 +00:00
|
|
|
g.fillAll(Colours::veryDark);
|
2024-08-10 17:36:43 +00:00
|
|
|
if (oldVisualiser) {
|
|
|
|
g.setColour(backgroundColour);
|
2024-03-18 23:06:14 +00:00
|
|
|
g.fillRoundedRectangle(getLocalBounds().toFloat(), OscirenderLookAndFeel::RECT_RADIUS);
|
2024-08-10 17:36:43 +00:00
|
|
|
|
|
|
|
auto r = getLocalBounds().toFloat();
|
2024-10-23 10:23:58 +00:00
|
|
|
r.removeFromBottom(25);
|
2024-08-10 17:36:43 +00:00
|
|
|
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));
|
2024-10-23 10:23:58 +00:00
|
|
|
g.fillRoundedRectangle(r, OscirenderLookAndFeel::RECT_RADIUS);
|
2024-08-10 17:36:43 +00:00
|
|
|
|
|
|
|
// add text
|
|
|
|
g.setColour(juce::Colours::white);
|
|
|
|
g.setFont(14.0f);
|
|
|
|
auto text = child != nullptr ? "Open in separate window" : "Paused";
|
2024-10-23 10:23:58 +00:00
|
|
|
g.drawFittedText(text, r.toNearestInt(), juce::Justification::centred, 1);
|
2024-08-10 17:36:43 +00:00
|
|
|
}
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-09 20:30:33 +00:00
|
|
|
void VisualiserComponent::timerCallback() {
|
2024-08-10 17:36:43 +00:00
|
|
|
if (oldVisualiser) {
|
|
|
|
repaint();
|
|
|
|
}
|
2023-07-09 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
2024-10-27 14:30:39 +00:00
|
|
|
void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|
|
|
setBuffer(points);
|
|
|
|
}
|
2024-10-27 12:02:54 +00:00
|
|
|
|
2024-10-27 14:30:39 +00:00
|
|
|
int VisualiserComponent::prepareTask(double sampleRate, int bufferSize) {
|
|
|
|
return sampleRate * BUFFER_LENGTH_SECS;
|
2023-07-10 12:17:04 +00:00
|
|
|
}
|
|
|
|
|
2024-06-02 19:56:33 +00:00
|
|
|
void VisualiserComponent::setPaused(bool paused) {
|
2024-10-23 10:23:58 +00:00
|
|
|
openGLVisualiser.setPaused(paused);
|
2024-06-02 19:56:33 +00:00
|
|
|
active = !paused;
|
|
|
|
if (active) {
|
|
|
|
startTimerHz(60);
|
|
|
|
} else {
|
|
|
|
stopTimer();
|
|
|
|
}
|
2024-10-27 14:30:39 +00:00
|
|
|
setShouldBeRunning(active);
|
2024-06-02 19:56:33 +00:00
|
|
|
repaint();
|
|
|
|
}
|
|
|
|
|
2023-09-10 10:14:10 +00:00
|
|
|
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
|
2024-06-02 19:56:33 +00:00
|
|
|
if (event.mods.isLeftButtonDown() && child == nullptr) {
|
|
|
|
setPaused(active);
|
2024-06-02 18:25:10 +00:00
|
|
|
}
|
|
|
|
}
|
2023-12-21 18:31:18 +00:00
|
|
|
|
2023-12-29 13:01:09 +00:00
|
|
|
bool VisualiserComponent::keyPressed(const juce::KeyPress& key) {
|
|
|
|
if (key.isKeyCode(juce::KeyPress::escapeKey)) {
|
|
|
|
if (fullScreenCallback) {
|
|
|
|
fullScreenCallback(FullScreenMode::MAIN_COMPONENT);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-10 17:36:43 +00:00
|
|
|
void VisualiserComponent::setFullScreen(bool fullScreen) {}
|
2023-07-08 17:59:05 +00:00
|
|
|
|
2024-08-14 19:27:56 +00:00
|
|
|
void VisualiserComponent::setVisualiserType(bool oldVisualiser) {
|
|
|
|
this->oldVisualiser = oldVisualiser;
|
2024-08-20 20:25:31 +00:00
|
|
|
if (child != nullptr) {
|
|
|
|
child->setVisualiserType(oldVisualiser);
|
|
|
|
}
|
2024-08-14 19:27:56 +00:00
|
|
|
if (oldVisualiser) {
|
2024-08-21 12:43:30 +00:00
|
|
|
if (closeSettings != nullptr) {
|
|
|
|
closeSettings();
|
|
|
|
}
|
2024-08-14 19:27:56 +00:00
|
|
|
}
|
2024-10-23 10:23:58 +00:00
|
|
|
openGLVisualiser.setVisible(!oldVisualiser);
|
|
|
|
resized();
|
|
|
|
repaint();
|
2024-08-14 19:27:56 +00:00
|
|
|
}
|
|
|
|
|
2023-07-08 17:59:05 +00:00
|
|
|
void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area) {
|
2023-07-19 20:40:31 +00:00
|
|
|
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;
|
2023-07-08 17:59:05 +00:00
|
|
|
|
2024-09-22 17:49:58 +00:00
|
|
|
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);
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 18:59:57 +00:00
|
|
|
double strength = 15;
|
2023-12-21 19:58:00 +00:00
|
|
|
double widthDivisor = 160;
|
2023-12-21 18:31:18 +00:00
|
|
|
double lengthIntensityScale = 700;
|
2023-11-07 18:59:57 +00:00
|
|
|
juce::Colour waveColor = waveformColour;
|
|
|
|
|
2023-07-19 20:40:31 +00:00
|
|
|
for (auto& line : lines) {
|
2023-12-21 18:46:28 +00:00
|
|
|
double thickness = area.getWidth() / widthDivisor;
|
2023-12-21 18:31:18 +00:00
|
|
|
float normalisedLength = line.getLength() * (sampleRate / DEFAULT_SAMPLE_RATE) / roughness.textBox.getValue();
|
2023-07-19 20:40:31 +00:00
|
|
|
line.applyTransform(transform);
|
2023-12-21 18:31:18 +00:00
|
|
|
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));
|
2023-12-21 18:46:28 +00:00
|
|
|
|
|
|
|
if (normalisedLength < 0.00001) {
|
|
|
|
g.fillEllipse(line.getStartX(), line.getStartY(), thickness, thickness);
|
|
|
|
} else {
|
|
|
|
g.drawLine(line, thickness);
|
|
|
|
}
|
2023-07-19 20:40:31 +00:00
|
|
|
}
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
2023-12-21 17:24:39 +00:00
|
|
|
|
2024-10-12 18:17:35 +00:00
|
|
|
void VisualiserComponent::toggleRecording() {
|
2024-10-22 08:03:19 +00:00
|
|
|
|
2024-10-12 18:17:35 +00:00
|
|
|
}
|
|
|
|
|
2024-10-12 19:07:53 +00:00
|
|
|
void VisualiserComponent::haltRecording() {
|
2024-10-23 10:23:58 +00:00
|
|
|
record.setToggleState(false, juce::NotificationType::dontSendNotification);
|
2024-10-12 19:07:53 +00:00
|
|
|
}
|
|
|
|
|
2024-06-02 18:25:10 +00:00
|
|
|
void VisualiserComponent::resized() {
|
|
|
|
auto area = getLocalBounds();
|
2024-10-23 10:23:58 +00:00
|
|
|
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);
|
2024-06-02 19:56:33 +00:00
|
|
|
}
|
2024-10-23 10:23:58 +00:00
|
|
|
if (!oldVisualiser) {
|
|
|
|
openGLVisualiser.setBounds(area);
|
2024-06-02 19:56:33 +00:00
|
|
|
}
|
2024-06-02 18:25:10 +00:00
|
|
|
}
|
2024-08-10 17:36:43 +00:00
|
|
|
|
|
|
|
void VisualiserComponent::popoutWindow() {
|
2024-10-12 19:07:53 +00:00
|
|
|
haltRecording();
|
2024-10-27 14:30:39 +00:00
|
|
|
auto visualiser = new VisualiserComponent(sampleRateManager, threadManager, settings, this, oldVisualiser);
|
2024-08-14 19:27:56 +00:00
|
|
|
visualiser->settings.setLookAndFeel(&getLookAndFeel());
|
2024-08-21 12:35:26 +00:00
|
|
|
visualiser->openSettings = openSettings;
|
2024-08-21 12:43:30 +00:00
|
|
|
visualiser->closeSettings = closeSettings;
|
2024-10-12 18:17:35 +00:00
|
|
|
visualiser->recordingHalted = recordingHalted;
|
2024-08-10 17:36:43 +00:00
|
|
|
child = visualiser;
|
2024-10-23 10:23:58 +00:00
|
|
|
childUpdated();
|
|
|
|
visualiser->setSize(300, 325);
|
2024-08-10 17:36:43 +00:00
|
|
|
popout = std::make_unique<VisualiserWindow>("Software Oscilloscope", this);
|
|
|
|
popout->setContentOwned(visualiser, true);
|
|
|
|
popout->setUsingNativeTitleBar(true);
|
|
|
|
popout->setResizable(true, false);
|
|
|
|
popout->setVisible(true);
|
2024-10-23 10:23:58 +00:00
|
|
|
popout->centreWithSize(300, 325);
|
2024-08-10 17:36:43 +00:00
|
|
|
setPaused(true);
|
|
|
|
resized();
|
2024-10-23 10:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VisualiserComponent::childUpdated() {
|
|
|
|
popOutButton.setVisible(child == nullptr);
|
2024-08-10 17:36:43 +00:00
|
|
|
}
|