osci-render/Source/components/VolumeComponent.cpp

179 wiersze
7.1 KiB
C++
Czysty Zwykły widok Historia

#include "VolumeComponent.h"
VolumeComponent::VolumeComponent(CommonAudioProcessor& p)
: osci::AudioBackgroundThread("VolumeComponent", p.threadManager),
audioProcessor(p)
{
setOpaque(false);
setShouldBeRunning(true);
leftVolumeSmoothed.reset(10); // Reset with 5 steps for smooth animation
rightVolumeSmoothed.reset(10);
leftVolumeSmoothed.setCurrentAndTargetValue(0.0f);
rightVolumeSmoothed.setCurrentAndTargetValue(0.0f);
addAndMakeVisible(volumeSlider);
volumeSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
volumeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
volumeSlider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::transparentWhite);
volumeSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite);
volumeSlider.setOpaque(false);
auto volumeParam = audioProcessor.volumeEffect->parameters[0];
volumeSlider.setRange(volumeParam->min, volumeParam->max, volumeParam->step);
volumeSlider.setValue(volumeParam->getValueUnnormalised());
volumeSlider.setDoubleClickReturnValue(true, 1.0);
volumeSlider.setLookAndFeel(&thumbRadiusLookAndFeel);
volumeSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::white);
addAndMakeVisible(thresholdSlider);
thresholdSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
thresholdSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
thresholdSlider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::transparentWhite);
thresholdSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite);
thresholdSlider.setOpaque(false);
auto& thresholdParam = audioProcessor.thresholdEffect->parameters[0];
thresholdSlider.setRange(thresholdParam->min, thresholdParam->max, thresholdParam->step);
thresholdSlider.setValue(thresholdParam->getValueUnnormalised());
thresholdSlider.setDoubleClickReturnValue(true, 1.0);
thresholdSlider.setLookAndFeel(&thresholdLookAndFeel);
thresholdSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
volumeSlider.onValueChange = [this]() {
audioProcessor.volumeEffect->setValue(volumeSlider.getValue());
};
thresholdSlider.onValueChange = [this]() {
audioProcessor.thresholdEffect->setValue(thresholdSlider.getValue());
};
2023-07-10 17:04:24 +00:00
addAndMakeVisible(volumeButton);
volumeButton.onClick = [this] {
audioProcessor.muteParameter->setBoolValueNotifyingHost(!audioProcessor.muteParameter->getBoolValue());
};
}
void VolumeComponent::paint(juce::Graphics& g) {
g.setColour(juce::Colours::transparentBlack);
g.fillAll();
auto r = getLocalBounds().toFloat();
2023-07-10 17:04:24 +00:00
r.removeFromTop(20);
r.removeFromRight(r.getWidth() / 2);
r.removeFromTop(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
r.removeFromBottom(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
auto channelHeight = r.getHeight();
auto leftVolumeHeight = channelHeight * leftVolume;
auto rightVolumeHeight = channelHeight * rightVolume;
auto overallRect = r;
auto leftRect = r.removeFromLeft(r.getWidth() / 2);
auto rightRect = r;
auto leftRegion = leftRect;
auto rightRegion = rightRect;
g.setColour(juce::Colours::white.withAlpha(0.1f));
g.drawRoundedRectangle(overallRect, 4.0f, 1.0f);
// Enable anti-aliasing for smoother rendering
g.setImageResamplingQuality(juce::Graphics::highResamplingQuality);
auto drawVolumeMeter = [&](juce::Rectangle<float>& rect, float volume) {
auto meterRect = rect.removeFromBottom(volume * channelHeight);
auto gradient = juce::ColourGradient(
juce::Colour(0xff00ff00), // Green
0, overallRect.getBottom(),
juce::Colour(0xffff0000), // Red
0, overallRect.getY(),
false);
gradient.addColour(0.3, juce::Colour(0xffffe600)); // Yellow in the middle
g.setGradientFill(gradient);
// Draw rounded meter
g.saveState();
g.reduceClipRegion(meterRect.toNearestInt());
g.fillRoundedRectangle(meterRect.reduced(1.0f), 2.0f);
g.restoreState();
};
drawVolumeMeter(leftRect, leftVolume);
drawVolumeMeter(rightRect, rightVolume);
// Draw smooth volume indicators
auto drawSmoothIndicator = [&](const juce::Rectangle<float>& region, float value) {
if (value > 0.0f) { // Only draw indicator if volume is greater than 0
auto y = region.getBottom() - (value * channelHeight);
auto indicatorHeight = 3.0f;
auto indicatorRect = juce::Rectangle<float>(
region.getX(), y - indicatorHeight / 2,
region.getWidth(), indicatorHeight);
g.setColour(juce::Colours::white.withAlpha(0.8f));
g.fillRoundedRectangle(indicatorRect, 1.5f);
}
};
drawSmoothIndicator(leftRegion, leftVolumeSmoothed.getNextValue());
drawSmoothIndicator(rightRegion, rightVolumeSmoothed.getNextValue());
// Draw threshold line with modern style
auto thresholdY = rightRegion.getBottom() - (thresholdSlider.getValue() * channelHeight);
auto thresholdRect = juce::Rectangle<float>(
leftRegion.getX(),
thresholdY - 1.5f,
leftRegion.getWidth() + rightRegion.getWidth(),
3.0f
);
g.setColour(juce::Colours::white.withAlpha(0.7f));
g.fillRoundedRectangle(thresholdRect, 1.5f);
}
void VolumeComponent::handleAsyncUpdate() {
repaint();
volumeSlider.setValue(audioProcessor.volumeEffect->getValue(), juce::NotificationType::dontSendNotification);
thresholdSlider.setValue(audioProcessor.thresholdEffect->getValue(), juce::NotificationType::dontSendNotification);
}
void VolumeComponent::runTask(const std::vector<osci::Point>& buffer) {
float leftVolume = 0;
float rightVolume = 0;
for (int i = 0; i < buffer.size(); i++) {
leftVolume += buffer[i].x * buffer[i].x;
rightVolume += buffer[i].y * buffer[i].y;
}
// RMS
leftVolume = std::sqrt(leftVolume / buffer.size());
rightVolume = std::sqrt(rightVolume / buffer.size());
if (std::isnan(leftVolume) || std::isnan(rightVolume)) {
leftVolume = 0;
rightVolume = 0;
}
this->leftVolume = leftVolume;
this->rightVolume = rightVolume;
leftVolumeSmoothed.setTargetValue(leftVolume);
rightVolumeSmoothed.setTargetValue(rightVolume);
triggerAsyncUpdate();
}
int VolumeComponent::prepareTask(double sampleRate, int bufferSize) {
return BUFFER_DURATION_SECS * sampleRate;
}
void VolumeComponent::stopTask() {}
void VolumeComponent::resized() {
auto r = getLocalBounds();
auto iconRow = r.removeFromTop(20);
volumeButton.setBounds(iconRow);
volumeSlider.setBounds(r.removeFromLeft(r.getWidth() / 2));
thresholdSlider.setBounds(r.reduced(0, 5.0));
}