Create fully-functioning and somewhat good looking volume visualiser

pull/170/head
James Ball 2023-07-10 17:42:22 +01:00
rodzic afdd4483b3
commit c0e64094aa
5 zmienionych plików z 96 dodań i 29 usunięć

Wyświetl plik

@ -56,7 +56,9 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g)
void OscirenderAudioProcessorEditor::resized() {
auto area = getLocalBounds();
volume.setBounds(area.removeFromLeft(50));
area.removeFromLeft(5);
auto volumeArea = area.removeFromLeft(30);
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), std::min(volumeArea.getHeight(), 300)));
auto sections = 2;
int index = audioProcessor.getCurrentFileIndex();
if (index != -1) {

Wyświetl plik

@ -444,9 +444,12 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
x = channels.x;
y = channels.y;
// clip to -1.0 to 1.0
x = std::max(-1.0, std::min(1.0, x));
y = std::max(-1.0, std::min(1.0, y));
x *= volume;
y *= volume;
// clip
x = std::max(-threshold, std::min(threshold.load(), x));
y = std::max(-threshold, std::min(threshold.load(), y));
if (totalNumOutputChannels >= 2) {
channelData[0][sample] = x;
@ -475,19 +478,6 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
}
}
}
juce::MidiBuffer processedMidi;
for (const auto metadata : midiMessages) {
auto message = metadata.getMessage();
const auto time = metadata.samplePosition;
if (message.isNoteOn()) {
message = juce::MidiMessage::noteOn(message.getChannel(), message.getNoteNumber(), (juce::uint8)noteOnVel);
}
processedMidi.addEvent(message, time);
}
midiMessages.swapWith(processedMidi);
}
void OscirenderAudioProcessor::incrementShapeDrawing() {

Wyświetl plik

@ -64,10 +64,11 @@ public:
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
float noteOnVel;
float frequency = 440.0f;
std::atomic<float> frequency = 440.0f;
std::atomic<double> volume = 1.0;
std::atomic<double> threshold = 1.0;
double currentSampleRate = 0.0;
std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
std::vector<std::shared_ptr<Effect>> allEffects;

Wyświetl plik

@ -11,7 +11,10 @@ VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p
volumeSlider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::transparentWhite);
volumeSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite);
volumeSlider.setOpaque(false);
volumeSlider.setRange(0, 1, 0.001);
volumeSlider.setRange(0, 2, 0.001);
volumeSlider.setValue(1);
volumeSlider.setLookAndFeel(&thumbRadiusLookAndFeel);
volumeSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
addAndMakeVisible(thresholdSlider);
thresholdSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
@ -20,6 +23,17 @@ VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p
thresholdSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite);
thresholdSlider.setOpaque(false);
thresholdSlider.setRange(0, 1, 0.001);
thresholdSlider.setValue(1);
thresholdSlider.setLookAndFeel(&thresholdLookAndFeel);
thresholdSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
volumeSlider.onValueChange = [this]() {
audioProcessor.volume = volumeSlider.getValue();
};
thresholdSlider.onValueChange = [this]() {
audioProcessor.threshold = thresholdSlider.getValue();
};
}
VolumeComponent::~VolumeComponent() {
@ -30,6 +44,8 @@ VolumeComponent::~VolumeComponent() {
void VolumeComponent::paint(juce::Graphics& g) {
auto r = getLocalBounds().toFloat();
r.removeFromRight(r.getWidth() / 2);
r.removeFromTop(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
r.removeFromBottom(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
g.setColour(juce::Colours::white);
g.fillRect(r);
@ -43,17 +59,19 @@ void VolumeComponent::paint(juce::Graphics& g) {
auto leftRegion = leftRect;
auto rightRegion = rightRect;
g.setGradientFill(juce::ColourGradient(juce::Colours::green, 0, leftRect.getBottom(), juce::Colours::red, 0, leftRect.getY(), false));
g.setGradientFill(juce::ColourGradient(juce::Colour(0xff00ff00), 0, leftRect.getBottom(), juce::Colours::red, 0, leftRect.getY(), false));
g.fillRect(leftRect.removeFromBottom(leftVolumeHeight));
g.setGradientFill(juce::ColourGradient(juce::Colours::green, 0, rightRect.getBottom(), juce::Colours::red, 0, rightRect.getY(), false));
g.setGradientFill(juce::ColourGradient(juce::Colour(0xff00ff00), 0, rightRect.getBottom(), juce::Colours::red, 0, rightRect.getY(), false));
g.fillRect(rightRect.removeFromBottom(rightVolumeHeight));
// draw average volume as new rectangles on each channel 10 pixels tall
g.setColour(juce::Colours::black);
g.fillRect(leftRegion.getX(), leftRegion.getBottom() - (avgLeftVolume * channelHeight), leftRegion.getWidth(), 5.0f);
g.fillRect(rightRegion.getX(), rightRegion.getBottom() - (avgRightVolume * channelHeight), rightRegion.getWidth(), 5.0f);
auto barWidth = 5.0f;
g.setColour(juce::Colours::black);
g.fillRect(leftRegion.getX(), leftRegion.getBottom() - (avgLeftVolume * channelHeight) - barWidth / 2, leftRegion.getWidth(), barWidth);
g.fillRect(rightRegion.getX(), rightRegion.getBottom() - (avgRightVolume * channelHeight) - barWidth / 2, rightRegion.getWidth(), barWidth);
g.fillRect(leftRegion.getX(), rightRegion.getBottom() - (thresholdSlider.getValue() * channelHeight) - barWidth / 2, leftRegion.getWidth() + rightRegion.getWidth(), barWidth);
}
void VolumeComponent::timerCallback() {

Wyświetl plik

@ -4,6 +4,60 @@
#include "../concurrency/BufferConsumer.h"
#include "../PluginProcessor.h"
class ThumbRadiusLookAndFeel : public juce::LookAndFeel_V4 {
public:
ThumbRadiusLookAndFeel(int thumbRadius) : thumbRadius(thumbRadius) {}
int getSliderThumbRadius(juce::Slider& slider) override {
return juce::jmin(thumbRadius, slider.isHorizontal() ? slider.getHeight() : slider.getWidth());
}
private:
int thumbRadius = 12;
};
// this is for a vertical slider
class ThresholdLookAndFeel : public ThumbRadiusLookAndFeel {
public:
ThresholdLookAndFeel(int thumbRadius) : ThumbRadiusLookAndFeel(thumbRadius) {}
void drawLinearSliderThumb(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, const juce::Slider::SliderStyle style, juce::Slider& slider) override {
float kx = (float) x + (float) width * 0.5f;
float ky = sliderPos;
auto outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
auto sliderRadius = (float) (getSliderThumbRadius(slider) - 2);
auto diameter = sliderRadius * 2.0f;
auto halfThickness = outlineThickness * 0.5f;
auto isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
auto knobColour = slider.findColour(juce::Slider::thumbColourId)
.withMultipliedSaturation((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha(slider.isEnabled() ? 1.0f : 0.7f);
y = (int) (ky - sliderRadius);
// draw triangle that points left
juce::Path p;
p.addTriangle(
x + diameter, y,
x + diameter, y + diameter,
x, ky
);
g.setColour(knobColour);
g.fillPath(p);
g.setColour(knobColour.brighter());
g.strokePath(p, juce::PathStrokeType(outlineThickness));
}
void drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, const juce::Slider::SliderStyle style, juce::Slider& slider) override {
drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
};
class VolumeComponent : public juce::Component, public juce::Timer, public juce::Thread {
public:
VolumeComponent(OscirenderAudioProcessor& p);
@ -22,8 +76,10 @@ private:
std::atomic<float> rightVolume = 0;
std::atomic<float> avgLeftVolume = 0;
std::atomic<float> avgRightVolume = 0;
juce::Slider volumeSlider;
ThumbRadiusLookAndFeel thumbRadiusLookAndFeel{20};
juce::Slider volumeSlider;
ThresholdLookAndFeel thresholdLookAndFeel{20};
juce::Slider thresholdSlider;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VolumeComponent)