mirror of
https://codeberg.org/armin/justasample.git
synced 2026-09-01 04:10:48 +02:00
fixes, changes to gitignore
This commit is contained in:
parent
2ab046489f
commit
11d81cf1fd
17 changed files with 1160 additions and 16 deletions
328
Source/Components/ModulationSection.cpp
Normal file
328
Source/Components/ModulationSection.cpp
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
ModulationSection.cpp
|
||||
Created: 24 Jul 2026
|
||||
Author: Armin
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "ModulationSection.h"
|
||||
|
||||
ModulationSection::ModulationSection(JustaSampleAudioProcessor& processor) :
|
||||
apvts(processor.APVTS()),
|
||||
|
||||
filterLabel("", "Filter"),
|
||||
lfoLabel("", "LFO"),
|
||||
phaserLabel("", "Phaser"),
|
||||
ringModLabel("", "Ring Mod"),
|
||||
|
||||
filterTypeAttachment(apvts, PluginParameters::MOD_FILTER_TYPE, filterTypeBox),
|
||||
filterCutoffAttachment(apvts, PluginParameters::MOD_FILTER_CUTOFF, filterCutoff),
|
||||
filterResonanceAttachment(apvts, PluginParameters::MOD_FILTER_RESONANCE, filterResonance),
|
||||
|
||||
lfoWaveformAttachment(apvts, PluginParameters::MOD_LFO_WAVEFORM, lfoWaveformBox),
|
||||
lfoRateAttachment(apvts, PluginParameters::MOD_LFO_RATE, lfoRate),
|
||||
lfoSyncAttachment(apvts, PluginParameters::MOD_LFO_SYNC, lfoSyncButton),
|
||||
lfoSyncDivAttachment(apvts, PluginParameters::MOD_LFO_SYNC_DIV, lfoSyncDivBox),
|
||||
lfoFilterDepthAttachment(apvts, PluginParameters::MOD_LFO_FILTER_DEPTH, lfoFilterDepth),
|
||||
lfoSpeedDepthAttachment(apvts, PluginParameters::MOD_LFO_SPEED_DEPTH, lfoSpeedDepth),
|
||||
|
||||
phaserEnabledAttachment(apvts, PluginParameters::MOD_PHASER_ENABLED, phaserEnabled),
|
||||
phaserDepthAttachment(apvts, PluginParameters::MOD_PHASER_DEPTH, phaserDepth),
|
||||
phaserFeedbackAttachment(apvts, PluginParameters::MOD_PHASER_FEEDBACK, phaserFeedback),
|
||||
phaserStagesAttachment(apvts, PluginParameters::MOD_PHASER_STAGES, phaserStages),
|
||||
|
||||
ringModEnabledAttachment(apvts, PluginParameters::MOD_RINGMOD_ENABLED, ringModEnabled),
|
||||
ringModMixAttachment(apvts, PluginParameters::MOD_RINGMOD_MIX, ringModMix)
|
||||
{
|
||||
// Labels
|
||||
auto setupLabel = [this](juce::Label& label)
|
||||
{
|
||||
label.setJustificationType(juce::Justification::centred);
|
||||
label.setInterceptsMouseClicks(false, false);
|
||||
addAndMakeVisible(label);
|
||||
};
|
||||
setupLabel(filterLabel);
|
||||
setupLabel(lfoLabel);
|
||||
setupLabel(phaserLabel);
|
||||
setupLabel(ringModLabel);
|
||||
|
||||
// Filter type combo
|
||||
filterTypeBox.addItemList(PluginParameters::MOD_FILTER_TYPE_LABELS, 1);
|
||||
filterTypeBox.setJustificationType(juce::Justification::centred);
|
||||
addAndMakeVisible(filterTypeBox);
|
||||
|
||||
// Filter rotaries
|
||||
auto setupRotary = [this](CustomRotary& rotary, const juce::String& unit)
|
||||
{
|
||||
rotary.setSliderStyle(juce::Slider::RotaryVerticalDrag);
|
||||
rotary.setMouseDragSensitivity(150);
|
||||
rotary.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 0, 0);
|
||||
rotary.getProperties().set(ComponentProps::ROTARY_UNIT, unit);
|
||||
addAndMakeVisible(rotary);
|
||||
rotaries.add(&rotary);
|
||||
};
|
||||
|
||||
setupRotary(filterCutoff, PluginParameters::FREQUENCY_UNIT);
|
||||
setupRotary(filterResonance, "");
|
||||
|
||||
// LFO waveform combo
|
||||
lfoWaveformBox.addItemList(PluginParameters::MOD_LFO_WAVEFORM_LABELS, 1);
|
||||
lfoWaveformBox.setJustificationType(juce::Justification::centred);
|
||||
addAndMakeVisible(lfoWaveformBox);
|
||||
|
||||
setupRotary(lfoRate, PluginParameters::FREQUENCY_UNIT);
|
||||
|
||||
lfoSyncButton.useShape(juce::Path());
|
||||
lfoSyncButton.setButtonText("Sync");
|
||||
addAndMakeVisible(lfoSyncButton);
|
||||
|
||||
lfoSyncDivBox.addItemList(PluginParameters::MOD_LFO_SYNC_DIV_LABELS, 1);
|
||||
lfoSyncDivBox.setJustificationType(juce::Justification::centred);
|
||||
addAndMakeVisible(lfoSyncDivBox);
|
||||
|
||||
setupRotary(lfoFilterDepth, "");
|
||||
setupRotary(lfoSpeedDepth, "");
|
||||
|
||||
// Phaser
|
||||
phaserEnabled.setButtonText("On");
|
||||
addAndMakeVisible(phaserEnabled);
|
||||
setupRotary(phaserDepth, "");
|
||||
setupRotary(phaserFeedback, "");
|
||||
phaserStages.getProperties().set(ComponentProps::ROTARY_UNIT, "stg");
|
||||
setupRotary(phaserStages, "stg");
|
||||
|
||||
// Ring mod
|
||||
ringModEnabled.setButtonText("On");
|
||||
addAndMakeVisible(ringModEnabled);
|
||||
setupRotary(ringModMix, "%");
|
||||
}
|
||||
|
||||
void ModulationSection::paint(juce::Graphics& g)
|
||||
{
|
||||
auto colors = getTheme();
|
||||
auto bounds = getLocalBounds().toFloat();
|
||||
|
||||
g.setColour(colors.foreground);
|
||||
g.fillRect(bounds);
|
||||
|
||||
// Draw section dividers
|
||||
g.setColour(colors.slate.withAlpha(0.2f));
|
||||
|
||||
float dividerY1 = bounds.getY() + bounds.getHeight() * 0.125f;
|
||||
float dividerY2 = bounds.getY() + bounds.getHeight() * 0.875f;
|
||||
|
||||
// Dividers between Filter | LFO | Phaser | Ring Mod
|
||||
float filterEnd = scalef(380.f);
|
||||
float lfoEnd = filterEnd + scalef(600.f);
|
||||
float phaserEnd = lfoEnd + scalef(530.f);
|
||||
|
||||
g.drawVerticalLine(int(filterEnd), dividerY1, dividerY2);
|
||||
g.drawVerticalLine(int(lfoEnd), dividerY1, dividerY2);
|
||||
g.drawVerticalLine(int(phaserEnd), dividerY1, dividerY2);
|
||||
}
|
||||
|
||||
void ModulationSection::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds().toFloat();
|
||||
|
||||
// Section labels
|
||||
auto filterSection = bounds.removeFromLeft(scalef(380.f));
|
||||
auto lfoSection = bounds.removeFromLeft(scalef(600.f));
|
||||
auto phaserSection = bounds.removeFromLeft(scalef(530.f));
|
||||
auto ringModSection = bounds;
|
||||
|
||||
// Reduce all sections vertically
|
||||
filterSection.reduce(scalef(16.f), scalef(8.f));
|
||||
lfoSection.reduce(scalef(16.f), scalef(8.f));
|
||||
phaserSection.reduce(scalef(16.f), scalef(8.f));
|
||||
ringModSection.reduce(scalef(16.f), scalef(8.f));
|
||||
|
||||
// === Filter section ===
|
||||
auto filterTitleBounds = filterSection.removeFromTop(scalef(28.f));
|
||||
filterLabel.setBounds(filterTitleBounds.toNearestInt());
|
||||
filterLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
|
||||
filterSection.removeFromTop(scalef(4.f));
|
||||
|
||||
auto filterTypeBounds = filterSection.removeFromTop(scalef(28.f)).reduced(scalef(20.f), 0.f);
|
||||
filterTypeBox.setBounds(filterTypeBounds.toNearestInt());
|
||||
filterSection.removeFromTop(scalef(6.f));
|
||||
|
||||
float filterRotarySize = scalef(50.f);
|
||||
float filterRotaryPad = filterRotarySize * Layout::rotaryPadding;
|
||||
|
||||
auto filterCutoffBounds = filterSection.removeFromLeft(filterRotarySize + 2 * filterRotaryPad);
|
||||
filterCutoff.setBounds(filterCutoffBounds.toNearestInt());
|
||||
filterCutoff.sendLookAndFeelChange();
|
||||
|
||||
filterSection.removeFromLeft(scalef(16.f));
|
||||
|
||||
auto filterResBounds = filterSection.removeFromLeft(filterRotarySize + 2 * filterRotaryPad);
|
||||
filterResonance.setBounds(filterResBounds.toNearestInt());
|
||||
filterResonance.sendLookAndFeelChange();
|
||||
|
||||
// === LFO section ===
|
||||
auto lfoTitleBounds = lfoSection.removeFromTop(scalef(28.f));
|
||||
lfoLabel.setBounds(lfoTitleBounds.toNearestInt());
|
||||
lfoLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
|
||||
lfoSection.removeFromTop(scalef(4.f));
|
||||
|
||||
auto lfoWaveformBounds = lfoSection.removeFromTop(scalef(28.f)).reduced(scalef(20.f), 0.f);
|
||||
lfoWaveformBox.setBounds(lfoWaveformBounds.toNearestInt());
|
||||
lfoSection.removeFromTop(scalef(4.f));
|
||||
|
||||
auto lfoTopRow = lfoSection.removeFromTop(scalef(56.f));
|
||||
auto lfoBottomRow = lfoSection;
|
||||
|
||||
float lfoRotarySize = scalef(48.f);
|
||||
float lfoRotaryPad = lfoRotarySize * Layout::rotaryPadding;
|
||||
|
||||
// Top row: Rate, Sync button, Sync div
|
||||
auto lfoRateBounds = lfoTopRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
|
||||
lfoRate.setBounds(lfoRateBounds.toNearestInt());
|
||||
lfoRate.sendLookAndFeelChange();
|
||||
|
||||
lfoTopRow.removeFromLeft(scalef(8.f));
|
||||
auto lfoSyncBounds = lfoTopRow.removeFromLeft(scalef(50.f));
|
||||
lfoSyncButton.setBounds(lfoSyncBounds.toNearestInt());
|
||||
lfoSyncButton.setBorder(scalef(2.f), scalef(4.f));
|
||||
|
||||
lfoTopRow.removeFromLeft(scalef(8.f));
|
||||
auto lfoSyncDivBounds = lfoTopRow.removeFromLeft(scalef(70.f));
|
||||
lfoSyncDivBox.setBounds(lfoSyncDivBounds.toNearestInt());
|
||||
|
||||
// Bottom row: Filter depth, Speed depth
|
||||
auto lfoFilterDepthBounds = lfoBottomRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
|
||||
lfoFilterDepth.setBounds(lfoFilterDepthBounds.toNearestInt());
|
||||
lfoFilterDepth.sendLookAndFeelChange();
|
||||
|
||||
lfoBottomRow.removeFromLeft(scalef(8.f));
|
||||
auto lfoSpeedDepthBounds = lfoBottomRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
|
||||
lfoSpeedDepth.setBounds(lfoSpeedDepthBounds.toNearestInt());
|
||||
lfoSpeedDepth.sendLookAndFeelChange();
|
||||
|
||||
// === Phaser section ===
|
||||
auto phaserTitleBounds = phaserSection.removeFromTop(scalef(28.f));
|
||||
phaserLabel.setBounds(phaserTitleBounds.toNearestInt());
|
||||
phaserLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
|
||||
phaserSection.removeFromTop(scalef(4.f));
|
||||
|
||||
auto phaserEnableBounds = phaserSection.removeFromTop(scalef(24.f)).reduced(scalef(20.f), 0.f);
|
||||
phaserEnabled.setBounds(phaserEnableBounds.toNearestInt());
|
||||
phaserSection.removeFromTop(scalef(4.f));
|
||||
|
||||
float phaserRotarySize = scalef(48.f);
|
||||
float phaserRotaryPad = phaserRotarySize * Layout::rotaryPadding;
|
||||
|
||||
auto phaserDepthBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
|
||||
phaserDepth.setBounds(phaserDepthBounds.toNearestInt());
|
||||
phaserDepth.sendLookAndFeelChange();
|
||||
|
||||
phaserSection.removeFromLeft(scalef(8.f));
|
||||
auto phaserFbBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
|
||||
phaserFeedback.setBounds(phaserFbBounds.toNearestInt());
|
||||
phaserFeedback.sendLookAndFeelChange();
|
||||
|
||||
phaserSection.removeFromLeft(scalef(8.f));
|
||||
auto phaserStagesBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
|
||||
phaserStages.setBounds(phaserStagesBounds.toNearestInt());
|
||||
phaserStages.sendLookAndFeelChange();
|
||||
|
||||
// === Ring Mod section ===
|
||||
auto ringModTitleBounds = ringModSection.removeFromTop(scalef(28.f));
|
||||
ringModLabel.setBounds(ringModTitleBounds.toNearestInt());
|
||||
ringModLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
|
||||
ringModSection.removeFromTop(scalef(4.f));
|
||||
|
||||
auto ringModEnableBounds = ringModSection.removeFromTop(scalef(24.f)).reduced(scalef(20.f), 0.f);
|
||||
ringModEnabled.setBounds(ringModEnableBounds.toNearestInt());
|
||||
ringModSection.removeFromTop(scalef(4.f));
|
||||
|
||||
float ringModRotarySize = scalef(48.f);
|
||||
float ringModRotaryPad = ringModRotarySize * Layout::rotaryPadding;
|
||||
|
||||
auto ringModMixBounds = ringModSection.removeFromLeft(ringModRotarySize + 2 * ringModRotaryPad);
|
||||
ringModMix.setBounds(ringModMixBounds.toNearestInt());
|
||||
ringModMix.sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
void ModulationSection::lookAndFeelChanged()
|
||||
{
|
||||
auto colors = getTheme();
|
||||
|
||||
filterLabel.setColour(juce::Label::textColourId, colors.dark);
|
||||
lfoLabel.setColour(juce::Label::textColourId, colors.dark);
|
||||
phaserLabel.setColour(juce::Label::textColourId, colors.dark);
|
||||
ringModLabel.setColour(juce::Label::textColourId, colors.dark);
|
||||
|
||||
phaserEnabled.setColour(juce::ToggleButton::textColourId, colors.dark);
|
||||
ringModEnabled.setColour(juce::ToggleButton::textColourId, colors.dark);
|
||||
|
||||
filterTypeBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
|
||||
filterTypeBox.setColour(juce::ComboBox::textColourId, colors.light);
|
||||
filterTypeBox.setColour(juce::ComboBox::arrowColourId, colors.dark);
|
||||
|
||||
lfoWaveformBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
|
||||
lfoWaveformBox.setColour(juce::ComboBox::textColourId, colors.light);
|
||||
lfoWaveformBox.setColour(juce::ComboBox::arrowColourId, colors.dark);
|
||||
|
||||
lfoSyncButton.setColors(colors.darkerSlate, colors.highlight, colors.dark.withAlpha(0.25f));
|
||||
|
||||
lfoSyncDivBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
|
||||
lfoSyncDivBox.setColour(juce::ComboBox::textColourId, colors.light);
|
||||
lfoSyncDivBox.setColour(juce::ComboBox::arrowColourId, colors.dark);
|
||||
}
|
||||
|
||||
void ModulationSection::enablementChanged()
|
||||
{
|
||||
bool enabled = isEnabled();
|
||||
|
||||
filterTypeBox.setEnabled(enabled);
|
||||
filterCutoff.setEnabled(enabled);
|
||||
filterResonance.setEnabled(enabled);
|
||||
|
||||
lfoWaveformBox.setEnabled(enabled);
|
||||
lfoRate.setEnabled(enabled);
|
||||
lfoSyncButton.setEnabled(enabled);
|
||||
lfoSyncDivBox.setEnabled(enabled);
|
||||
lfoFilterDepth.setEnabled(enabled);
|
||||
lfoSpeedDepth.setEnabled(enabled);
|
||||
|
||||
phaserEnabled.setEnabled(enabled);
|
||||
phaserDepth.setEnabled(enabled);
|
||||
phaserFeedback.setEnabled(enabled);
|
||||
phaserStages.setEnabled(enabled);
|
||||
|
||||
ringModEnabled.setEnabled(enabled);
|
||||
ringModMix.setEnabled(enabled);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ModulationSection::mouseDown(const juce::MouseEvent& event)
|
||||
{
|
||||
auto parent = event.originalComponent->getParentComponent();
|
||||
for (auto* rotary : rotaries)
|
||||
if (parent == rotary)
|
||||
rotary->mouseDown(event.getEventRelativeTo(rotary));
|
||||
}
|
||||
|
||||
void ModulationSection::mouseUp(const juce::MouseEvent& event)
|
||||
{
|
||||
auto parent = event.originalComponent->getParentComponent();
|
||||
for (auto* rotary : rotaries)
|
||||
if (parent == rotary)
|
||||
rotary->mouseUp(event.getEventRelativeTo(rotary));
|
||||
}
|
||||
|
||||
void ModulationSection::mouseDrag(const juce::MouseEvent& event)
|
||||
{
|
||||
auto parent = event.originalComponent->getParentComponent();
|
||||
for (auto* rotary : rotaries)
|
||||
if (parent == rotary)
|
||||
rotary->mouseDrag(event.getEventRelativeTo(rotary));
|
||||
}
|
||||
80
Source/Components/ModulationSection.h
Normal file
80
Source/Components/ModulationSection.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
ModulationSection.h
|
||||
Created: 24 Jul 2026
|
||||
Author: Armin
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "../PluginProcessor.h"
|
||||
#include "../PluginParameters.h"
|
||||
#include "../Utilities/ComponentUtils.h"
|
||||
#include "../Components/Buttons.h"
|
||||
|
||||
/** The modulation section: filter, LFO, phaser, and ring mod controls. */
|
||||
class ModulationSection final : public CustomComponent
|
||||
{
|
||||
public:
|
||||
explicit ModulationSection(JustaSampleAudioProcessor& processor);
|
||||
|
||||
private:
|
||||
void paint(juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
void enablementChanged() override;
|
||||
|
||||
void mouseDown(const juce::MouseEvent& event) override;
|
||||
void mouseUp(const juce::MouseEvent& event) override;
|
||||
void mouseDrag(const juce::MouseEvent& event) override;
|
||||
|
||||
float scale(float value) const { return std::roundf(value * float(getWidth()) / float(Layout::figmaWidth)); }
|
||||
float scalef(float value) const { return value * float(getWidth()) / float(Layout::figmaWidth); }
|
||||
|
||||
//==============================================================================
|
||||
APVTS& apvts;
|
||||
|
||||
// Section labels
|
||||
juce::Label filterLabel, lfoLabel, phaserLabel, ringModLabel;
|
||||
|
||||
// Filter controls
|
||||
juce::ComboBox filterTypeBox;
|
||||
APVTS::ComboBoxAttachment filterTypeAttachment;
|
||||
CustomRotary filterCutoff, filterResonance;
|
||||
CustomRotaryAttachment filterCutoffAttachment, filterResonanceAttachment;
|
||||
|
||||
// LFO controls
|
||||
juce::ComboBox lfoWaveformBox;
|
||||
APVTS::ComboBoxAttachment lfoWaveformAttachment;
|
||||
CustomRotary lfoRate;
|
||||
CustomRotaryAttachment lfoRateAttachment;
|
||||
CustomToggleableButton lfoSyncButton;
|
||||
APVTS::ButtonAttachment lfoSyncAttachment;
|
||||
juce::ComboBox lfoSyncDivBox;
|
||||
APVTS::ComboBoxAttachment lfoSyncDivAttachment;
|
||||
CustomRotary lfoFilterDepth, lfoSpeedDepth;
|
||||
CustomRotaryAttachment lfoFilterDepthAttachment, lfoSpeedDepthAttachment;
|
||||
|
||||
// Phaser controls
|
||||
juce::ToggleButton phaserEnabled;
|
||||
APVTS::ButtonAttachment phaserEnabledAttachment;
|
||||
CustomRotary phaserDepth, phaserFeedback, phaserStages;
|
||||
CustomRotaryAttachment phaserDepthAttachment, phaserFeedbackAttachment;
|
||||
CustomRotaryAttachment phaserStagesAttachment;
|
||||
|
||||
// Ring mod controls
|
||||
juce::ToggleButton ringModEnabled;
|
||||
APVTS::ButtonAttachment ringModEnabledAttachment;
|
||||
CustomRotary ringModMix;
|
||||
CustomRotaryAttachment ringModMixAttachment;
|
||||
|
||||
// Track all rotaries for mouse forwarding
|
||||
juce::Array<CustomRotary*> rotaries;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModulationSection)
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue