2023-07-08 17:59:05 +00:00
|
|
|
#include "VisualiserComponent.h"
|
|
|
|
|
2023-07-10 12:17:04 +00:00
|
|
|
VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcessor& p) : numChannels(numChannels), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), juce::Thread("VisualiserComponent") {
|
2023-07-08 17:59:05 +00:00
|
|
|
setOpaque(true);
|
2023-12-21 17:24:39 +00:00
|
|
|
resetBuffer();
|
2023-07-09 20:30:33 +00:00
|
|
|
startTimerHz(60);
|
2023-07-10 12:17:04 +00:00
|
|
|
startThread();
|
2023-12-21 18:31:18 +00:00
|
|
|
|
|
|
|
roughness.textBox.setValue(4);
|
2023-12-22 17:52:36 +00:00
|
|
|
intensity.textBox.setValue(1.0);
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 12:17:04 +00:00
|
|
|
VisualiserComponent::~VisualiserComponent() {
|
2023-09-05 19:46:05 +00:00
|
|
|
audioProcessor.consumerStop(consumer);
|
2023-07-10 12:17:04 +00:00
|
|
|
stopThread(1000);
|
|
|
|
}
|
2023-07-08 17:59:05 +00:00
|
|
|
|
|
|
|
void VisualiserComponent::setBuffer(std::vector<float>& newBuffer) {
|
2023-07-22 17:42:30 +00:00
|
|
|
juce::CriticalSection::ScopedLockType scope(lock);
|
2023-07-08 17:59:05 +00:00
|
|
|
buffer.clear();
|
2023-12-21 18:31:18 +00:00
|
|
|
for (int i = 0; i < newBuffer.size(); i += (int) roughness.textBox.getValue() * numChannels) {
|
2023-07-08 17:59:05 +00:00
|
|
|
buffer.push_back(newBuffer[i]);
|
|
|
|
buffer.push_back(newBuffer[i + 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisualiserComponent::setColours(juce::Colour bk, juce::Colour fg) {
|
|
|
|
backgroundColour = bk;
|
|
|
|
waveformColour = fg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisualiserComponent::paint(juce::Graphics& g) {
|
|
|
|
g.fillAll(backgroundColour);
|
2023-07-28 20:10:21 +00:00
|
|
|
g.drawRect(getLocalBounds(), 1);
|
2023-07-08 17:59:05 +00:00
|
|
|
|
|
|
|
auto r = getLocalBounds().toFloat();
|
2023-07-19 20:40:31 +00:00
|
|
|
auto minDim = juce::jmin(r.getWidth(), r.getHeight());
|
2023-07-08 17:59:05 +00:00
|
|
|
|
2023-09-10 10:14:10 +00:00
|
|
|
{
|
|
|
|
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.fillRect(getLocalBounds());
|
|
|
|
|
|
|
|
// add text
|
|
|
|
g.setColour(juce::Colours::white);
|
|
|
|
g.setFont(14.0f);
|
|
|
|
g.drawFittedText("Paused", getLocalBounds(), juce::Justification::centred, 1);
|
2023-07-08 17:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-09 20:30:33 +00:00
|
|
|
void VisualiserComponent::timerCallback() {
|
|
|
|
repaint();
|
|
|
|
}
|
|
|
|
|
2023-07-10 12:17:04 +00:00
|
|
|
void VisualiserComponent::run() {
|
|
|
|
while (!threadShouldExit()) {
|
2023-12-21 17:24:39 +00:00
|
|
|
if (sampleRate != (int) audioProcessor.currentSampleRate) {
|
|
|
|
resetBuffer();
|
|
|
|
}
|
|
|
|
|
2023-09-05 19:46:05 +00:00
|
|
|
consumer = audioProcessor.consumerRegister(tempBuffer);
|
|
|
|
audioProcessor.consumerRead(consumer);
|
2023-09-01 22:42:17 +00:00
|
|
|
setBuffer(tempBuffer);
|
2023-07-10 12:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-10 10:14:10 +00:00
|
|
|
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
|
2023-09-10 11:35:26 +00:00
|
|
|
if (event.mods.isLeftButtonDown()) {
|
|
|
|
active = !active;
|
|
|
|
if (active) {
|
|
|
|
startTimerHz(60);
|
|
|
|
startThread();
|
|
|
|
} else {
|
|
|
|
audioProcessor.consumerStop(consumer);
|
|
|
|
stopTimer();
|
|
|
|
stopThread(1000);
|
|
|
|
}
|
|
|
|
repaint();
|
2023-12-21 18:31:18 +00:00
|
|
|
} else if (event.mods.isPopupMenu()) {
|
|
|
|
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) {});
|
2023-09-10 10:14:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-08 17:59:05 +00:00
|
|
|
void VisualiserComponent::paintChannel(juce::Graphics& g, juce::Rectangle<float> area, int channel) {
|
|
|
|
juce::Path path;
|
|
|
|
|
|
|
|
for (int i = 0; i < buffer.size(); i += numChannels) {
|
|
|
|
auto sample = buffer[i + channel];
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
path.startNewSubPath(0.0f, sample);
|
|
|
|
} else {
|
|
|
|
path.lineTo((float)i, sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply affine transform to path to fit in area
|
|
|
|
auto transform = juce::AffineTransform::fromTargetPoints(0.0f, -1.0f, area.getX(), area.getY(), 0.0f, 1.0f, area.getX(), area.getBottom(), buffer.size(), -1.0f, area.getRight(), area.getY());
|
|
|
|
path.applyTransform(transform);
|
|
|
|
|
|
|
|
g.strokePath(path, juce::PathStrokeType(1.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
for (int i = 2; i < buffer.size(); i += 2) {
|
2023-07-19 20:40:31 +00:00
|
|
|
lines.emplace_back(buffer[i - 2], buffer[i - 1], buffer[i], buffer[i + 1]);
|
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
|
|
|
|
|
|
|
void VisualiserComponent::resetBuffer() {
|
|
|
|
sampleRate = (int) audioProcessor.currentSampleRate;
|
|
|
|
tempBuffer = std::vector<float>(2 * sampleRate * BUFFER_LENGTH_SECS);
|
|
|
|
}
|